ip_nat.c revision 87c3980e28619b2b20d03f92d14b18fd89c183ca
/*
* Copyright (C) 1995-2004 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2005 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(_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 __sgi
# ifdef IFF_DRVRLOCK /* IRIX6 */
# endif
#endif
#ifdef RFC1825
#endif
#if SOLARIS2 >= 10
#include "ip_compat.h"
#include "ip_fil.h"
#include "ip_nat.h"
#include "ip_frag.h"
#include "ip_state.h"
#include "ip_proxy.h"
#else
#include "netinet/ip_compat.h"
#include "netinet/ip_state.h"
#include "netinet/ip_proxy.h"
#endif
#ifdef IPFILTER_SYNC
#endif
#if (__FreeBSD_version >= 300000)
#endif
#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". */
/* ======================================================================== */
*nat_instances = NULL;
u_int fr_nat_maxbucket = 0,
#ifdef IPFILTER_LOG
int nat_logging = 1;
#else
int nat_logging = 0;
#endif
int fr_nat_lock = 0;
int fr_nat_init = 0;
extern ipfrwlock_t ipf_nat;
extern int pfil_delayed_copy;
static int nat_flushtable __P((void));
static int nat_clearlist __P((void));
/* ------------------------------------------------------------------------ */
/* Function: fr_natinit */
/* Returns: int - 0 == success, -1 == failure */
/* Parameters: Nil */
/* */
/* Initialise all of the NAT locks, tables and other structures. */
/* ------------------------------------------------------------------------ */
int fr_natinit()
{
int i;
else
return -1;
else
return -1;
else
return -1;
else
return -1;
else
return -1;
ipf_nattable_sz * sizeof(u_long));
return -1;
ipf_nattable_sz * sizeof(u_long));
ipf_nattable_sz * sizeof(u_long));
return -1;
ipf_nattable_sz * sizeof(u_long));
if (fr_nat_maxbucket == 0) {
for (i = ipf_nattable_sz; i > 0; i >>= 1)
fr_nat_maxbucket *= 2;
}
/*
* 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.
*/
fr_nat_init = 1;
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)
ipnat_t *n;
{
u_32_t j;
int k;
k = count4bits(n->in_outmsk);
if ((k >= 0) && (k != 32))
rdr_masks |= 1 << k;
*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)
ipnat_t *n;
{
u_32_t j;
int k;
k = count4bits(n->in_inmsk);
if ((k >= 0) && (k != 32))
nat_masks |= 1 << k;
*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. */
/* ------------------------------------------------------------------------ */
{
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)
{
}
}
/* ------------------------------------------------------------------------ */
/* Function: fix_outcksum */
/* Returns: Nil */
/* Parameters: fin(I) - pointer to packet information */
/* 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. */
/* ------------------------------------------------------------------------ */
u_32_t n;
{
if (n == 0)
return;
if (n & NAT_HW_CKSUM) {
n &= 0xffff;
n = (n & 0xffff) + (n >> 16);
*sp = n & 0xffff;
return;
}
sum1 += (n);
/* Again */
}
/* ------------------------------------------------------------------------ */
/* Function: fix_incksum */
/* Returns: Nil */
/* Parameters: fin(I) - pointer to packet information */
/* 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. */
/* ------------------------------------------------------------------------ */
u_32_t n;
{
if (n == 0)
return;
if (n & NAT_HW_CKSUM) {
n &= 0xffff;
n = (n & 0xffff) + (n >> 16);
*sp = n & 0xffff;
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. */
/* ------------------------------------------------------------------------ */
#else
int cmd;
#endif
int mode;
{
return EPERM;
#endif
getlock = 0;
#else
#endif
} else {
}
if (mode & NAT_SYSSPACE) {
error = 0;
} else {
}
}
if (error != 0)
goto done;
/*
*/
}
break;
}
switch (cmd)
{
#ifdef IPFILTER_LOG
case SIOCIPFFB :
{
int tmp;
else {
}
break;
}
case SIOCSETLG :
else {
sizeof(nat_logging));
}
break;
case SIOCGETLG :
sizeof(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) {
if (nat_lookupredir(&nl)) {
} else
}
if (getlock) {
}
break;
}
case SIOCIPFFL : /* old SIOCFLNAT & SIOCCNATL */
break;
}
if (getlock) {
}
error = 0;
if (arg == 0)
ret = nat_flushtable();
else if (arg == 1)
ret = nat_clearlist();
else
if (getlock) {
}
if (error == 0) {
}
if (getlock) {
}
break;
case SIOCPROXY :
break;
case SIOCSTLCK :
break;
case SIOCSTPUT :
if (fr_nat_lock) {
if (getlock) {
}
if (getlock) {
}
} else
break;
case SIOCSTGSZ :
if (fr_nat_lock) {
if (getlock) {
}
if (getlock) {
}
} else
break;
case SIOCSTGET :
if (fr_nat_lock) {
if (getlock) {
}
if (getlock) {
}
} else
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;
nat_resolverule(n);
if (n->in_plabel[0] != '\0') {
return ENOENT;
}
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
/*
* 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);
}
n->in_flags &= ~IPN_NOTSRC;
nat_addnat(n);
}
n = NULL;
pfil_delayed_copy = 0;
if (getlock) {
}
return error;
}
/* ------------------------------------------------------------------------ */
/* Function: nat_resolvrule */
/* Returns: Nil */
/* Parameters: n(I) - pointer to NAT rule */
/* */
/* 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). */
/* ------------------------------------------------------------------------ */
static INLINE void nat_resolverule(n)
ipnat_t *n;
{
n->in_ifps[0] = (void *)-1;
} else {
n->in_ifps[0] = (void *)-1;
}
} else {
} else {
}
}
if (n->in_plabel[0] != '\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);
nat_masks = 0;
rdr_masks = 0;
}
if (n->in_tqehead[0] != NULL)
fr_deletetimeoutqueue(n->in_tqehead[0]);
if (n->in_use == 0) {
if (n->in_apr)
KFREE(n);
pfil_delayed_copy = 1;
} 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. */
/* ------------------------------------------------------------------------ */
static int fr_natgetsz(data)
{
if (!nat) {
nat = nat_instances;
/*
* 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.
*/
for (n = nat_instances; n; n = n->nat_next)
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 */
/* ------------------------------------------------------------------------ */
static int fr_natgetent(data)
{
int error;
if (error != 0)
return error;
nat = nat_instances;
if (nat_instances == NULL)
return ENOENT;
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.
*/
for (n = nat_instances; n; n = n->nat_next)
if (n == nat)
break;
if (n == NULL)
return ESRCH;
}
/*
* Copy out the NAT structure.
*/
sizeof(struct nat));
if (error != 0)
return error;
/*
* If we have a pointer to the NAT rule it belongs to, save that too.
*/
(char *)data +
sizeof(struct ipnat));
if (error != 0)
return error;
}
/*
* If we also know the NAT entry has an associated filter rule,
* save that too.
*/
(char *)data +
sizeof(struct frentry));
if (error != 0)
return error;
}
/*
* 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 *)data +
if (error != 0)
return error;
sizeof(*aps));
if (error != 0)
return error;
if (error != 0)
return error;
}
}
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. */
/* */
/* 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. */
/* ------------------------------------------------------------------------ */
static int fr_natputent(data)
{
int error;
if (error != 0)
return error;
return ENOMEM;
/* XXX should use fr_inobj */
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;
}
}
/*
* Check that the NAT entry doesn't already exist in the kernel.
*/
goto junkput;
}
goto junkput;
}
} else {
goto junkput;
}
/*
* Restore ap_session_t structure. Include the private data allocated
* if it was there.
*/
goto junkput;
}
ap_sess_list = aps;
if (in)
goto junkput;
}
} else {
}
}
/*
* If there was a filtering rule associated with this entry then
* build up a new one.
*/
goto junkput;
}
} else {
for (n = nat_instances; n; n = n->nat_next)
break;
if (!n) {
goto junkput;
}
}
}
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;
{
if (logtype != 0 && nat_logging != 0)
/*
* Take it as a general indication that all the pointers are set if
* nat_pnext is set.
*/
}
}
}
}
}
else
}
return;
}
#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.
*/
pfil_delayed_copy = 1;
}
}
/*
* 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.
*/
fr_forgetnat((void *)nat);
}
/* ------------------------------------------------------------------------ */
/* 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()
{
int j = 0;
/*
* ALL NAT mappings deleted, so lets just make the deletions
* quicker.
*/
sizeof(nat_table[0]) * ipf_nattable_sz);
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()
{
int i = 0;
if (n->in_use == 0) {
KFREE(n);
} else {
n->in_flags |= IPN_DELETE;
}
i++;
}
pfil_delayed_copy = 1;
nat_masks = 0;
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. */
/* ------------------------------------------------------------------------ */
{
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.
*/
in, 0);
}
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_TCPUDPICMP) {
} 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. */
/* */
/* ------------------------------------------------------------------------ */
{
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;
#endif
move = 1;
nflags &= NAT_FROMRULE;
/* Give me a new nat */
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.
*/
return natl;
}
if (move == -1) {
goto badnat;
}
} else {
/*
* NAT_INBOUND is used only for redirects rules
*/
return natl;
}
if (move == -1) {
goto badnat;
}
}
nat_delrdr(np);
nat_addrdr(np);
nat_delnat(np);
nat_addnat(np);
}
}
if (flags & IPN_TCPUDP) {
} else if (flags & IPN_ICMPQUERY) {
dport = 0;
}
#ifndef IRE_ILL_CN
#else
#endif /* IRE_ILL_CN */
if (direction == NAT_OUTBOUND)
else
} else
#endif
if (direction == NAT_OUTBOUND)
else
} else {
if (!(flags & IPN_TCPUDPICMP)) {
}
}
return NULL;
}
return nat;
return NULL;
}
/* ------------------------------------------------------------------------ */
/* 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 (nat_logging)
if (nat_insert(nat) == 0) {
}
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 nat_insert(nat)
{
/*
* 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
ifq = &nat_icmptq;
else
}
}
} else {
}
if (nat_instances)
nat_instances = nat;
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. */
/* ------------------------------------------------------------------------ */
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.
*/
return NULL;
/*
* If it's not an error type, then return.
*/
if (!nat_icmperrortype4(type))
return NULL;
/*
* Check packet size
*/
return NULL;
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
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
}
}
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;
{
return NULL;
/*
* nat_icmperrorlookup() will return NULL for `defective' packets.
*/
return NULL;
flags = 0;
sumd2 = 0;
*nflags = IPN_ICMPERR;
/*
* Need to adjust ICMP header to include the real IP#'s and
* port #'s. Only apply a checksum change relative to the
* IP address change as it will be modified again in fr_checknatout
* for both address and port. Two checksum changes are
* necessary for the two header address changes. Be careful
* to only modify the checksum once for the port # and twice
* for the IP#.
*/
/*
* Step 1
* Fix the IP addresses in the offending IP packet. You also need
* to adjust the IP header checksum of that offending IP packet
* and the ICMP checksum of the ICMP error message itself.
*
* Unfortunately, for UDP and TCP, the IP addresses are also contained
* in the pseudo header that is used to compute the UDP resp. TCP
* checksum. So, we must compensate that as well. Even worse, the
* change in the UDP and TCP checksums require yet another
* adjustment of the ICMP checksum of the ICMP error message.
*/
} else {
}
/*
* Fix IP checksum of the offending IP packet to adjust for
* the change in the IP address.
*
* Normally, you would expect that the ICMP checksum of the
* ICMP error message needs to be adjusted as well for the
* IP address change in oip.
* However, this is a NOP, because the ICMP checksum is
* calculated over the complete ICMP packet, which includes the
* changed oip IP addresses and oip->ip_sum. However, 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),
* so no change in the icmp_cksum is necessary.
*
* Be careful that nat_dir refers to the direction of the
* offending IP packet (oip), not to its ICMP response (icmp)
*/
/* Fix icmp cksum : IP Addr + Cksum */
/*
* Fix UDP pseudo header checksum to compensate for the
* IP address change.
*/
/*
* The UDP checksum is optional, only adjust it
* if it has been set.
*/
/*
* Fix ICMP checksum to compensate the UDP
* checksum adjustment.
*/
}
/*
* Fix TCP pseudo header checksum to compensate for the
* IP address change. Before we can do the change, we
* must make sure that oip is sufficient large to hold
* the TCP checksum (normally it does not!).
*/
/*
* Fix ICMP checksum to compensate the TCP checksum
* adjustment.
*/
}
} else {
/*
* Fix IP checksum of the offending IP packet to adjust for
* the change in the IP address.
*
* Normally, you would expect that the ICMP checksum of the
* ICMP error message needs to be adjusted as well for the
* IP address change in oip.
* However, this is a NOP, because the ICMP checksum is
* calculated over the complete ICMP packet, which includes the
* changed oip IP addresses and oip->ip_sum. However, 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),
* so no change in the icmp_cksum is necessary.
*
* Be careful that nat_dir refers to the direction of the
* offending IP packet (oip), not to its ICMP response (icmp)
*/
/* Fix icmp cksum : IP Addr + Cksum */
/* XXX FV : without having looked at Solaris source code, it seems unlikely
* that SOLARIS would compensate this in the kernel (a body of an IP packet
* in the data section of an ICMP packet). I have the feeling that this should
* be unconditional, but I'm not in a position to check.
*/
/*
* Fix UDP pseudo header checksum to compensate for the
* IP address change.
*/
/*
* The UDP checksum is optional, only adjust it
* if it has been set
*/
/*
* Fix ICMP checksum to compensate the UDP
* checksum adjustment.
*/
}
/*
* Fix TCP pseudo header checksum to compensate for the
* IP address change. Before we can do the change, we
* must make sure that oip is sufficient large to hold
* the TCP checksum (normally it does not!).
*/
/*
* Fix ICMP checksum to compensate the TCP checksum
* adjustment.
*/
}
#endif
}
if ((flags & IPN_TCPUDP) != 0) {
/*
* Step 2 :
* well, based on the NAT specification. Of course such
* a change must be reflected in the ICMP checksum as well.
*
* Advance notice : Now it becomes complicated :-)
*
* of the offending IP packet, you need to adjust that checksum
* as well... but, if you change, you must change the icmp
* checksum *again*, to reflect that change.
*
* To further complicate: the TCP checksum is not in the first
* 8 bytes of the offending ip packet, so it most likely is not
* available. OSses like Solaris return enough bytes to
* include the TCP checksum. So we have to check if the
* ip->ip_len actually holds the TCP checksum of the oip!
*/
/*
* Fix ICMP checksum to compensate port
* adjustment.
*/
/*
* Fix udp checksum to compensate port
* adjustment. NOTE : the offending IP packet
* flows the other direction compared to the
* ICMP message.
*
* The UDP checksum is optional, only adjust
* it if it has been set.
*/
/*
* Fix ICMP checksum to compenstate
* UDP checksum adjustment.
*/
}
/*
* Fix TCP checksum (if present) to compensate
* port adjustment. NOTE : the offending IP
* packet flows the other direction compared to
* the ICMP message.
*/
/*
* Fix ICMP checksum to compensate
* TCP checksum adjustment.
*/
}
}
} else {
/*
* Fix ICMP checksum to compensate port
* adjustment.
*/
/*
* Fix UDP checksum to compensate port
* adjustment. NOTE : the offending IP
* packet flows the other direction compared
* to the ICMP message.
*
* The UDP checksum is optional, only adjust
* it if it has been set.
*/
/*
* Fix ICMP checksum to compensate
* UDP checksum adjustment.
*/
}
/*
* Fix TCP checksum (if present) to compensate
* port adjustment. NOTE : the offending IP
* packet flows the other direction compared to
* the ICMP message.
*/
/*
* Fix ICMP checksum to compensate
* TCP checksum adjustment.
*/
}
}
}
if (sumd2 != 0) {
} else {
}
}
} else if ((flags & IPN_ICMPQUERY) != 0) {
/*
* XXX - what if this is bogus hl and we go off the end ?
* In this case, nat_icmperrorlookup() will have returned NULL.
*/
/*
* 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 == NAT_INBOUND is impossible for icmp queries */
}
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
if ((flags & IPN_TCPUDP) != 0) {
} else {
sport = 0;
dport = 0;
}
goto find_in_wild_ports;
continue;
} else {
continue;
}
}
switch (p)
{
case IPPROTO_GRE :
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;
} else {
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. */
/* ------------------------------------------------------------------------ */
static void nat_tabmove(nat)
{
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;
if ((flags & IPN_TCPUDP) != 0) {
} else {
sport = 0;
dport = 0;
}
goto find_out_wild_ports;
continue;
} else {
continue;
}
}
switch (p)
{
case IPPROTO_GRE :
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;
} else {
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 */
/* ------------------------------------------------------------------------ */
{
/*
* 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 {
ifq2 = &nat_icmptq;
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;
int rval;
return 0;
else
{
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;
}
NAT_OUTBOUND))) {
break;
}
}
while (nmsk) {
msk <<= 1;
if (nmsk & 0x80000000)
break;
nmsk <<= 1;
}
if (nmsk != 0) {
nmsk <<= 1;
goto maskloop;
}
}
}
if (rval == 1) {
}
} else
rval = 0;
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;
/*
* Fix up checksums, not by recalculating them, but
* simply computing adjustments.
*/
if (nflags == IPN_ICMPERR) {
else
}
}
nat->nat_ipsumd);
else
nat->nat_ipsumd);
}
#endif
}
}
}
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. */
/* ------------------------------------------------------------------------ */
{
int rval;
return 0;
dport = 0;
natadd = 1;
nflags = 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;
} else {
continue;
continue;
}
continue;
}
}
break;
}
}
while (rmsk) {
msk <<= 1;
if (rmsk & 0x80000000)
break;
rmsk <<= 1;
}
if (rmsk != 0) {
rmsk <<= 1;
goto maskloop;
}
}
}
if (rval == 1) {
}
} else
rval = 0;
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;
/* ------------------------------------------------------------- */
/* 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.
*/
else
#endif
}
}
}
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 */
/* */
/* Make special protocol timeout adjustments and return a pointer to the */
/* checksum location for updating. */
/* ------------------------------------------------------------------------ */
{
} 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()
{
(void) nat_clearlist();
(void) nat_flushtable();
}
}
}
}
}
sizeof(u_long *) * ipf_nattable_sz);
}
sizeof(u_long *) * ipf_nattable_sz);
}
if (fr_nat_maxbucket_reset == 1)
fr_nat_maxbucket = 0;
if (fr_nat_init == 1) {
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()
{
int s;
#endif
int i;
SPL_NET(s);
break;
}
}
break;
}
}
SPL_X(s);
}
/* ------------------------------------------------------------------------ */
/* Function: fr_natsync */
/* Returns: Nil */
/* Parameters: ifp(I) - pointer to network interface */
/* */
/* Walk through all of the currently active NAT sessions, looking for those */
/* which need to have their translated address updated. */
/* ------------------------------------------------------------------------ */
void fr_natsync(ifp)
void *ifp;
{
ipnat_t *n;
void *ifp2;
int s;
#endif
if (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.
*/
SPL_NET(s);
if (fr_running <= 0) {
return;
}
/*
* Change the map-to address to be the same as the
* new one.
*/
continue;
/*
* Readjust the checksum adjustment to take into
* account the new IP#.
*/
/* XXX - dont change for TCP when solaris does
* hardware checksumming.
*/
}
n->in_ifps[0] = (void *)-1;
}
}
}
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_icmperrortype4 */
/* Returns: int - 1 == success, 0 == failure */
/* Parameters: icmptype(I) - ICMP type number */
/* */
/* Tests to see if the ICMP type number passed is an error type or not. */
/* ------------------------------------------------------------------------ */
int icmptype;
{
switch (icmptype)
{
case ICMP_SOURCEQUENCH :
case ICMP_PARAMPROB :
case ICMP_REDIRECT :
case ICMP_TIMXCEED :
case ICMP_UNREACH :
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
void *items[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 nat_ifdetach(ifp)
void *ifp;
{
frsync();
return;
}
#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. */
/* ------------------------------------------------------------------------ */
void fr_natderef(natp)
{
}
/* ------------------------------------------------------------------------ */
/* 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;
}
}
return NULL;
}
}
if (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 */
/* fin(I) - pointer to packet information */
/* 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;
}
}
}
}