📄 nat_fragment.c
字号:
/* nat_fragment.c */
/* Copyright 2000-2003 Wind River Systems, Inc. */
/* @format.tab-size 4, @format.use-tabs true, @format.new-line lf */
/*
modification history
--------------------
01e,18jul03,vks changes for Virtual Stack
01d,16jun03,myz rewrite this fragment translation module.
01c,23apr03,zhu updated copyright
01b,26sep02,vvv modified to add new fragment_info at start of fragment_list
01a,24sep02,vvv replaced rw_container references with direct linked list
access for improved performance; added semaphore
protection (SPR #79675)
*/
#include "stdio.h"
#include "nat.h"
#include "dllLib.h"
#include "netLib.h"
/*
DESCRIPTION
This library handles the fragments passing through the NAT device.
The reason the fragments need to be treated differently is because that only
the first fragment of the datagram carries the transport level protocol header
such as the UDP header, the subsequent fragments simply carry an IP header.
Therefore there is simply not enough information for the NAT to perform the
address translation in the NAPT mode when fragments come in from the global
port side.
The algorithm used by this library can be described as follows:
A translation entry(type of NAT_FRAGMENT_ENTRY) will be created when the first
fragment comes in. The fields in the IP header which uniquely identifies the
subsequent fragments for the same datagram are saved in the entry. Then the
first fragment will continue to pass through the NAT address translation engine
just as the non-fragmented packet does. The translated address will be saved
to the entry. The subsequent fragments will not go through the NAT address
translation engine, rather search the translation entries to find one with the
same datagram identifier and applies the translated address in the entry to
the proper address field. On the last fragment, the translation entry will be
freed. To make this process as efficient as possible, the program uses a
combination of the cache and list objects for the translation entry operation.
The cache consists of an array of the translation entries at the fixed location.
Simply a flag is used to indicate whether it is free or not. The array index
is taken from the last least significant bits of the fragment identifier in
the IP header. The cache entry is always used first for the fragment
translation. In case another translation entry is needed with the same index,
then the program will get one from a free entry list and put it to a list
associated with the cache entry. We expect most operation is done in the cache
entries.
RFC3022 section 6.3. states:
"Translation of outbound TCP/UDP fragments(i.e., those originating from private
hosts) in NAPT setup are doomed to fail. The reason is as follows. Only the
first fragment contains the TCP/UDP header that would be necessary to associate
the packet to a session for translation purposes. Subsequent fragments do not
contain TCP/UDP port information, but simply carry the same fragmentation
identifier specified in the first fragment. Say, two private hosts originated
fragmented TCP/UDP packets to the same destination host. And, they happened to
use the same fragmentation identifier. When the target host receives the two
unrelated datagrams, carrying same fragmentation id, and from the same assigned
host address, it is unable to determine which of the two sessions the datagrams
belong to. Consequently, both sessions will be corrupted."
This library will check whether this scenario occurs. If it does, it
will drop the fragments other then for the first session. Therefore instead
of corrupting both sessions, this handling will keep the first one alive.
*/
/* #define FRAGMENT_DEBUG */
#ifdef FRAGMENT_DEBUG
int fragDebug = 1;
#define DBG_PRINT(X) \
do { \
if (fragDebug) \
printf X; \
}while(0)
#else
#define DBG_PRINT(X)
#endif
#ifdef VIRTUAL_STACK
#include "netinet/vsLib.h"
#include "netinet/vsIp.h"
#endif /* VIRTUAL_STACK */
/* local defines */
/* This parameter should be adjusted based on the number of the outstanding
* fragmented datagrams going through the NAT device at the same time.
* For example, in case there is always one or no fragmented traffic,
* the parameter FRAG_CACHE_ARRAY_LEN can be set to 1.
*/
#define FRAG_CACHE_ARRAY_LEN (1 << 5) /* 2**5, must be in the order of 2 */
#define FRAG_ID_HASH_MASK (FRAG_CACHE_ARRAY_LEN - 1)
#define FRAG_PREALLOC_ENTRY_NUM 32
/* fragment translation entry's flag value */
#define ENTRY_FREE 0x0 /* free to use */
#define ENTRY_IN_USE 0x1 /* being used */
/* index for global port, or ALL local ports*/
#define GLOBAL_PORT_INX 0
#define LOCAL_PORTS_INX 1
/* typedefs */
/* Fragment translation entry */
typedef struct
{
union {
DL_NODE node; /* node as list element */
DL_LIST list; /* list header in cache entry */
} u;
ULONG orgSrcAddr; /* original fragment source address */
ULONG orgDstAddr; /* original fragment destination address */
UINT16 fragId; /* fragment identifier in IP header */
UINT8 proto; /* protocol type in IP header */
UINT8 ttl; /* time to live for this entry same as in IP header */
ULONG newAddr; /* the translated address */
UINT32 flag; /* entry's usage flag */
} NAT_FRAGMENT_ENTRY;
/* Memory resource node, used to keep track allocated memory */
typedef struct
{
DL_NODE node;
void * pMem;
} NAT_FRAG_MEM_RESOURCE;
/* Fragment translation resource holder */
typedef struct
{
DL_LIST fragFreeList; /* The free translation entry list */
DL_LIST memList; /* The list to keep track of the allocated memory*/
NAT_FRAGMENT_ENTRY * pArray; /* the cache translation entry array */
UINT32 outstandingEntries; /* number of outstanding translation entries*/
} NAT_FRAGMENT_RESOURCE_DESC;
/* One entry for Global port, one for ALL local ports */
LOCAL NAT_FRAGMENT_RESOURCE_DESC fragResrcDesc[2];
LOCAL NAT_FRAGMENT_RESOURCE_DESC *
pFragResrcDesc[2] = {&fragResrcDesc[0],&fragResrcDesc[1]};
/* function prototypes */
LOCAL BOOL entryTimerEventProcess (NAT_FRAGMENT_ENTRY *, DL_LIST *);
LOCAL void fragTranTimerNetJob (int);
/* externals */
#ifndef VIRTUAL_STACK
extern int _ipCfgFlags;
#endif /* VIRTUAL_STACK */
/****************************************************************************
*
* fragMemGet - Allocate a chunk of memory and keep the pointer in a list
*
* RETURNS: the allocated memory or NULL
*/
LOCAL void * fragMemGet
(
int portInx, /* global or local index */
int size /* size of requested memory */
)
{
NAT_FRAG_MEM_RESOURCE * pResource;
/* add overhead for remembering the allocated resource */
size += sizeof(NAT_FRAG_MEM_RESOURCE);
if ((pResource = (NAT_FRAG_MEM_RESOURCE *)malloc(size)) == NULL)
{
nat_printf(NAT_PRINTF_ERROR,
"unable to allocate fragment list memory\n");
return NULL;
}
/* zero out the memory */
bzero((char *)pResource,size);
/* remember the allocated memory*/
pResource->pMem = (void *)pResource;
dllAdd(&pFragResrcDesc[portInx]->memList,&pResource->node);
return((void *)(pResource + 1));
}
/*****************************************************************************
*
* fragFreeTranEntryAdd - Add pre-allocated translation entries to a free list
*
* This routine creates a batch of free translation entries and add them to the
* list.
*
*/
LOCAL STATUS fragFreeTranEntryAdd
(
int portInx, /* global or local index */
int numOfEntries /* number of translation entries to be added to the list*/
)
{
NAT_FRAGMENT_ENTRY * pEntry;
int size;
int j;
size = sizeof(NAT_FRAGMENT_ENTRY) * numOfEntries;
if ((pEntry = (NAT_FRAGMENT_ENTRY *)fragMemGet(portInx,size)) == NULL)
return ERROR;
/* put the pre-allocated free entries to the list */
for (j = 0; j < numOfEntries; j++)
{
(pEntry + j)->flag = ENTRY_IN_USE;
dllAdd(&pFragResrcDesc[portInx]->fragFreeList,
&(pEntry + j)->u.node);
}
return OK;
}
/****************************************************************************
*
* natFragTranModuleInit - Initialize this fragment handling module
*
* This routine initializes and allocates the memory resources required for
* the NAT fragment handling.
*
* RETURNS: resource handler or NULL if fail
*/
void * natFragTranModuleInit (void)
{
int portInx;
for (portInx = 0; portInx < 2; portInx++)
{
dllInit(&pFragResrcDesc[portInx]->fragFreeList);
dllInit(&pFragResrcDesc[portInx]->memList);
pFragResrcDesc[portInx]->outstandingEntries = 0;
/* allocate a chunk of memory for the entry array used as the cache */
pFragResrcDesc[portInx]->pArray = (NAT_FRAGMENT_ENTRY *)fragMemGet(
portInx,(sizeof(NAT_FRAGMENT_ENTRY)) * FRAG_CACHE_ARRAY_LEN);
if (pFragResrcDesc[portInx]->pArray == NULL)
return NULL;
/* create the pre-allocated free entries and put to the list */
if (fragFreeTranEntryAdd(portInx,FRAG_PREALLOC_ENTRY_NUM) != OK)
return (NULL);
}
return ((void *)(&pFragResrcDesc[0]));
}
/****************************************************************************
*
* natFragTranCleanup - Free the resource allocated for this module
*
* This routine frees the allocated memory resources for this module
*
* RETURNS: NONE
*/
void natFragTranCleanup
(
void * pResrcHandle
)
{
int portInx;
NAT_FRAG_MEM_RESOURCE * pResource;
NAT_FRAGMENT_RESOURCE_DESC ** pResrcDesc;
pResrcDesc = (NAT_FRAGMENT_RESOURCE_DESC **)pResrcHandle;
for (portInx = 0; portInx < 2; portInx++)
{
while ((pResource = (NAT_FRAG_MEM_RESOURCE *)dllGet(
&pResrcDesc[portInx]->memList)) != NULL)
free(pResource->pMem);
}
}
/***************************************************************************
*
* natFragTranEntryGet - Get a fragment translation entry
*
* This routine first check whether the cache entry is available. If it does,
* The cache entry is returned. If it does not, it get one from the free entry
* list.
*
* RETURNS: the translation entry pointer or NULL if fail
*/
void * natFragTranEntryGet
(
enum NAT_PORT_TYPE portType, /* Global or local port type */
IP_PACKET * pIpPacket /* fragment packet */
)
{
int inx;
NAT_FRAGMENT_ENTRY * pEntry;
NAT_FRAGMENT_ENTRY * pNewEntry;
int portInx;
/* fragment coming from global port or local ports */
portInx = portType == NAT_GLOBAL_PORT? GLOBAL_PORT_INX: LOCAL_PORTS_INX;
/* index to cache entry */
inx = pIpPacket->header.identifier & FRAG_ID_HASH_MASK;
pEntry = &pFragResrcDesc[portInx]->pArray[inx];
/* check if the cache entry is available */
if (!(pEntry->flag & ENTRY_IN_USE))
{
/* available, save the fragment identification info */
pEntry->orgSrcAddr = pIpPacket->header.source_address;
pEntry->orgDstAddr = pIpPacket->header.destination_address;
pEntry->fragId = pIpPacket->header.identifier;
pEntry->ttl = pIpPacket->header.time_to_live;
pEntry->proto = pIpPacket->header.protocol;
pEntry->flag = ENTRY_IN_USE;
DBG_PRINT(("Cache Entry: inx=0x%x, srcAdr=0x%x, dstAdr=0x%x, id=0x%x\n",
inx,(int)pEntry->orgSrcAddr,(int)pEntry->orgDstAddr,
(int)pEntry->fragId));
}
else
{
/* not available, get a entry from the free list */
if ((pNewEntry = (NAT_FRAGMENT_ENTRY *)dllGet(
&pFragResrcDesc[portInx]->fragFreeList)) == NULL)
{
/* no more free entries, unlikely, resupply it */
if (fragFreeTranEntryAdd(portInx,16) != OK)
return (NULL);
pNewEntry = (NAT_FRAGMENT_ENTRY *)dllGet(
&pFragResrcDesc[portInx]->fragFreeList);
}
/* save the fragment identification info */
pNewEntry->orgSrcAddr = pIpPacket->header.source_address;
pNewEntry->orgDstAddr = pIpPacket->header.destination_address;
pNewEntry->fragId = pIpPacket->header.identifier;
pNewEntry->ttl = pIpPacket->header.time_to_live;
pNewEntry->proto = pIpPacket->header.protocol;
/* put to the list associated with the cache entry, so it can be
* easily searched by the subsequenct fragments of the datagram.
*/
dllAdd(&pEntry->u.list,&pNewEntry->u.node);
pEntry = pNewEntry;
DBG_PRINT(("list Entry: inx=0x%x, srcAdr=0x%x, dstAdr=0x%x, id=0x%x\n",
inx,(int)pEntry->orgSrcAddr,(int)pEntry->orgDstAddr,
pEntry->fragId));
}
pFragResrcDesc[portInx]->outstandingEntries++;
return (void *)pEntry;
}
/******************************************************************************
*
* fragTranIdMatch - Match the fragment from the same datagram
*
* RETURNS: the translation entry pointer or NULL if not found
*
*/
LOCAL NAT_FRAGMENT_ENTRY * fragTranIdMatch
(
NAT_FRAGMENT_ENTRY * pEntry, /* The fragment cache translation entry */
IP_PACKET * pIpPacket /* The fragment packet to match */
)
{
while (pEntry)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -