Files
m241Neelima/components/ip_net2-6.9/ipnet2/src/ipnet_nat.c
T
2026-07-30 11:19:44 +05:30

4026 lines
150 KiB
C

/*
******************************************************************************
* INTERPEAK SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_nat.c,v $ $Revision: 1.129.10.7 $
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_nat.c,v $
* $Author: jren $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Markus Carlstedt <markus@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
* The core of the NAT.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#include "ipnet_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#ifdef IPNET_USE_NAT
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_type.h>
#include <ipcom_clib.h>
#include <ipcom_sock.h>
#include <ipcom_sock2.h>
#include <ipcom_syslog.h>
#include <ipcom_shell.h>
#include <ipcom_err.h>
#include "ipnet.h"
#include "ipnet_h.h"
#include "ipnet_nat.h"
#include "ipnet_nat_h.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
IP_EXTERN Ipnet_nat_trigger example_trigger;
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_nat_syslog_mapping
*===========================================================================
* Description: Logs information about a NAT mapping to syslog.
* Parameters: nat_mapping - pointer to the NAT mapping.
* Returns: nothing.
*/
#ifdef IPNET_DEBUG
IP_STATIC void
ipnet_nat_syslog_mapping(Ipnet_nat_mapping *nat_mapping)
{
char src[64];
char nat_addr[IP_INET_ADDRSTRLEN];
char dst[IP_INET_ADDRSTRLEN];
char protostr[16];
char state[8];
char buf[200];
Ip_u16 sport, dport, nat_sport;
ipcom_inet_ntop(IP_AF_INET, &nat_mapping->inner.nat_addr_n, nat_addr, sizeof(nat_addr));
ipcom_inet_ntop(IP_AF_INET, &nat_mapping->inner.tuple.dst_n, dst, sizeof(dst));
if(nat_mapping->rule->inner.map == 1 || nat_mapping->rule->inner.map_block == 1
|| nat_mapping->rule->inner.rdr == 1)
{
ipcom_inet_ntop(IP_AF_INET, &nat_mapping->inner.tuple.src_n, src, sizeof(src));
}
else
{
#ifdef IPNET_USE_NAT_PT
ipcom_inet_ntop(IP_AF_INET6, &nat_mapping->inner.tuple.pt_src, src, sizeof(src));
#else
IP_PANIC();
return;
#endif
}
if(nat_mapping->inner.inner_state == IPNET_NAT_MAPPING_CLO)
ipcom_strcpy(state, "CLO/");
else if(nat_mapping->inner.inner_state == IPNET_NAT_MAPPING_SYN)
ipcom_strcpy(state, "SYN/");
else if(nat_mapping->inner.inner_state == IPNET_NAT_MAPPING_EST)
ipcom_strcpy(state, "EST/");
else if(nat_mapping->inner.inner_state == IPNET_NAT_MAPPING_FIN)
ipcom_strcpy(state, "FIN/");
else
ipcom_strcpy(state, "NUL/");
if(nat_mapping->inner.outer_state == IPNET_NAT_MAPPING_CLO)
ipcom_strcpy(&state[4], "CLO");
else if(nat_mapping->inner.outer_state == IPNET_NAT_MAPPING_SYN)
ipcom_strcpy(&state[4], "SYN");
else if(nat_mapping->inner.outer_state == IPNET_NAT_MAPPING_EST)
ipcom_strcpy(&state[4], "EST");
else if(nat_mapping->inner.outer_state == IPNET_NAT_MAPPING_FIN)
ipcom_strcpy(&state[4], "FIN");
else
ipcom_strcpy(&state[4], "NUL");
if(nat_mapping->inner.tuple.fragoff == 0
&& (nat_mapping->inner.tuple.protocol == IP_IPPROTO_UDP
|| nat_mapping->inner.tuple.protocol == IP_IPPROTO_TCP
|| (nat_mapping->inner.tuple.protocol == IP_IPPROTO_ICMP
&& IPNET_NAT_IS_ICMP_REQUEST(nat_mapping->inner.tuple.icmp_type))))
{
sport = ip_ntohs(nat_mapping->inner.tuple.src_port_n);
dport = ip_ntohs(nat_mapping->inner.tuple.dst_port_n);
nat_sport = ip_ntohs(nat_mapping->inner.nat_port_n);
ipcom_sprintf(buf, "%s:%d -> %s:%d proto %s (%s:%d) state %s\n"
"-------------------------------------------------------------",
src, sport, dst, dport, ipnet_nat_protocol_name(nat_mapping->inner.tuple.protocol, protostr, sizeof(protostr)),
nat_addr, nat_sport, state);
}
else
{
if(nat_mapping->inner.tuple.fragoff == 0)
ipcom_sprintf(buf, "%s -> %s proto %s (%s) state %s\n"
"-------------------------------------------------------------",
src, dst, ipnet_nat_protocol_name(nat_mapping->inner.tuple.protocol, protostr, sizeof(protostr)),
nat_addr, state);
else
ipcom_sprintf(buf, "%s -> %s proto %s FRAG (%s) state %s\n"
"-------------------------------------------------------------",
src, dst, ipnet_nat_protocol_name(nat_mapping->inner.tuple.protocol, protostr, sizeof(protostr)),
nat_addr, state);
}
IPCOM_LOG0(DEBUG, buf);
}
#endif
/*
*===========================================================================
* ipnet_nat_mapping_get
*===========================================================================
* Description: Retrieves a pointer to a NAT mapping in the linked list of
* mappings using its sequential number. Sequential numbering
* starts with zero at the first element in the list.
* Parameters: i - sequential number
* Returns: Pointer to the mapping or IP_NULL if not found.
*/
IP_STATIC Ipnet_nat_mapping *
ipnet_nat_mapping_get(int i)
{
int c;
Ipcom_list *l;
for (c = 0, l = ipcom_list_first(&ipnet->nat.head_mapping);
l != IP_NULL;
c++, l = ipcom_list_next(l))
{
if (c == i)
return (Ipnet_nat_mapping*) l;
}
return IP_NULL;
}
/*
*===========================================================================
* ipnet_nat_get_id
*===========================================================================
* Description: Allocates a port from the pool of ports available for NAT
* port translation. Attempt to avoid reusing expired ports by
* choosing the next available port in the pool before the last
* used.
* Parameters: port_hi - end port number.
* port_lo - start port number.
* ports - pointer to port array.
* last_port - pointer to the last used port in the pool.
* port_in - port used by incoming packet.
* Returns: The allocated port or -1 if unavailable.
*/
IP_STATIC Ip_s32
ipnet_nat_get_id(Ip_u16 port_hi,
Ip_u16 port_lo,
Ip_u32 *ports,
Ip_u32 *last_port,
Ip_u16 port_in)
{
Ip_u32 i, j, num_longs, start_long, start_index;
Ip_s32 cnt = -1;
int try_again = 1;
ip_assert(ports != IP_NULL);
num_longs = ((port_hi - port_lo)>>5) + 1;
if (*last_port >= port_hi)
{
start_long = 0;
start_index = 0;
try_again = 0;
}
else
{
start_long = (*last_port + 1 - (Ip_u32)port_lo) >> 5;
start_index = (*last_port + 1 - (Ip_u32)port_lo) % 32;
}
/* Check if incomming port number is available */
if (port_in <= port_hi && port_in >= port_lo)
{
Ip_u32 din = port_in - port_lo;
if (!IP_BIT_ISSET(ports[din/32], 1<<(din%32)))
{
cnt = din;
goto done;
}
}
loop:
for(i=start_long; i<num_longs; i++)
{
if(ports[i] != 0xffffffff)
{
for(j=start_index; j<32; j++)
{
if(!IP_BIT_ISSET(ports[i], 1<<j))
{
cnt = (i<<5) + j;
IP_BIT_SET(ports[i], 1<<j);
goto done;
}
}
}
}
done:
if(cnt < 0 || (cnt + (Ip_s32)port_lo) > port_hi)
{
if (try_again)
{
start_long = 0;
start_index = 0;
try_again = 0;
cnt = -1;
goto loop;
}
return -1;
}
cnt += port_lo;
*last_port = (Ip_u32)cnt;
return cnt;
}
/*
*===========================================================================
* ipnet_nat_release_id
*===========================================================================
* Description: Return previously allocated port to the pool of ports
* available for NAT port translation.
* Parameters: port - port to release.
* port_lo - start port number.
* ports - pointer to port array.
* Returns: nothing.
*
*/
IP_STATIC void
ipnet_nat_release_id(Ip_u16 port, Ip_u16 port_lo, Ip_u32 *ports)
{
int id;
ip_assert(ports != IP_NULL);
id = (int)(port - port_lo);
IP_BIT_CLR(ports[id>>5], 1<<(id%32));
}
/*
*===========================================================================
* ipnet_nat_clean_mapping
*===========================================================================
* Description: Deletes a NAT mapping. Cancels timeout and releases the port.
* Parameters: nat_mapping - pointer to the mapping.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_clean_mapping(Ipnet_nat_mapping *nat_mapping)
{
Ip_u16 nat_port;
Ipnet_nat_rule *rule;
ipnet_timeout_cancel(nat_mapping->tmo);
ipcom_list_remove(&nat_mapping->list);
(void)ipcom_hash_remove(ipnet->nat.hash_out_port, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_out_noport, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_port, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_noport, nat_mapping);
ip_assert(ipnet->nat.head_mapping.size > 0 ||
(ipnet->nat.head_mapping.size == 0
&& ipnet->nat.hash_out_port->elem == 0
&& ipnet->nat.hash_out_noport->elem == 0
&& ipnet->nat.hash_in_port->elem == 0
&& ipnet->nat.hash_in_noport->elem == 0));
rule = nat_mapping->rule;
if (rule->ports != IP_NULL)
{
ip_assert(nat_mapping->inner.autoport == 0);
nat_port = ip_ntohs(nat_mapping->inner.nat_port_n);
if (nat_port >= rule->inner.port_lo && nat_port <= rule->inner.port_hi)
ipnet_nat_release_id(nat_port, rule->inner.port_lo, rule->ports);
}
if (nat_mapping->inner.autoport != 0)
{
nat_port = ip_ntohs(nat_mapping->inner.nat_port_n);
ip_assert(rule->ports == IP_NULL);
if (nat_port >= ipnet->nat.inner.port_lo && nat_port <= ipnet->nat.inner.port_hi)
ipnet_nat_release_id(nat_port, ipnet->nat.inner.port_lo, ipnet->nat.ports);
}
ipcom_free(nat_mapping);
}
/*
*===========================================================================
* ipnet_nat_mapping_timeout
*===========================================================================
* Description: Timeout handler for expired mappings.
* Parameters: nat_mapping - pointer to the mapping.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_mapping_timeout(Ipnet_nat_mapping *nat_mapping)
{
Ip_u32 sec_now;
sec_now = ipnet_sec_now();
if (IPCOM_IS_GT(nat_mapping->inner.tmo_abs_sec, sec_now))
{
(void) ipnet_timeout_schedule((nat_mapping->inner.tmo_abs_sec - sec_now) * 1000,
(Ipnet_timeout_handler) ipnet_nat_mapping_timeout,
nat_mapping,
&nat_mapping->tmo);
return;
}
#ifdef IPNET_DEBUG
IPCOM_LOG0(DEBUG, "ipnet_nat_mapping_timeout() :: Mapping expired");
ipnet_nat_syslog_mapping(nat_mapping);
#endif
ipnet_nat_clean_mapping(nat_mapping);
ipnet->nat.inner.stats.mappings_expired++;
}
/*
*===========================================================================
* ipnet_nat_mapping_to_string
*===========================================================================
* Description: Helper function called when timeouts are listed with IPNET.
* Parameters: nat_mapping - pointer to the NAT mapping.
* buf - pointer to buffer for printing info.
* buflen - length of buffer.
* Returns: The number of bytes written.
*/
#ifdef IPNET_DEBUG
IP_STATIC int
ipnet_nat_mapping_to_string(Ipnet_nat_mapping *nat_mapping, char *buf, int buf_len)
{
(void)nat_mapping;
(void)buf;
(void)buf_len;
return ipcom_snprintf(buf, buf_len, "NAT mapping. Use \"nat -m\" for details.");
}
#endif
/*
*===========================================================================
* ipnet_nat_obj_hash_out_port
*===========================================================================
* Description: Hash value compute function for the object in the output port
* hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_obj_hash_out_port(Ipnet_nat_mapping *nat_mapping)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_mapping->inner.tuple.protocol, sizeof(nat_mapping->inner.tuple.protocol), 0);
initval = ipcom_hash_update(&nat_mapping->inner.tuple.dst_n, sizeof(nat_mapping->inner.tuple.dst_n), initval);
initval = ipcom_hash_update(&nat_mapping->inner.tuple.dst_port_n, sizeof(nat_mapping->inner.tuple.dst_port_n), initval);
initval = ipcom_hash_update(&nat_mapping->inner.tuple.src_n, sizeof(nat_mapping->inner.tuple.src_n), initval);
return ipcom_hash_update(&nat_mapping->inner.tuple.src_port_n, sizeof(nat_mapping->inner.tuple.src_port_n), initval);
}
/*
*===========================================================================
* ipnet_nat_key_hash_out_port
*===========================================================================
* Description: Hash value compute function for the search key in the output
* port hash table.
* Parameters: nat_key - pointer to the search key.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_key_hash_out_port(Ipnet_nat_hash_key *nat_key)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_key->protocol, sizeof(nat_key->protocol), 0);
initval = ipcom_hash_update(&nat_key->dst_n, sizeof(nat_key->dst_n), initval);
initval = ipcom_hash_update(&nat_key->dst_port_n, sizeof(nat_key->dst_port_n), initval);
initval = ipcom_hash_update(&nat_key->src_n, sizeof(nat_key->src_n), initval);
return ipcom_hash_update(&nat_key->src_port_n, sizeof(nat_key->src_port_n), initval);
}
/*
*===========================================================================
* ipnet_nat_cmp_hash_out_port
*===========================================================================
* Description: Compare function for objects and keys in the output port
* hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* nat_key - pointer to th search key.
* Returns: IP_TRUE if equal IP_FALSE if not.
*/
IP_STATIC Ip_bool
ipnet_nat_cmp_hash_out_port(Ipnet_nat_mapping *nat_mapping, Ipnet_nat_hash_key *nat_key)
{
return nat_mapping->inner.tuple.protocol == nat_key->protocol
&& nat_mapping->inner.tuple.dst_n == nat_key->dst_n
&& nat_mapping->inner.tuple.dst_port_n == nat_key->dst_port_n
&& nat_mapping->inner.tuple.src_n == nat_key->src_n
&& nat_mapping->inner.tuple.src_port_n == nat_key->src_port_n ? IP_TRUE : IP_FALSE;
}
/*
*===========================================================================
* ipnet_nat_obj_hash_out_noport
*===========================================================================
* Description: Hash value compute function for the object in the output
* no-port hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_obj_hash_out_noport(Ipnet_nat_mapping *nat_mapping)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_mapping->inner.tuple.protocol, sizeof(nat_mapping->inner.tuple.protocol), 0);
initval = ipcom_hash_update(&nat_mapping->inner.tuple.dst_n, sizeof(nat_mapping->inner.tuple.dst_n), initval);
return ipcom_hash_update(&nat_mapping->inner.tuple.src_n, sizeof(nat_mapping->inner.tuple.src_n), initval);
}
/*
*===========================================================================
* ipnet_nat_key_hash_out_noport
*===========================================================================
* Description: Hash value compute function for the search key in the output
* no-port hash table.
* Parameters: nat_key - pointer to the search key.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_key_hash_out_noport(Ipnet_nat_hash_key *nat_key)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_key->protocol, sizeof(nat_key->protocol), 0);
initval = ipcom_hash_update(&nat_key->dst_n, sizeof(nat_key->dst_n), initval);
return ipcom_hash_update(&nat_key->src_n, sizeof(nat_key->src_n), initval);
}
/*
*===========================================================================
* ipnet_nat_cmp_hash_out_noport
*===========================================================================
* Description: Compare function for objects and keys in the output no-port
* hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* nat_key - pointer to th search key.
* Returns: IP_TRUE if equal IP_FALSE if not.
*/
IP_STATIC Ip_bool
ipnet_nat_cmp_hash_out_noport(Ipnet_nat_mapping *nat_mapping, Ipnet_nat_hash_key *nat_key)
{
return nat_mapping->inner.tuple.protocol == nat_key->protocol
&& nat_mapping->inner.tuple.dst_n == nat_key->dst_n
&& nat_mapping->inner.tuple.src_n == nat_key->src_n ? IP_TRUE : IP_FALSE;
}
/*
*===========================================================================
* ipnet_nat_obj_hash_in_port
*===========================================================================
* Description: Hash value compute function for the object in the input port
* hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_obj_hash_in_port(Ipnet_nat_mapping *nat_mapping)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_mapping->inner.tuple.protocol, sizeof(nat_mapping->inner.tuple.protocol), 0);
initval = ipcom_hash_update(&nat_mapping->inner.dstmap_addr, sizeof(nat_mapping->inner.dstmap_addr), initval);
initval = ipcom_hash_update(&nat_mapping->inner.tuple.dst_port_n, sizeof(nat_mapping->inner.tuple.dst_port_n), initval);
initval = ipcom_hash_update(&nat_mapping->inner.nat_addr_n, sizeof(nat_mapping->inner.nat_addr_n), initval);
return ipcom_hash_update(&nat_mapping->inner.nat_port_n, sizeof(nat_mapping->inner.nat_port_n), initval);
}
/*
*===========================================================================
* ipnet_nat_key_hash_in_port
*===========================================================================
* Description: Hash value compute function for the search key in the input
* port hash table.
* Parameters: nat_key - pointer to the search key.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_key_hash_in_port(Ipnet_nat_hash_key *nat_key)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_key->protocol, sizeof(nat_key->protocol), 0);
initval = ipcom_hash_update(&nat_key->dst_n, sizeof(nat_key->dst_n), initval);
initval = ipcom_hash_update(&nat_key->dst_port_n, sizeof(nat_key->dst_port_n), initval);
initval = ipcom_hash_update(&nat_key->nat_addr_n, sizeof(nat_key->nat_addr_n), initval);
return ipcom_hash_update(&nat_key->nat_port_n, sizeof(nat_key->nat_port_n), initval);
}
/*
*===========================================================================
* ipnet_nat_cmp_hash_in_port
*===========================================================================
* Description: Compare function for objects and keys in the input port
* hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* nat_key - pointer to th search key.
* Returns: IP_TRUE if equal IP_FALSE if not.
*/
IP_STATIC Ip_bool
ipnet_nat_cmp_hash_in_port(Ipnet_nat_mapping *nat_mapping, Ipnet_nat_hash_key *nat_key)
{
return nat_mapping->inner.tuple.protocol == nat_key->protocol
&& nat_mapping->inner.dstmap_addr == nat_key->dst_n
&& nat_mapping->inner.tuple.dst_port_n == nat_key->dst_port_n
&& nat_mapping->inner.nat_addr_n == nat_key->nat_addr_n
&& nat_mapping->inner.nat_port_n == nat_key->nat_port_n ? IP_TRUE : IP_FALSE;
}
/*
*===========================================================================
* ipnet_nat_obj_hash_in_noport
*===========================================================================
* Description: Hash value compute function for the object in the input
* no-port hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_obj_hash_in_noport(Ipnet_nat_mapping *nat_mapping)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_mapping->inner.tuple.protocol, sizeof(nat_mapping->inner.tuple.protocol), 0);
initval = ipcom_hash_update(&nat_mapping->inner.dstmap_addr, sizeof(nat_mapping->inner.dstmap_addr), initval);
return ipcom_hash_update(&nat_mapping->inner.nat_addr_n, sizeof(nat_mapping->inner.nat_addr_n), initval);
}
/*
*===========================================================================
* ipnet_nat_key_hash_in_noport
*===========================================================================
* Description: Hash value compute function for the search key in the input
* no-port hash table.
* Parameters: nat_key - pointer to the search key.
* Returns: The hash value.
*/
IP_STATIC Ip_u32
ipnet_nat_key_hash_in_noport(Ipnet_nat_hash_key *nat_key)
{
Ip_u32 initval;
initval = ipcom_hash_update(&nat_key->protocol, sizeof(nat_key->protocol), 0);
initval = ipcom_hash_update(&nat_key->dst_n, sizeof(nat_key->dst_n), initval);
return ipcom_hash_update(&nat_key->nat_addr_n, sizeof(nat_key->nat_addr_n), initval);
}
/*
*===========================================================================
* ipnet_nat_cmp_hash_in_noport
*===========================================================================
* Description: Compare function for objects and keys in the input no-port
* hash table.
* Parameters: nat_mapping - pointer to the NAT mapping.
* nat_key - pointer to th search key.
* Returns: IP_TRUE if equal IP_FALSE if not.
*/
IP_STATIC Ip_bool
ipnet_nat_cmp_hash_in_noport(Ipnet_nat_mapping *nat_mapping, Ipnet_nat_hash_key *nat_key)
{
return nat_mapping->inner.tuple.protocol == nat_key->protocol
&& nat_mapping->inner.tuple.dst_n == nat_key->dst_n
&& nat_mapping->inner.nat_addr_n == nat_key->nat_addr_n ? IP_TRUE : IP_FALSE;
}
/*
*===========================================================================
* ipnet_nat_icmp_checksum
*===========================================================================
* Description: Updates ICMP checksum.
* Parameters: pkt - pointer to the packet.
* icmp_old - pointer to the old ICMP header.
* mod - IP_TRUE if complete checksum calculation necessary.
* IP_FALSE if smart update possible.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_icmp_checksum(Ipcom_pkt *pkt, Ip_u16 orig_icmp_id, Ip_bool mod)
{
Ipnet_pkt_icmp *icmp = ipcom_pkt_get_data(pkt, 0);
if (mod == IP_TRUE)
{
/* Complete checksum calculation required */
icmp->cksum = 0;
icmp->cksum = ipcom_in_checksum_pkt(pkt, 0);
}
else
{
if (IPNET_NAT_IS_ICMP_REQUEST(icmp->type) || IPNET_NAT_IS_ICMP_REPLY(icmp->type))
{
/* Smart update */
if(icmp->data.echo.id != orig_icmp_id)
{
ipnet_nat_checksum_update((unsigned char *)&icmp->cksum,
(unsigned char *)&orig_icmp_id,
sizeof(orig_icmp_id),
(unsigned char *)&icmp->data.echo.id,
sizeof(icmp->data.echo.id));
}
}
}
}
/*
*===========================================================================
* ipnet_nat_udp_checksum
*===========================================================================
* Description: Updates UDP checksum.
* Parameters: pkt - pointer to the packet.
* old_src_n - old source address.
* old_dst_n - old destination address.
* udp_old - pointer to the old UDP header.
* mod - IP_TRUE if complete checksum calculation necessary.
* IP_FALSE if smart update possible.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_udp_checksum(Ipcom_pkt *pkt, Ip_u32 old_src_n, Ip_u32 old_dst_n, Ipcom_udp_header *udp_old, Ip_bool mod)
{
Ipcom_ip_header *iph;
Ipcom_udp_header *udp;
Ipnet_nat_pseudo_hdr pseudo_hdr;
Ip_u16 hlen;
if(udp_old->checksum != 0)
{
iph = (Ipcom_ip_header *)&pkt->data[pkt->ipstart];
udp = (Ipcom_udp_header *)&pkt->data[pkt->start];
if(mod == IP_TRUE)
{
/* Complete checksum calculation required */
hlen = (Ip_u16)((iph->vers_len & 0x0f)<<2);
pseudo_hdr.src_n = IP_GET_32ON16((void *)&iph->src.addr32);
pseudo_hdr.dst_n = IP_GET_32ON16((void *)&iph->dst.addr32);
pseudo_hdr.mbz = 0;
pseudo_hdr.protocol = iph->protocol;
pseudo_hdr.length_n = (Ip_u16)(ip_htons(ip_ntohs(iph->length) - hlen));
udp->checksum = 0;
udp->checksum = (Ip_u16)(~ipcom_in_checksum(&pseudo_hdr, sizeof(pseudo_hdr)));
udp->checksum = ipcom_in_checksum_pkt(pkt, 0);
}
else
{
/* Smart update */
if(IP_GET_32ON16((void *)&iph->src.addr32) != old_src_n)
{
ipnet_nat_checksum_update((unsigned char *)&udp->checksum, (unsigned char *)&old_src_n, sizeof(old_src_n),
(unsigned char *)&iph->src.addr32, sizeof(iph->src.addr32));
}
if(IP_GET_32ON16((void *)&iph->dst.addr32) != old_dst_n)
{
ipnet_nat_checksum_update((unsigned char *)&udp->checksum, (unsigned char *)&old_dst_n, sizeof(old_dst_n),
(unsigned char *)&iph->dst.addr32, sizeof(iph->dst.addr32));
}
if(udp->src_port != udp_old->src_port)
{
ipnet_nat_checksum_update((unsigned char *)&udp->checksum, (unsigned char *)&udp_old->src_port, sizeof(udp_old->src_port),
(unsigned char *)&udp->src_port, sizeof(udp->src_port));
}
if(udp->dst_port != udp_old->dst_port)
{
ipnet_nat_checksum_update((unsigned char *)&udp->checksum, (unsigned char *)&udp_old->dst_port, sizeof(udp_old->dst_port),
(unsigned char *)&udp->dst_port, sizeof(udp->dst_port));
}
}
}
}
/*
*===========================================================================
* ipnet_nat_tcp_checksum
*===========================================================================
* Description: Updates TCP checksum.
* Parameters: pkt - pointer to the packet.
* old_src_n - old source address.
* old_dst_n - old destination address.
* udp_old - pointer to the old TCP header.
* mod - IP_TRUE if complete checksum calculation necessary.
* IP_FALSE if smart update possible.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_tcp_checksum(Ipcom_pkt *pkt, Ip_u32 old_src_n, Ip_u32 old_dst_n, Ipcom_tcp_header *tcp_old, Ip_bool mod)
{
Ipcom_ip_header *iph;
Ipcom_tcp_header *tcp;
Ipnet_nat_pseudo_hdr pseudo_hdr;
Ip_u16 hlen;
iph = (Ipcom_ip_header *)&pkt->data[pkt->ipstart];
tcp = (Ipcom_tcp_header *)&pkt->data[pkt->start];
if(mod == IP_TRUE)
{
/* Complete checksum calculation required */
hlen = (Ip_u16)((iph->vers_len & 0x0f)<<2);
pseudo_hdr.src_n = IP_GET_32ON16((void *)&iph->src.addr32);
pseudo_hdr.dst_n = IP_GET_32ON16((void *)&iph->dst.addr32);
pseudo_hdr.mbz = 0;
pseudo_hdr.protocol = iph->protocol;
pseudo_hdr.length_n = (Ip_u16)(ip_htons(ip_ntohs(iph->length) - hlen));
tcp->checksum = 0;
tcp->checksum = (Ip_u16)(~ipcom_in_checksum(&pseudo_hdr, sizeof(pseudo_hdr)));
tcp->checksum = ipcom_in_checksum_pkt(pkt, 0);
}
else
{
/* Smart update */
if(IP_GET_32ON16((void *)&iph->src.addr32) != old_src_n)
{
ipnet_nat_checksum_update((unsigned char *)&tcp->checksum, (unsigned char *)&old_src_n, sizeof(old_src_n),
(unsigned char *)&iph->src.addr32, sizeof(iph->src.addr32));
}
if(IP_GET_32ON16((void *)&iph->dst.addr32) != old_dst_n)
{
ipnet_nat_checksum_update((unsigned char *)&tcp->checksum, (unsigned char *)&old_dst_n, sizeof(old_dst_n),
(unsigned char *)&iph->dst.addr32, sizeof(iph->dst.addr32));
}
if(tcp->src_port != tcp_old->src_port)
{
ipnet_nat_checksum_update((unsigned char *)&tcp->checksum, (unsigned char *)&tcp_old->src_port, sizeof(tcp_old->src_port),
(unsigned char *)&tcp->src_port, sizeof(tcp->src_port));
}
if(tcp->dst_port != tcp_old->dst_port)
{
ipnet_nat_checksum_update((unsigned char *)&tcp->checksum, (unsigned char *)&tcp_old->dst_port, sizeof(tcp_old->dst_port),
(unsigned char *)&tcp->dst_port, sizeof(tcp->dst_port));
}
if(IP_GET_32ON16((void *)&tcp->seq_num) != IP_GET_32ON16((void *)&tcp_old->seq_num))
{
ipnet_nat_checksum_update((unsigned char *)&tcp->checksum, (unsigned char *)&tcp_old->seq_num, sizeof(tcp_old->seq_num),
(unsigned char *)&tcp->seq_num, sizeof(tcp->seq_num));
}
if(IP_GET_32ON16((void *)&tcp->ack_num) != IP_GET_32ON16((void *)&tcp_old->ack_num))
{
ipnet_nat_checksum_update((unsigned char *)&tcp->checksum, (unsigned char *)&tcp_old->ack_num, sizeof(tcp_old->ack_num),
(unsigned char *)&tcp->ack_num, sizeof(tcp->ack_num));
}
}
}
/*
*===========================================================================
* ipnet_nat_translate_embedded_icmp
*===========================================================================
* Description: Translate an embedded icmp error message.
* Parameters: incoming - IP_TRUE for incoming packets.
* pkt - pointer to the packet.
* nat_mapping - pointer to the NAT mapping.
* Returns: 1 = Translated.
* 0 = No translation.
*/
IP_STATIC Ip_s32
ipnet_nat_translate_embedded_icmp(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping)
{
Ipnet_pkt_icmp *icmp;
Ipnet_pkt_icmp *icmp_embed;
Ipcom_ip_header *iph_embed;
Ipcom_udp_header *udp_embed;
Ipcom_tcp_header *tcp_embed;
Ip_u16 hlen_embed, old_port_n, new_port_n;
Ip_u32 old_n, new_n;
icmp = (Ipnet_pkt_icmp *)&pkt->data[pkt->start];
iph_embed = (Ipcom_ip_header *)icmp->data.failing_pkt.ip;
hlen_embed = (Ip_u16)((iph_embed->vers_len & 0x0f)<<2);
if (IPNET_NAT_IS_ICMP_ERROR(icmp->type) == IP_FALSE || icmp->type == IPCOM_ICMP_REDIRECT)
{
/* Skip re-direct mesage type */
return 0;
}
/* Restore the embedded ip header */
if(incoming == IP_TRUE)
{
old_n = IP_GET_32ON16((void *)&iph_embed->src.addr32);
new_n = nat_mapping->inner.tuple.src_n;
ipcom_memcpy(&iph_embed->src, &nat_mapping->inner.tuple.src_n, sizeof(iph_embed->src));
}
else
{
old_n = IP_GET_32ON16((void *)&iph_embed->dst.addr32);
new_n = nat_mapping->inner.nat_addr_n;
ipcom_memcpy(&iph_embed->dst, &nat_mapping->inner.nat_addr_n, sizeof(iph_embed->dst));
}
/* Recalculate embedded ip header checksum */
iph_embed->checksum = 0;
iph_embed->checksum = ipcom_in_checksum(iph_embed, hlen_embed);
/* Restore the embedded port */
if(iph_embed->protocol == IP_IPPROTO_UDP)
{
udp_embed = (Ipcom_udp_header *)((Ip_u8 *)iph_embed + hlen_embed);
if(udp_embed->checksum != 0 && new_n != old_n)
{
ipnet_nat_checksum_update((unsigned char *)&udp_embed->checksum,
(unsigned char *)&old_n, sizeof(old_n),
(unsigned char *)&new_n, sizeof(new_n));
}
if(incoming == IP_TRUE)
{
old_port_n = udp_embed->src_port;
new_port_n = nat_mapping->inner.tuple.src_port_n;
udp_embed->src_port = nat_mapping->inner.tuple.src_port_n;
}
else
{
old_port_n = udp_embed->dst_port;
new_port_n = nat_mapping->inner.nat_port_n;
udp_embed->dst_port = nat_mapping->inner.nat_port_n;
}
if(udp_embed->checksum != 0 && new_port_n != old_port_n)
{
ipnet_nat_checksum_update((unsigned char *)&udp_embed->checksum,
(unsigned char *)&old_port_n, sizeof(old_port_n),
(unsigned char *)&new_port_n, sizeof(new_port_n));
}
}
else if(iph_embed->protocol == IP_IPPROTO_TCP)
{
tcp_embed = (Ipcom_tcp_header *)((Ip_u8 *)iph_embed + hlen_embed);
/* Check if packet contains enough data to update TCP checksum */
if (pkt->end - pkt->start >= 8 + hlen_embed + IPCOM_TCP_HEADER_LENGTH)
{
if(new_n != old_n)
{
ipnet_nat_checksum_update((unsigned char *)&tcp_embed->checksum,
(unsigned char *)&old_n, sizeof(old_n),
(unsigned char *)&new_n, sizeof(new_n));
}
}
if(incoming == IP_TRUE)
{
old_port_n = tcp_embed->src_port;
new_port_n = nat_mapping->inner.tuple.src_port_n;
tcp_embed->src_port = nat_mapping->inner.tuple.src_port_n;
}
else
{
old_port_n = tcp_embed->dst_port;
new_port_n = nat_mapping->inner.nat_port_n;
tcp_embed->dst_port = nat_mapping->inner.nat_port_n;
}
/* Check if packet contains enough data to update TCP checksum */
if (pkt->end - pkt->start >= 8 + hlen_embed + IPCOM_TCP_HEADER_LENGTH)
{
if(new_port_n != old_port_n)
{
ipnet_nat_checksum_update((unsigned char *)&tcp_embed->checksum,
(unsigned char *)&old_port_n, sizeof(old_port_n),
(unsigned char *)&new_port_n, sizeof(new_port_n));
}
}
}
else if(iph_embed->protocol == IP_IPPROTO_ICMP)
{
icmp_embed = (Ipnet_pkt_icmp *)((Ip_u8 *)iph_embed + hlen_embed);
if(IPNET_NAT_IS_ICMP_REQUEST(icmp_embed->type) || IPNET_NAT_IS_ICMP_REPLY(icmp_embed->type))
{
if(incoming == IP_TRUE)
{
old_port_n = icmp_embed->data.echo.id;
new_port_n = nat_mapping->inner.tuple.icmp_id_n;
icmp_embed->data.echo.id = nat_mapping->inner.tuple.icmp_id_n;
}
else
{
old_port_n = icmp_embed->data.echo.id;
new_port_n = nat_mapping->inner.nat_port_n;
icmp_embed->data.echo.id = nat_mapping->inner.nat_port_n;
}
if(new_port_n != old_port_n)
{
ipnet_nat_checksum_update((unsigned char *)&icmp_embed->cksum,
(unsigned char *)&old_port_n, sizeof(old_port_n),
(unsigned char *)&new_port_n, sizeof(new_port_n));
}
}
}
return 1;
}
/*
*===========================================================================
* ipnet_nat_translate_address
*===========================================================================
* Description: Translate an IPv4 address.
* Parameters: incoming - IP_TRUE for incoming packets.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_translate_address(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping)
{
Ipcom_ip_header *iph;
iph = (Ipcom_ip_header *)&pkt->data[pkt->ipstart];
/* Translate the source/destination address */
if(incoming == IP_FALSE)
ipcom_memcpy(&iph->src, &nat_mapping->inner.nat_addr_n, sizeof(iph->src));
else
ipcom_memcpy(&iph->dst, &nat_mapping->inner.tuple.src_n, sizeof(iph->dst));
}
/*
*===========================================================================
* ipnet_nat_get_configuration
*===========================================================================
* Description: Get NAT configuration from sysvars.
* Parameters: none.
* Returns: IPCOM_SUCCESS
* IPCOM_ERR_FAILED
*/
IP_STATIC Ip_err
ipnet_nat_get_configuration(void)
{
/* Get NAT configuration */
ipnet->nat.inner.timeout_icmp = ipcom_sysvar_get_as_int0("ipnet.nat.timeout.icmp", 5);
ipnet->nat.inner.timeout_udp = ipcom_sysvar_get_as_int0("ipnet.nat.timeout.udp", 60);
ipnet->nat.inner.timeout_tcp = ipcom_sysvar_get_as_int0("ipnet.nat.timeout.tcp", 432000);
ipnet->nat.inner.timeout_other = ipcom_sysvar_get_as_int0("ipnet.nat.timeout.other", 60);
ipnet->nat.inner.max_mappings = ipcom_sysvar_get_as_int0("ipnet.nat.capacity.mappings", 1000);
ipnet->nat.inner.port_lo = ipcom_sysvar_get_as_int0("ipnet.nat.autoport.start", 29000);
ipnet->nat.inner.port_hi = ipcom_sysvar_get_as_int0("ipnet.nat.autoport.end", 29999);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipnet_nat_remove_mappings
*===========================================================================
* Description: Remove all mappings belonging to a rule or all mappings
* globally if the nat_rule parameter is zero.
* Parameters: nat_rule - pointer to the rule.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_remove_mappings(Ipnet_nat_rule *nat_rule)
{
Ipnet_nat_mapping *nat_mapping;
int i = 0;
while (IP_NULL != (nat_mapping = ipnet_nat_mapping_get(i)))
{
if (nat_rule == IP_NULL || nat_rule == nat_mapping->rule)
ipnet_nat_clean_mapping(nat_mapping);
else
++i;
}
}
/*
*===========================================================================
* ipnet_nat_internal_add_rule
*===========================================================================
* Description: Add a rule.
* Parameters: nat_rule - pointer to the rule.
* rule_index - the index of the rule in the list of rules.
* Returns: 0 = success
* -1 = fail
*/
IP_STATIC Ip_s32
ipnet_nat_internal_add_rule(struct Ipnet_nat_rule_inner *nat_rule_info, Ip_u32 rule_index)
{
Ipnet_nat_rule *nat_rule_tmp, *nat_rule_new = IP_NULL;
Ipnet_nat_proxy *nat_proxy;
Ip_u32 i;
Ip_s32 num_longs, ret = 0;
if(ipnet->nat.head_rule.size >= IPNET_NAT_MAX_RULES)
{
ret = -1;
goto exit;
}
/* Allocate space for the rule */
nat_rule_new = ipcom_malloc(sizeof(*nat_rule_new));
if(nat_rule_new == IP_NULL)
{
ret = -1;
goto exit;
}
ipcom_memset(nat_rule_new, 0, sizeof(*nat_rule_new));
ipcom_memcpy(&nat_rule_new->inner, nat_rule_info, sizeof(nat_rule_new->inner));
/* Allocate space for portmap and icmpidmap nat ids */
if(nat_rule_info->portmap == 1 || nat_rule_info->icmpidmap == 1)
{
num_longs = ((nat_rule_new->inner.port_hi - nat_rule_new->inner.port_lo)>>5) + 1;
nat_rule_new->ports = ipcom_malloc(num_longs<<2);
if(nat_rule_new->ports == IP_NULL)
{
ret = -1;
goto exit;
}
ipcom_memset(nat_rule_new->ports, 0, num_longs<<2);
nat_rule_new->last_port = nat_rule_new->inner.port_hi;
}
/* Find the proxy */
if(nat_rule_new->inner.proxy == 1)
{
nat_proxy = IPCOM_LIST_FIRST(&ipnet->nat.head_proxy);
while(nat_proxy != IP_NULL)
{
if(ipcom_strcmp(nat_proxy->inner.label, nat_rule_new->inner.proxy_label) == 0)
{
/* Check proxies on rdr rules */
if(nat_rule_new->inner.rdr_tcpudp == 1 &&
(nat_proxy->inner.protocol == IP_IPPROTO_TCP ||
nat_proxy->inner.protocol == IP_IPPROTO_UDP))
{
nat_rule_new->proxy_func = nat_proxy->func;
nat_rule_new->proxy_cookie = nat_proxy->cookie;
break;
}
/* Next check covers also proxies on "map" and "map-block" rules
since the rdr_tcpudp field is zero */
else if(nat_rule_new->inner.rdr_tcpudp == 0 &&
nat_rule_new->inner.proxy_protocol == nat_proxy->inner.protocol)
{
nat_rule_new->proxy_func = nat_proxy->func;
nat_rule_new->proxy_cookie = nat_proxy->cookie;
break;
}
}
nat_proxy = IPCOM_LIST_NEXT(&nat_proxy->list_proxy);
}
if(nat_proxy == IP_NULL)
{
/* No matching proxy found */
ret = -1;
goto exit;
}
}
nat_rule_tmp = IPCOM_LIST_FIRST(&ipnet->nat.head_rule);
/* Insert last if no index given or empty list */
if(rule_index == 0 || nat_rule_tmp == IP_NULL)
{
ipcom_list_insert_last(&ipnet->nat.head_rule, &nat_rule_new->list_rule);
goto exit;
}
/* Walk through the list and insert in the right spot */
i = 1;
while(nat_rule_tmp != IP_NULL)
{
if(rule_index <= i++)
{
ipcom_list_insert_before(&nat_rule_tmp->list_rule, &nat_rule_new->list_rule);
goto exit;
}
nat_rule_tmp = IPCOM_LIST_NEXT(&nat_rule_tmp->list_rule);
}
/* Insert at end */
ipcom_list_insert_last(&ipnet->nat.head_rule, &nat_rule_new->list_rule);
exit:
if(ret != 0 && nat_rule_new != IP_NULL)
{
if(nat_rule_new->ports != IP_NULL)
ipcom_free(nat_rule_new->ports);
ipcom_free(nat_rule_new);
}
return ret;
}
/*
*===========================================================================
* ipnet_nat_internal_remove_rule
*===========================================================================
* Description: Remove a rule.
* Parameters: nat_rule - pointer to the rule.
* Returns: 0 = success
* -1 = fail
*/
IP_STATIC Ip_s32
ipnet_nat_internal_remove_rule(struct Ipnet_nat_rule_inner *nat_rule_info)
{
Ipnet_nat_rule *nat_rule_tmp;
Ip_u8 auto_port_set = 0;
nat_rule_tmp = IPCOM_LIST_FIRST(&ipnet->nat.head_rule);
while(nat_rule_tmp != IP_NULL)
{
/* Reset auto_port variable before check */
if (nat_rule_tmp->inner.auto_port == 1) {
nat_rule_tmp->inner.auto_port = 0;
auto_port_set = 1;
}
/* Compare only actual rule paramters */
if(!ipcom_memcmp(nat_rule_info, &nat_rule_tmp->inner, sizeof(nat_rule_tmp->inner)))
{
ipcom_list_remove(&nat_rule_tmp->list_rule);
ipnet_nat_remove_mappings(nat_rule_tmp);
if(nat_rule_tmp->ports != IP_NULL)
ipcom_free(nat_rule_tmp->ports);
ipcom_free(nat_rule_tmp);
return 0;
}
/* Restore auto_port variable */
nat_rule_tmp->inner.auto_port = auto_port_set;
auto_port_set = 0;
nat_rule_tmp = IPCOM_LIST_NEXT(&nat_rule_tmp->list_rule);
}
return -1;
}
/*
*===========================================================================
* ipnet_nat_internal_flush_rules
*===========================================================================
* Description: Remove all rules.
* Parameters: none.
* Returns: 0 = success
* -1 = fail
*/
IP_STATIC Ip_s32
ipnet_nat_internal_flush_rules(void)
{
Ipnet_nat_rule *nat_rule;
Ip_s32 ret = 0;
while((nat_rule = IPCOM_LIST_FIRST(&ipnet->nat.head_rule)) != IP_NULL)
{
ret = ipnet_nat_internal_remove_rule(&nat_rule->inner);
if(ret != 0)
{
IP_PANIC();
break;
}
}
return ret;
}
/*
*===========================================================================
* ipnet_nat_internal_get_rule
*===========================================================================
* Description: Get a rule from the list of rules.
* Parameters: ctrl - pointer to the IP_IP_NAT socket option structure.
* lenp - pointer to the length of the IP_IP_NAT structure.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_get_rule(Ipnet_nat_ctrl *ctrl, Ip_socklen_t *lenp)
{
Ipnet_nat_rule *nat_rule;
Ip_s32 i;
/* Get the specified entry */
nat_rule = IPCOM_LIST_FIRST(&ipnet->nat.head_rule);
for(i=0; i<ctrl->seqno && nat_rule != IP_NULL; i++)
nat_rule = IPCOM_LIST_NEXT(&nat_rule->list_rule);
/* Check if the entry existed */
if(nat_rule == IP_NULL)
{
*lenp = 0;
return 0;
}
ipcom_memcpy(&ctrl->type.rule_info, &nat_rule->inner, sizeof(nat_rule->inner));
*lenp = sizeof(*ctrl);
return 0;
}
/*
*===========================================================================
* ipnet_nat_internal_get_mapping
*===========================================================================
* Description: Get a mapping from the list of mappings.
* Parameters: ctrl - pointer to the IP_IP_NAT socket option structure.
* lenp - pointer to the length of the IP_IP_NAT structure.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_get_mapping(Ipnet_nat_ctrl *ctrl, Ip_socklen_t *lenp)
{
Ipnet_nat_mapping *nat_mapping;
/* Get the specified entry (index starts at zero) */
nat_mapping = ipnet_nat_mapping_get(ctrl->seqno);
/* Check if the entry existed */
if (nat_mapping == IP_NULL)
{
*lenp = 0;
return 0;
}
if(nat_mapping->rule->inner.pt == 1 || nat_mapping->rule->inner.pt_block == 1)
nat_mapping->inner.pt = 1;
else
nat_mapping->inner.pt = 0;
ipcom_memcpy(&ctrl->type.mapping_info, &nat_mapping->inner, sizeof(nat_mapping->inner));
*lenp = sizeof(*ctrl);
return 0;
}
/*
*===========================================================================
* ipnet_nat_internal_get_proxy
*===========================================================================
* Description: Get a proxy from the list of proxies.
* Parameters: ctrl - pointer to the IP_IP_NAT socket option structure.
* lenp - pointer to the length of the IP_IP_NAT structure.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_get_proxy(Ipnet_nat_ctrl *ctrl, Ip_socklen_t *lenp)
{
Ipnet_nat_proxy *nat_proxy;
Ip_s32 i;
/* Get the specified entry */
nat_proxy = IPCOM_LIST_FIRST(&ipnet->nat.head_proxy);
for(i=0; i<ctrl->seqno && nat_proxy != IP_NULL; i++)
nat_proxy = IPCOM_LIST_NEXT(&nat_proxy->list_proxy);
/* Check if the entry existed */
if(nat_proxy == IP_NULL)
{
*lenp = 0;
return 0;
}
ipcom_memcpy(&ctrl->type.proxy_info, &nat_proxy->inner, sizeof(nat_proxy->inner));
*lenp = sizeof(*ctrl);
return 0;
}
/*
*===========================================================================
* ipnet_nat_internal_get_info
*===========================================================================
* Description: Get NAT info.
* Parameters: ctrl - pointer to the IP_IP_NAT socket option structure.
* lenp - pointer to the length of the IP_IP_NAT structure.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_get_info(Ipnet_nat_ctrl *ctrl, Ip_socklen_t *lenp)
{
ipnet->nat.inner.second = ipnet_sec_now();
ipcom_memcpy(&ctrl->type.info, &ipnet->nat.inner, sizeof(ipnet->nat.inner));
*lenp = sizeof(*ctrl);
return 0;
}
/*
*===========================================================================
* ipnet_nat_flush_mappings
*===========================================================================
* Description: Remove all mappings.
* Parameters: none.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_flush_mappings(void)
{
/* Remove all mappings */
ipnet_nat_remove_mappings(IP_NULL);
return 0;
}
/*
*===========================================================================
* ipnet_nat_internal_clear_stats
*===========================================================================
* Description: Clear NAT statistics.
* Parameters: none.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_clear_stats(void)
{
ipcom_memset(&ipnet->nat.inner.stats, 0, sizeof(ipnet->nat.inner.stats));
return 0;
}
/*
*===========================================================================
* ipnet_nat_internal_enable
*===========================================================================
* Description: Enable NAT.
* Parameters: none.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_enable(void)
{
int num_longs;
if(ipnet->nat.inner.open == 1)
return -1; /* Already enabled */
if(ipnet_nat_get_configuration() != IPCOM_SUCCESS)
return -1; /* Invalid configuration */
/* Allocate space for the auto port array */
if (ipnet->nat.ports != IP_NULL)
ipcom_free(ipnet->nat.ports); /* Free previous array if allocated */
num_longs = ((ipnet->nat.inner.port_hi - ipnet->nat.inner.port_lo)>>5) + 1;
ipnet->nat.ports = ipcom_malloc(num_longs<<2);
if(ipnet->nat.ports == IP_NULL)
return -1; /* Not enough memory */
ipcom_memset(ipnet->nat.ports, 0, num_longs<<2);
ipnet->nat.last_port = ipnet->nat.inner.port_hi;
ipnet->nat.inner.open = 1;
return 0;
}
/*
*===========================================================================
* ipnet_nat_internal_disable
*===========================================================================
* Description: Disable NAT.
* Parameters: none.
* Returns: 0 = success.
* -1 = fail.
*/
IP_STATIC Ip_s32
ipnet_nat_internal_disable(void)
{
if(ipnet->nat.inner.open == 0)
return -1; /* Already disabled */
(void)ipnet_nat_internal_flush_mappings();
ipnet->nat.inner.open = 0;
return 0;
}
/*
*===========================================================================
* ipnet_nat_assign_mapping
*===========================================================================
* Description: Assign new address and port to a mapping
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_mapping - pointer to the NAT mapping.
* nat_tuple - pointer to the packet tuple.
* port - pointer to boolean that indicates if mapping shall
* be added to the port hash tables.
* Returns: 0 = ok.
* -1 = fail.
*/
IP_STATIC int
ipnet_nat_assign_mapping(Ip_bool incoming, Ipnet_nat_mapping *nat_mapping, Ipnet_nat_tuple *nat_tuple, Ip_bool *port)
{
Ip_s32 nat_id;
Ipnet_netif *netif;
*port = IP_FALSE;
if(incoming == IP_FALSE)
{
/* Outgoing - The rule must be "map", "map-block", "pt" or "pt-block" */
ip_assert(nat_mapping->rule->inner.map == 1 || nat_mapping->rule->inner.map_block == 1
|| nat_mapping->rule->inner.pt == 1 || nat_mapping->rule->inner.pt_block == 1);
/* Assign the NAT address */
if(nat_mapping->rule->inner.nat_addr_n == 0 && nat_mapping->rule->inner.nat_mask_n == 0xffffffff)
{
/* 0/32 -> Get the address from the interface's address */
ip_assert(nat_mapping->rule->inner.map == 1 || nat_mapping->rule->inner.map_block == 1);
netif = ipnet_if_nametonetif(ipnet_get_effective_vr(), nat_mapping->rule->inner.ifname);
if (netif == IP_NULL || netif->inet4_addr_list == IP_NULL)
{
IP_PANIC();
return -1;
}
nat_mapping->inner.nat_addr_n = netif->inet4_addr_list->ipaddr_n;
}
else if(nat_mapping->rule->inner.nat_addr_n == 0 && nat_mapping->rule->inner.nat_mask_n == 0)
{
/* 0/0 -> Skip translating */
nat_mapping->inner.nat_addr_n = nat_tuple->src_n;
}
else if(nat_mapping->rule->inner.map == 1)
{
/* "map" -> Use specified address */
nat_mapping->inner.nat_addr_n = nat_mapping->rule->inner.nat_addr_n;
}
else if(nat_mapping->rule->inner.pt == 1)
{
/* "pt" -> Use specified address */
nat_mapping->inner.nat_addr_n = nat_mapping->rule->inner.nat_addr_n;
}
else if(nat_mapping->rule->inner.pt_block == 1)
{
#ifdef IPNET_USE_NAT_PT
/* "pt-block" -> Static translation. Calculate an address */
nat_mapping->inner.nat_addr_n = nat_mapping->rule->inner.nat_addr_n +
(nat_tuple->src_n - nat_mapping->rule->inner.src_n_pt.in6.addr32[3]);
#endif
}
else if(nat_mapping->rule->inner.map_block == 1)
{
/* "map-block" -> Static translation. Calculate an address */
nat_mapping->inner.nat_addr_n = nat_mapping->rule->inner.nat_addr_n +
(nat_tuple->src_n - nat_mapping->rule->inner.src_n);
}
if(nat_mapping->rule->inner.dstmap == 1)
{
/* "dstmap, store new destination address" */
nat_mapping->inner.dstmap_addr = nat_mapping->rule->inner.dstmap_addr;
}
else
nat_mapping->inner.dstmap_addr = nat_tuple->dst_n;
if(nat_tuple->protocol == IP_IPPROTO_UDP || nat_tuple->protocol == IP_IPPROTO_TCP)
{
/* Assign the NAT port */
if(nat_mapping->rule->inner.portmap == 1)
{
if((nat_id = ipnet_nat_get_id(nat_mapping->rule->inner.port_hi,
nat_mapping->rule->inner.port_lo,
nat_mapping->rule->ports,
&nat_mapping->rule->last_port,
ip_htons((Ip_u16)nat_tuple->src_port_n))) < 0)
{
IPCOM_LOG0(ERR, "ipnet_nat_assign_mapping() :: Could not find available nat id");
return -1;
}
nat_mapping->inner.nat_port_n = ip_htons((Ip_u16)nat_id);
}
else if(nat_mapping->rule->inner.proxy == 1)
{
if (nat_mapping->rule->inner.proxy_nonapt == 1)
{
nat_mapping->inner.nat_port_n = nat_tuple->src_port_n;
}
else
{
if((nat_id = ipnet_nat_get_id(ipnet->nat.inner.port_hi,
ipnet->nat.inner.port_lo,
ipnet->nat.ports,
&ipnet->nat.last_port,
ip_htons((Ip_u16)nat_tuple->src_port_n))) < 0)
{
IPCOM_LOG0(ERR, "ipnet_nat_assign_mapping() :: Could not find available automatic nat id");
return -1;
}
nat_mapping->inner.nat_port_n = ip_htons((Ip_u16)nat_id);
nat_mapping->inner.autoport = 1;
}
}
else
{
nat_mapping->inner.nat_port_n = nat_tuple->src_port_n;
}
*port = IP_TRUE;
}
else if(nat_tuple->protocol == IP_IPPROTO_ICMP &&
(IPNET_NAT_IS_ICMP_REQUEST(nat_tuple->icmp_type) ||
IPNET_NAT_IS_ICMP_REPLY(nat_tuple->icmp_type)))
{
/* Assign the NAT port */
if(nat_mapping->rule->inner.icmpidmap == 1)
{
if((nat_id = ipnet_nat_get_id(nat_mapping->rule->inner.port_hi,
nat_mapping->rule->inner.port_lo,
nat_mapping->rule->ports,
&nat_mapping->rule->last_port,
ip_htons((Ip_u16)nat_tuple->src_port_n))) < 0)
{
IPCOM_LOG0(ERR, "ipnet_nat_assign_mapping() :: Could not find available nat id");
return -1;
}
nat_mapping->inner.nat_port_n = ip_htons((Ip_u16)nat_id);
}
else
{
nat_mapping->inner.nat_port_n = nat_tuple->icmp_id_n;
}
/* Use icmp id as source port in hash tables */
nat_mapping->inner.tuple.dst_port_n = 0;
nat_mapping->inner.tuple.src_port_n = nat_tuple->icmp_id_n;
*port = IP_TRUE;
}
}
else
{
/* Incoming - The rule must be "rdr" */
ip_assert(nat_mapping->rule->inner.rdr == 1);
/* Reverse the addresses */
nat_mapping->inner.tuple.dst_n = nat_tuple->src_n;
nat_mapping->inner.dstmap_addr = nat_tuple->src_n;
if(nat_mapping->rule->inner.rdr == 1)
nat_mapping->inner.tuple.src_n = nat_mapping->rule->inner.nat_addr_n;
/* Assign the NAT address */
nat_mapping->inner.nat_addr_n = nat_tuple->dst_n;
if(nat_tuple->protocol == IP_IPPROTO_UDP || nat_tuple->protocol == IP_IPPROTO_TCP)
{
/* Reverse the ports */
nat_mapping->inner.tuple.dst_port_n = nat_tuple->src_port_n;
if (nat_mapping->rule->inner.rdr_tcpudp == 1 || nat_mapping->rule->inner.rdr_protocol != 0)
nat_mapping->inner.tuple.src_port_n = nat_mapping->rule->inner.rdr_new_port_n;
else
nat_mapping->inner.tuple.src_port_n = nat_tuple->dst_port_n;
/* Assign the NAT port */
nat_mapping->inner.nat_port_n = nat_tuple->dst_port_n;
*port = IP_TRUE;
}
else if(nat_tuple->protocol == IP_IPPROTO_ICMP && IPNET_NAT_IS_ICMP_REQUEST(nat_tuple->icmp_type))
{
/* Assign the NAT port */
nat_mapping->inner.nat_port_n = nat_tuple->icmp_id_n;
/* Use icmp id as source port in hash tables */
nat_mapping->inner.tuple.dst_port_n = 0;
nat_mapping->inner.tuple.src_port_n = nat_tuple->icmp_id_n;
*port = IP_TRUE;
}
}
return 0;
}
/*
*===========================================================================
* ipnet_nat_reset_mapping_timeout
*===========================================================================
* Description: Sets the timeout of a mapping.
* Parameters: mapping - Pointer to the NAT mapping.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_reset_mapping_timeout(Ipnet_nat_mapping *nat_mapping)
{
Ip_bool reset_timer;
Ip_u32 sec_now = ipnet_sec_now();
ip_assert(nat_mapping->tmo);
reset_timer = IPCOM_IS_LT(nat_mapping->inner.tmo_int_sec + sec_now, nat_mapping->inner.tmo_abs_sec);
nat_mapping->inner.tmo_abs_sec = nat_mapping->inner.tmo_int_sec + sec_now;
if (reset_timer)
{
ipnet_timeout_cancel(nat_mapping->tmo);
(void) ipnet_timeout_schedule(nat_mapping->inner.tmo_int_sec * 1000,
(Ipnet_timeout_handler) ipnet_nat_mapping_timeout,
nat_mapping,
&nat_mapping->tmo);
}
}
/*
*===========================================================================
* ipnet_nat_assign_timeout
*===========================================================================
* Description: Assign timeouts to a mapping
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_mapping - pointer to the NAT mapping.
* nat_tuple - pointer to the packet tuple.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_assign_timeout(Ip_bool incoming,
Ipnet_nat_mapping *nat_mapping,
Ipnet_nat_tuple *nat_tuple,
Ip_u32 timeout)
{
/* Assign the states and timeout */
if(nat_tuple->protocol == IP_IPPROTO_TCP)
{
nat_mapping->inner.tmo_int_sec = ipnet->nat.inner.timeout_other;
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_CLO;
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_CLO;
if (IP_BIT_ISSET(nat_tuple->tcp_flags, IPNET_NAT_TCPFLAG_SYN))
{
if(incoming == IP_TRUE)
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_SYN;
else
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_SYN;
}
}
else if(nat_tuple->protocol == IP_IPPROTO_UDP)
{
nat_mapping->inner.tmo_int_sec = ipnet->nat.inner.timeout_udp;
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_EST;
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_EST;
}
else if(nat_tuple->protocol == IP_IPPROTO_ICMP || nat_tuple->protocol == IP_IPPROTO_ICMPV6)
{
nat_mapping->inner.tmo_int_sec = ipnet->nat.inner.timeout_icmp;
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_EST;
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_EST;
}
else
{
nat_mapping->inner.tmo_int_sec = ipnet->nat.inner.timeout_other;
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_EST;
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_EST;
}
/* Set the timeout if other than default */
if (timeout)
nat_mapping->inner.tmo_int_sec = timeout;
/* Add the timeout to the mapping */
(void) ipnet_timeout_schedule(nat_mapping->inner.tmo_int_sec * 1000,
(Ipnet_timeout_handler) ipnet_nat_mapping_timeout,
nat_mapping,
&nat_mapping->tmo);
nat_mapping->inner.tmo_abs_sec = nat_mapping->inner.tmo_int_sec + ipnet_sec_now();
}
/*
*===========================================================================
* ipnet_nat_translate_icmp_id
*===========================================================================
* Description: Translate the ICMP id.
* Parameters: incoming - IP_TRUE for incoming packets.
* pkt - pointer to the packet.
* nat_mapping - pointer to the NAT mapping.
* Returns: nothing.
*/
IP_STATIC void
ipnet_nat_translate_icmp_id(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping, Ip_u16 *orig_icmp_id)
{
Ipnet_pkt_icmp *icmp;
icmp = (Ipnet_pkt_icmp *)&pkt->data[pkt->start];
if(IPNET_NAT_IS_ICMP_REQUEST(icmp->type) || IPNET_NAT_IS_ICMP_REPLY(icmp->type))
{
*orig_icmp_id = icmp->data.echo.id;
if(incoming == IP_FALSE)
icmp->data.echo.id = nat_mapping->inner.nat_port_n;
else
icmp->data.echo.id = nat_mapping->inner.tuple.icmp_id_n;
}
}
/*
*===========================================================================
* ipnet_nat_translate
*===========================================================================
* Description: Translate a packet.
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_tuple - pointer to packet tuple.
* nat_mapping - pointer to NAT mapping.
* Returns: IPNET_NAT_TRANSLATED
* IPNET_NAT_DROP
*/
IP_STATIC Ip_s32
ipnet_nat_translate(Ip_bool incoming, Ipcom_pkt **pktp, Ipnet_nat_tuple *nat_tuple, Ipnet_nat_mapping *nat_mapping)
{
Ipcom_ip_header *iph;
Ip_u16 hlen, orig_icmp_id = 0;
Ip_s32 ret, delta = 0;
Ip_u32 orig_src_n, orig_dst_n; /* Original source and destination addresses */
Ip_u8 orig_tcp_hdr[IPCOM_TCP_HEADER_LENGTH];
Ip_u8 orig_udp_hdr[IPCOM_UDP_HEADER_LENGTH];
Ip_bool mod = IP_FALSE;
iph = (Ipcom_ip_header *)&(*pktp)->data[(*pktp)->ipstart];
hlen = (Ip_u16)((iph->vers_len & 0x0f)<<2);
/* Translate the address */
orig_src_n = IP_GET_32ON16((void *)&iph->src.addr32);
orig_dst_n = IP_GET_32ON16((void *)&iph->dst.addr32);
ipnet_nat_translate_address(incoming, *pktp, nat_mapping);
/* Translate destination address */
if (nat_mapping->rule->inner.dstmap == 1)
{
if (incoming)
{
ipcom_memcpy(&iph->src, &nat_mapping->inner.tuple.dst_n, sizeof(iph->dst));
}
else
{
ipcom_memcpy(&iph->dst, &nat_mapping->inner.dstmap_addr, sizeof(iph->dst));
}
}
/* Translate the tcp/udp port */
if(nat_tuple->fragoff == 0)
{
switch(iph->protocol)
{
case IP_IPPROTO_TCP:
ipcom_memcpy(orig_tcp_hdr, &(*pktp)->data[(*pktp)->start], sizeof(orig_tcp_hdr));
ipnet_nat_translate_tcpudp_port(incoming, *pktp, nat_mapping);
break;
case IP_IPPROTO_UDP:
ipcom_memcpy(orig_udp_hdr, &(*pktp)->data[(*pktp)->start], sizeof(orig_udp_hdr));
ipnet_nat_translate_tcpudp_port(incoming, *pktp, nat_mapping);
break;
case IP_IPPROTO_ICMP:
ipnet_nat_translate_icmp_id(incoming, *pktp, nat_mapping, &orig_icmp_id);
break;
default:
break;
}
}
/* Call the application proxy */
if(nat_mapping->proxy_func != IP_NULL && nat_tuple->protocol != IP_IPPROTO_ICMP)
{
ret = ipnet_nat_proxy(incoming, pktp, nat_tuple, nat_mapping, (int)hlen, &mod, &delta);
if (ret < 0)
{
/* Drop packet */
IPCOM_LOG1(INFO, "ipnet_nat_translate() :: Proxy %s dropped packet",
nat_mapping->rule->inner.proxy_label);
if(incoming == IP_FALSE)
ipnet->nat.inner.stats.dropped_out++;
else
ipnet->nat.inner.stats.dropped_in++;
return IPNET_NAT_DROP;
}
iph = (Ipcom_ip_header *)&(*pktp)->data[(*pktp)->ipstart];
hlen = (Ip_u16)((iph->vers_len & 0x0f)<<2);
/* Fix IP length */
if(delta != 0)
{
Ip_u16 tmp;
tmp = ip_ntohs(iph->length);
tmp = (Ip_u16)((Ip_s32)tmp + delta);
iph->length = ip_htons(tmp);
}
if(iph->protocol == IP_IPPROTO_TCP)
{
/* Save, translate and update the sequence number */
if(delta != 0)
{
Ip_u32 seq_num;
seq_num = IP_GET_32ON16((((Ip_u32 *)&(*pktp)->data[(*pktp)->start]))+1);
seq_num = ip_ntohl(seq_num);
ipnet_nat_translate_sequence_number(incoming, *pktp, nat_mapping);
ipnet_nat_update_sequence_number(incoming, nat_mapping, seq_num, delta);
}
else
{
ipnet_nat_translate_sequence_number(incoming, *pktp, nat_mapping);
}
/* Translate acknowledgment number */
ipnet_nat_translate_ack_number(incoming, *pktp, nat_mapping);
}
else if(iph->protocol == IP_IPPROTO_UDP)
{
/* Fix UDP length */
if(delta != 0)
{
Ip_u16 tmp;
tmp = *(((Ip_u16 *)&(*pktp)->data[(*pktp)->start])+2);
tmp = ip_ntohs(tmp);
tmp = (Ip_u16)((Ip_s32)tmp + delta);
*(((Ip_u16 *)&(*pktp)->data[(*pktp)->start])+2) = ip_htons(tmp);
}
}
}
if(nat_tuple->fragoff == 0)
{
if(iph->protocol == IP_IPPROTO_TCP)
{
/* Recalculate tcp checksum */
ipnet_nat_tcp_checksum(*pktp, orig_src_n, orig_dst_n, (Ipcom_tcp_header *)orig_tcp_hdr, mod);
}
else if(iph->protocol == IP_IPPROTO_UDP)
{
/* Recalculate udp checksum */
ipnet_nat_udp_checksum(*pktp, orig_src_n, orig_dst_n, (Ipcom_udp_header *)orig_udp_hdr, mod);
}
else if(iph->protocol == IP_IPPROTO_ICMP)
{
/* Translate the icmp error embedded datagram */
if (ipnet_nat_translate_embedded_icmp(incoming, *pktp, nat_mapping) != 0)
{
mod = IP_TRUE;
}
/* Recalculate icmp checksum */
ipnet_nat_icmp_checksum(*pktp, orig_icmp_id, mod);
}
}
/* Recalculate ip header checksum */
iph->checksum = 0;
iph->checksum = ipcom_in_checksum(iph, hlen);
if(incoming == IP_FALSE)
ipnet->nat.inner.stats.translated_out++;
else
ipnet->nat.inner.stats.translated_in++;
return IPNET_NAT_TRANSLATED;
}
/*
*===========================================================================
* ipnet_nat_get_tuple
*===========================================================================
* Description: Populates a NAT tuple structure from an IPv4 packet.
* Parameters: pkt - pointer to the packet.
* nat_tuple - pointer to the NAT tuple.
* Returns: 0 = OK
* -1 = Cannot NAT
*/
IP_STATIC Ip_s32
ipnet_nat_get_tuple(Ipcom_pkt *pkt, Ipnet_nat_tuple *nat_tuple)
{
Ipcom_ip_header *iph;
Ipcom_ip_header *iph_embed;
Ip_u16 hlen_embed;
ip_assert(pkt);
ipcom_memset(nat_tuple, 0, sizeof(*nat_tuple));
/* Get pointer to the ip header */
iph = (Ipcom_ip_header *)&pkt->data[pkt->ipstart];
/* Set family */
nat_tuple->family = IP_AF_INET;
/* Get tos */
nat_tuple->tos = iph->tos;
/* Get ttl */
nat_tuple->ttl = iph->ttl;
/* Get fragment offset and flags */
nat_tuple->fragoff = ip_ntohs(iph->frag) & 0x1fff;
nat_tuple->fragmf = (Ip_u8)((ip_ntohs(iph->frag) & 0x2000)>>13);
nat_tuple->fragid = (Ip_u32)ip_ntohs(iph->id);
/* Get addr */
nat_tuple->protocol = iph->protocol;
ipcom_memcpy(&nat_tuple->src_n, &iph->src, sizeof(iph->src));
ipcom_memcpy(&nat_tuple->dst_n, &iph->dst, sizeof(iph->dst));
/* Check transport length */
if (nat_tuple->fragoff == 0)
{
if (nat_tuple->protocol == IP_IPPROTO_ICMP)
{
Ipnet_pkt_icmp *icmp;
if(pkt->end - pkt->start < IPCOM_ICMP_HEADER_LENGTH)
return -1; /* Too short */
icmp = (Ipnet_pkt_icmp *)&pkt->data[pkt->start];
nat_tuple->icmp_type = icmp->type;
if (IPNET_NAT_IS_ICMP_REQUEST(icmp->type) || IPNET_NAT_IS_ICMP_REPLY(icmp->type))
{
if(pkt->end - pkt->start < IPNET_NAT_ICMP_QUERY_LENGTH)
return -1; /* Too short */
nat_tuple->icmp_id_n = icmp->data.echo.id;
}
else if (IPNET_NAT_IS_ICMP_ERROR(icmp->type))
{
iph_embed = (Ipcom_ip_header *)icmp->data.failing_pkt.ip;
hlen_embed = (Ip_u16)((iph_embed->vers_len & 0x0f)<<2);
if (pkt->end - pkt->start < 8 + hlen_embed + 8)
return -1; /* Too short */
}
}
else if (nat_tuple->protocol == IP_IPPROTO_UDP)
{
Ipcom_udp_header *udp;
udp = (Ipcom_udp_header *)&pkt->data[pkt->start];
if(pkt->end - pkt->start < IPCOM_UDP_HEADER_LENGTH)
return -1; /* Too short */
nat_tuple->src_port_n = udp->src_port;
nat_tuple->dst_port_n = udp->dst_port;
}
else if (nat_tuple->protocol == IP_IPPROTO_TCP)
{
Ipcom_tcp_header *tcp;
tcp = (Ipcom_tcp_header *)&pkt->data[pkt->start];
if(pkt->end - pkt->start < IPCOM_TCP_HEADER_LENGTH)
return -1; /* Too short */
nat_tuple->src_port_n = tcp->src_port;
nat_tuple->dst_port_n = tcp->dst_port;
nat_tuple->tcp_flags = ip_ntohs(tcp->offset_flags) & 0x003f;
}
}
return 0;
}
/*
*===========================================================================
* ipnet_nat_loop_rules
*===========================================================================
* Description: Loop through rules and match packets against mappings and
* rules.
* Parameters: incoming - IP_TRUE for incoming packets.
* pktp - pointer to pointer to packet.
* param - pointer to the output parameters.
* Returns: IPNET_NAT_PT_TRANSLATED
* IPNET_NAT_TRANSLATED
* IPNET_NAT_NOMATCH
* IPNET_NAT_INVALID
* IPNET_NAT_DROP
*/
IP_STATIC Ip_s32
ipnet_nat_loop_rules(Ip_bool incoming, Ipcom_pkt **pktp)
{
Ipnet_nat_rule *nat_rule;
Ipnet_nat_tuple nat_tuple;
Ipnet_nat_mapping *nat_mapping;
Ip_s32 ret, i = 0;
/* Get the tuple */
ret = ipnet_nat_get_tuple(*pktp, &nat_tuple);
if(ret < 0)
{
if(incoming == IP_FALSE)
{
IPCOM_LOG0(INFO, "ipnet_nat_loop_rules() :: OUTGOING packet is invalid");
ipnet->nat.inner.stats.invalid_out++;
}
else
{
IPCOM_LOG0(INFO, "ipnet_nat_loop_rules() :: INCOMING packet is invalid");
ipnet->nat.inner.stats.invalid_in++;
}
return IPNET_NAT_INVALID; /* Packet is invalid in some way */
}
/* Search for existing mapping */
nat_mapping = ipnet_nat_find_mapping(incoming, *pktp, &nat_tuple);
if(nat_mapping != IP_NULL)
{
#ifdef IPNET_USE_NAT_PT
if(incoming == IP_TRUE && (nat_mapping->rule->inner.pt == 1 ||
nat_mapping->rule->inner.pt_block == 1))
{
/* Update the mapping */
ipnet_nat_update_mapping(IP_TRUE, nat_mapping, &nat_tuple);
return ipnet_nat_pt_translate(IP_TRUE, nat_mapping, pktp, &nat_tuple);
}
else
#endif
{
/* Update the mapping */
ipnet_nat_update_mapping(incoming, nat_mapping, &nat_tuple);
/* Perform the network address translation */
return ipnet_nat_translate(incoming, pktp, &nat_tuple, nat_mapping);
}
}
/* Match packet against nat rules */
if (nat_tuple.fragoff == 0)
{
nat_rule = IPCOM_LIST_FIRST(&ipnet->nat.head_rule);
while(nat_rule != IP_NULL)
{
i++;
ret = ipnet_nat_match_rule(incoming, nat_rule, (*pktp)->ifindex, &nat_tuple);
if(ret == 1)
{
IPCOM_LOG2(INFO, "ipnet_nat_loop_rules() :: %s packet matched rule @%d",
(incoming ? "INCOMING" : "OUTGOING"),i);
break;
}
/* Try next rule */
nat_rule = IPCOM_LIST_NEXT(&nat_rule->list_rule);
}
if(nat_rule != IP_NULL)
{
/* Add a mapping */
nat_mapping = ipnet_nat_add_mapping(incoming,
nat_rule,
&nat_tuple,
0,
IP_NULL,
IP_FALSE, /* Rule selects port */
nat_rule->proxy_func,
nat_rule->proxy_cookie);
if(nat_mapping == IP_NULL)
{
IPCOM_LOG0(WARNING, "ipnet_nat_loop_rules() :: Failed to add mapping. (dropping packet)");
if(incoming == IP_FALSE)
ipnet->nat.inner.stats.dropped_out++;
else
ipnet->nat.inner.stats.dropped_in++;
return IPNET_NAT_DROP;
}
/* Perform the network address translation */
return ipnet_nat_translate(incoming, pktp, &nat_tuple, nat_mapping);
}
}
else
{
/* Incoming fragments must be re-assembled first */
ip_assert(incoming == IP_FALSE);
}
if(incoming == IP_FALSE)
ipnet->nat.inner.stats.no_rule_match_out++;
else
ipnet->nat.inner.stats.no_rule_match_in++;
return IPNET_NAT_NOMATCH;
} /*lint !e550*/
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_nat_match_rule
*===========================================================================
* Description: Match packet against NAT rules.
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_rule - pointer to the rule.
* ifindex - interface index.
* nat_tuple - pointer to the packet tuple.
* Returns: 1 = packet matched a rule.
* 0 = no matching rules.
*/
IP_GLOBAL Ip_s32
ipnet_nat_match_rule(Ip_bool incoming, Ipnet_nat_rule *nat_rule, Ip_u32 ifindex, Ipnet_nat_tuple *nat_tuple)
{
Ipnet_netif *netif;
/* Check interface */
netif = ipnet_if_indextonetif(IPCOM_VR_ANY, ifindex);
if (netif == IP_NULL
|| ipcom_strcmp(nat_rule->inner.ifname, netif->ipcom.name) != 0)
return 0; /* Interface does not match */
/* Check map and map-block rules for outgoing packets */
if(incoming == IP_FALSE
&& (nat_rule->inner.map == 1 || nat_rule->inner.map_block == 1
|| nat_rule->inner.pt == 1 || nat_rule->inner.pt_block == 1))
{
if(nat_rule->inner.map == 1 || nat_rule->inner.map_block == 1)
{
/* INET packet? */
if (nat_tuple->family != IP_AF_INET)
return 0;
if(nat_rule->inner.src_n != (nat_tuple->src_n & nat_rule->inner.src_mask_n))
return 0; /* Source address does not match */
}
else
{
#ifdef IPNET_USE_NAT_PT
int i;
/* INET packet? */
if (nat_tuple->family != IP_AF_INET6)
return 0;
for(i=0; i<4; i++)
{
if(nat_rule->inner.src_n_pt.in6.addr32[i] !=
(nat_tuple->pt_src.in6.addr32[i] & nat_rule->inner.src_mask_n_pt.in6.addr32[i]))
return 0; /* Source address does not match */
}
#endif
}
if(nat_rule->inner.portmap == 1)
{
if(nat_rule->inner.portmap_tcpudp == 1 && nat_tuple->protocol != IP_IPPROTO_TCP &&
nat_tuple->protocol != IP_IPPROTO_UDP)
return 0; /* Protocol does not match */
if(nat_rule->inner.portmap_tcpudp == 0 && nat_rule->inner.portmap_protocol != nat_tuple->protocol)
return 0; /* Protocol does not match */
}
if(nat_rule->inner.icmpidmap == 1)
{
if(nat_tuple->protocol != IP_IPPROTO_ICMP)
return 0; /* Protocol does not match */
}
if(nat_rule->inner.proxy == 1)
{
if(nat_rule->inner.proxy_protocol != nat_tuple->protocol)
return 0; /* Protocol does not match */
if (nat_tuple->protocol == IP_IPPROTO_TCP || nat_tuple->protocol == IP_IPPROTO_UDP)
{
if(nat_rule->inner.proxy_trigger != ip_ntohs(nat_tuple->dst_port_n))
return 0; /* Port does not match */
}
}
}
/* Check rdr rules for incoming packets */
else if (incoming == IP_TRUE && nat_rule->inner.rdr == 1)
{
/* INET packet? */
if (nat_tuple->family != IP_AF_INET)
return 0;
if(nat_rule->inner.dst_n != (nat_tuple->dst_n & nat_rule->inner.dst_mask_n))
return 0; /* Destination address does not match */
if (nat_rule->inner.rdr_tcpudp == 1)
{
/* Must be UDP or TCP */
if (nat_tuple->protocol != IP_IPPROTO_TCP
&& nat_tuple->protocol != IP_IPPROTO_UDP)
{
return 0; /* Protocol does not match */
}
/* Port must match */
if(nat_rule->inner.rdr_old_port_n != nat_tuple->dst_port_n)
return 0; /* Destination port does not match */
}
else
{
if (nat_rule->inner.rdr_protocol != 0)
{
/* Protocol must match */
if (nat_rule->inner.rdr_protocol != nat_tuple->protocol)
return 0; /* Protocol does not match */
/* Port must match for UDP and TCP */
if (nat_tuple->protocol == IP_IPPROTO_TCP
|| nat_tuple->protocol == IP_IPPROTO_UDP)
{
if(nat_rule->inner.rdr_old_port_n != nat_tuple->dst_port_n)
return 0; /* Destination port does not match */
}
}
}
}
else
{
return 0;
}
/* Rule matched packet! */
return 1;
}
/*
*===========================================================================
* ipnet_nat_checksum_update
*===========================================================================
* Description: Efficient checksum update algorithm taken from RFC 3022
* Copyright (C) The Internet Society (2001). All Rights Reserved.
* assuming: unsigned char is 8 bits, long is 32 bits.
* Parameters: chksum - points to the chksum in the packet.
* optr - points to the old data in the packet.
* olen - old data length.
* nptr - points to the new data in the packet.
* nlen - new data length.
* Returns: nothing.
*/
IP_GLOBAL void
ipnet_nat_checksum_update(unsigned char *chksum, unsigned char *optr, int olen,
unsigned char *nptr, int nlen)
{
int x, old, new;
x = chksum[0] * 256 + chksum[1];
x = ~((unsigned)x) & 0xFFFF;
while (olen)
{
old = optr[0] * 256 + optr[1];
optr += 2;
x -= old & 0xffff;
if (x<=0)
{
x--;
x &= 0xffff;
}
olen -= 2;
}
while (nlen)
{
new = nptr[0] * 256 + nptr[1];
nptr+=2;
x += new & 0xffff;
if (x & 0x10000)
{
x++;
x&=0xffff;
}
nlen -= 2;
}
x = ~((unsigned)x) & 0xFFFF;
chksum[0] = (unsigned char)(x / 256);
chksum[1] = (unsigned char)(x & 0xff);
}
/*
*===========================================================================
* ipnet_nat_update_sequence_number
*===========================================================================
* Description: Updates TCP sequence number information.
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_mapping - pointer to the NAT mapping.
* seq_num - TCP sequence number.
* delta - new correction of sequence number.
* Returns: nothing.
*/
IP_GLOBAL void
ipnet_nat_update_sequence_number(Ip_bool incoming, Ipnet_nat_mapping *nat_mapping, Ip_u32 seq_num, Ip_s32 delta)
{
/* Check that this was not a resend */
if ((nat_mapping->inner.seq_initiated == 0) || ((Ip_s32)(seq_num - nat_mapping->inner.seq_start) > 0))
{
nat_mapping->inner.seq_start = seq_num;
nat_mapping->inner.prev_delta = nat_mapping->inner.curr_delta;
nat_mapping->inner.curr_delta += delta;
nat_mapping->inner.seq_initiated = 1;
nat_mapping->inner.seq_incoming = incoming == IP_TRUE ? 1 : 0;
}
}
/*
*===========================================================================
* ipnet_nat_translate_sequence_number
*===========================================================================
* Description: Translates TCP sequence number.
* Parameters: incoming - IP_TRUE for incoming packets.
* pkt - pointer to the packet.
* nat_mapping - pointer to the NAT mapping.
* Returns: nothing.
*/
IP_GLOBAL void
ipnet_nat_translate_sequence_number(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping)
{
if(nat_mapping->inner.seq_initiated == 1)
{
if((incoming == IP_TRUE && nat_mapping->inner.seq_incoming == 1) ||
(incoming == IP_FALSE && nat_mapping->inner.seq_incoming == 0))
{
Ip_u32 seq_num;
seq_num = IP_GET_32ON16(((Ip_u32 *)&pkt->data[pkt->start] + 1));
seq_num = ip_ntohl(seq_num);
if ((Ip_s32)(seq_num - nat_mapping->inner.seq_start) > 0)
seq_num += nat_mapping->inner.curr_delta;
else
seq_num += nat_mapping->inner.prev_delta;
IP_SET_HTONL((((Ip_u32 *)&pkt->data[pkt->start])+1), seq_num);
}
}
}
/*
*===========================================================================
* ipnet_nat_translate_ack_number
*===========================================================================
* Description: Translates TCP acknowledgement number.
* Parameters: incoming - IP_TRUE for incoming packets.
* pkt - pointer to the packet.
* nat_mapping - pointer to the NAT mapping.
* Returns: nothing.
*/
IP_GLOBAL void
ipnet_nat_translate_ack_number(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping)
{
if(nat_mapping->inner.seq_initiated == 1)
{
if((incoming == IP_TRUE && nat_mapping->inner.seq_incoming == 0) ||
(incoming == IP_FALSE && nat_mapping->inner.seq_incoming == 1))
{
Ip_u32 ack_num, tmp;
ack_num = IP_GET_32ON16(((Ip_u32 *)&pkt->data[pkt->start] + 2));
ack_num = ip_ntohl(ack_num);
tmp = nat_mapping->inner.seq_start + nat_mapping->inner.prev_delta;
if ((Ip_s32)(ack_num - tmp) > 0)
ack_num -= nat_mapping->inner.curr_delta;
else
ack_num -= nat_mapping->inner.prev_delta;
IP_SET_HTONL((((Ip_u32 *)&pkt->data[pkt->start])+2), ack_num);
}
}
}
/*
*===========================================================================
* ipnet_nat_proxy
*===========================================================================
* Description: Call application proxy (ALG).
* Parameters: pktp - pointer to packet.
* nat_tuple - pointer to packet tuple.
* nat_mapping - pointer to NAT mapping.
* hlen - header length in case a new packet must be generated.
* mod - pointer to boolean for indicatation that the proxy
* modified the packet.
* delta - pointer to variable used for returning the
* difference in length after the proxy modified
* the packet.
* Returns: 1 = proxy modified packet.
* 0 = unmodified.
* -1 = proxy requests to drop packet.
*/
IP_GLOBAL Ip_s32
ipnet_nat_proxy(Ip_bool incoming,
Ipcom_pkt **pktp,
Ipnet_nat_tuple *nat_tuple,
Ipnet_nat_mapping *nat_mapping,
int hlen,
Ip_bool *mod,
Ip_s32 *delta)
{
Ipnet_nat_proxy_param proxy_param;
Ipcom_tcp_header *tcp;
Ipcom_udp_header *udp;
Ip_u8 *newhdr, *appdata, *newdata = IP_NULL;
int tlen, applen, origlen, ret;
Ipcom_pkt *new_pkt;
/*
* because nat can not process scattered packet, so we have to
* merge the scattered packet into one packet.
*/
if ((*pktp)->next_part != IP_NULL)
{
int pkt_start = (*pktp)->start;
int pkt_ipstart = (*pktp)->ipstart;
(*pktp)->start = 0;
*pktp = ipcom_pkt_make_linear(*pktp);
if (*pktp == IP_NULL)
return -1;
(*pktp)->start += pkt_start;
(*pktp)->ipstart = (*pktp)->start + (pkt_ipstart - pkt_start);
}
newhdr = &(*pktp)->data[(*pktp)->start-hlen];
if(nat_tuple->fragoff == 0 && nat_tuple->protocol == IP_IPPROTO_TCP)
{
tcp = (Ipcom_tcp_header *)&(*pktp)->data[(*pktp)->start];
tlen = ip_ntohs(tcp->offset_flags)>>10;
appdata = (Ip_u8 *)tcp + tlen;
applen = ((*pktp)->end - (*pktp)->start) - tlen;
}
else if(nat_tuple->fragoff == 0 && nat_tuple->protocol == IP_IPPROTO_UDP)
{
udp = (Ipcom_udp_header *)&(*pktp)->data[(*pktp)->start];
tlen = sizeof(*udp);
appdata = (Ip_u8 *)udp + tlen;
applen = ((*pktp)->end - (*pktp)->start) - tlen;
}
else
{
tlen = 0;
appdata = &(*pktp)->data[(*pktp)->start] + tlen;
applen = ((*pktp)->end - (*pktp)->start) - tlen;
}
/* Populate proxy parameters */
ipcom_memset(&proxy_param, 0, sizeof(proxy_param));
proxy_param.mapping = nat_mapping;
proxy_param.inbound = nat_mapping->inner.rdr == 1 ? IP_TRUE : IP_FALSE;
proxy_param.incoming = incoming;
proxy_param.fragid = nat_tuple->fragid;
proxy_param.fragmf = nat_tuple->fragmf;
proxy_param.fragoff = nat_tuple->fragoff;
proxy_param.cookie = nat_mapping->proxy_cookie;
proxy_param.tuple.protocol = nat_mapping->inner.tuple.protocol;
proxy_param.tuple.private_addr = ip_ntohl(nat_mapping->inner.tuple.src_n);
proxy_param.tuple.public_addr = ip_ntohl(nat_mapping->inner.tuple.dst_n);
proxy_param.tuple.private_port = ip_ntohs(nat_mapping->inner.tuple.src_port_n);
proxy_param.tuple.public_port = ip_ntohs(nat_mapping->inner.tuple.dst_port_n);
proxy_param.nat_addr = ip_ntohl(nat_mapping->inner.nat_addr_n);
proxy_param.nat_port = ip_ntohs(nat_mapping->inner.nat_port_n);
if (nat_mapping->rule->inner.pt == 1 || nat_mapping->rule->inner.pt_block == 1)
{
proxy_param.natpt = IP_TRUE;
#ifdef IPNET_USE_NAT_PT
proxy_param.prefix[0] = nat_mapping->inner.tuple.pt_dst.in6.addr32[0];
proxy_param.prefix[1] = nat_mapping->inner.tuple.pt_dst.in6.addr32[1];
proxy_param.prefix[2] = nat_mapping->inner.tuple.pt_dst.in6.addr32[2];
#endif
}
origlen = applen;
ip_assert(nat_mapping->proxy_func != IP_NULL);
ret = nat_mapping->proxy_func(newhdr,
appdata,
&applen,
(*pktp)->maxlen - (*pktp)->end,
&proxy_param,
&newdata);
if (ret == 1)
{
/* Packet modified */
*delta = (Ip_s32)(applen - origlen);
*mod = IP_TRUE;
if (newdata != IP_NULL)
{
ip_assert(applen > origlen); /* Did not fit so new buffer must be larger */
new_pkt = ipcom_pkt_malloc(IPNET_NAT_LINK_HDR_SIZE + hlen + tlen + applen,
IP_FLAG_FC_STACKCONTEXT);
if(new_pkt == IP_NULL)
{
*delta = 0;
*mod = IP_FALSE;
return -1;
}
new_pkt->ipstart = IPNET_NAT_LINK_HDR_SIZE;
new_pkt->start = new_pkt->ipstart + hlen;
new_pkt->end = new_pkt->start + tlen + applen;
new_pkt->ifindex = (*pktp)->ifindex;
new_pkt->flags = (*pktp)->flags;
ipcom_memcpy(&new_pkt->data[new_pkt->start-hlen], &(*pktp)->data[(*pktp)->start-hlen], hlen+tlen);
ipcom_memcpy(&new_pkt->data[new_pkt->start+tlen], newdata, applen);
ipcom_free(newdata);
ipcom_pkt_free(*pktp);
*pktp = new_pkt;
}
else
{
(*pktp)->end = (*pktp)->end + *delta;
}
ip_assert((*pktp)->end <= (*pktp)->maxlen);
}
else if (ret == 0)
{
/* Packet untouched */
ip_assert(origlen == applen);
*delta = 0;
*mod = IP_FALSE;
}
else
{
/* Drop packet */
*delta = 0;
*mod = IP_FALSE;
}
return (Ip_s32)ret;
}
/*
*===========================================================================
* ipnet_nat_translate_tcpudp_port
*===========================================================================
* Description: Translate the TCP/UDP port.
* Parameters: incoming - IP_TRUE for incoming packets.
* pkt - pointer to the packet.
* nat_mapping - pointer to the NAT mapping.
* Returns: nothing.
*/
IP_GLOBAL void
ipnet_nat_translate_tcpudp_port(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping)
{
/* Translate the source/destination port */
if(incoming == IP_FALSE)
*(Ip_u16 *)&pkt->data[pkt->start] = nat_mapping->inner.nat_port_n;
else
*(((Ip_u16 *)&pkt->data[pkt->start])+1) = nat_mapping->inner.tuple.src_port_n;
}
/*
*===========================================================================
* ipnet_nat_add_mapping
*===========================================================================
* Description: Add a NAT mapping.
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_rule - pointer to the rule.
* nat_tuple - pointer to the packet tuple.
* timeout - mapping timeout in seconds (0 means default)
* parent - pointer to parent mapping.
* proxy_napt - IP_TRUE to enable NAPT for proxy added mappings.
* proxy_func - proxy function (optional).
* proxy_cookie - proxy cookie (optional).
* Returns: pointer to the NAT mapping or IP_NULL if failed.
*/
IP_GLOBAL Ipnet_nat_mapping *
ipnet_nat_add_mapping(Ip_bool incoming,
Ipnet_nat_rule *nat_rule,
Ipnet_nat_tuple *nat_tuple,
Ip_u32 timeout,
Ipnet_nat_mapping *parent,
Ip_bool proxy_napt,
Ipnet_nat_proxy_func proxy_func,
void *proxy_cookie)
{
Ipnet_nat_mapping *nat_mapping;
Ip_bool add_in_port_hash;
Ip_s32 nat_id;
/* Check that there is room for another mapping */
if(ipnet->nat.head_mapping.size >= (Ip_u32)ipnet->nat.inner.max_mappings)
{
IPCOM_LOG0(WARNING, "ipnet_nat_add_mapping() :: Maximum number of mappings reached");
ipnet->nat.inner.stats.mapping_failures++;
return IP_NULL;
}
nat_mapping = ipcom_malloc(sizeof(*nat_mapping));
if(nat_mapping == IP_NULL)
{
IPCOM_LOG0(ERR, "ipnet_nat_add_mapping() :: Could not allocate memory for mapping");
ipnet->nat.inner.stats.mapping_failures++;
return IP_NULL;
}
ipcom_memset(nat_mapping, 0, sizeof(*nat_mapping));
ipcom_memcpy(&nat_mapping->inner.tuple, nat_tuple, sizeof(*nat_tuple));
nat_mapping->rule = nat_rule;
nat_mapping->inner.rdr = incoming == IP_TRUE ? 1 : 0;
nat_mapping->proxy_func = proxy_func;
nat_mapping->proxy_cookie = proxy_cookie;
if (parent == IP_NULL)
{
/* Assign new address and port */
if (ipnet_nat_assign_mapping(incoming, nat_mapping, nat_tuple, &add_in_port_hash) != 0)
{
ipnet->nat.inner.stats.mapping_failures++;
ipcom_free(nat_mapping);
return IP_NULL;
}
}
else
{
ip_assert(nat_tuple->protocol != IP_IPPROTO_ICMP);
/* Mapping has a parent, use parent's address */
nat_mapping->inner.nat_addr_n = parent->inner.nat_addr_n;
/* Store destination address */
nat_mapping->inner.dstmap_addr = nat_tuple->dst_n;
if(nat_tuple->protocol == IP_IPPROTO_UDP
|| nat_tuple->protocol == IP_IPPROTO_TCP)
{
if (proxy_napt == IP_TRUE)
{
/* Allocate a port from the auto pool */
if((nat_id = ipnet_nat_get_id(ipnet->nat.inner.port_hi,
ipnet->nat.inner.port_lo,
ipnet->nat.ports,
&ipnet->nat.last_port,
ip_htons((Ip_u16)nat_tuple->src_port_n))) < 0)
{
IPCOM_LOG0(ERR, "ipnet_nat_add_mapping() :: Could not find available automatic nat id");
ipnet->nat.inner.stats.mapping_failures++;
ipcom_free(nat_mapping);
return IP_NULL;
}
nat_mapping->inner.nat_port_n = ip_htons((Ip_u16)nat_id);
nat_mapping->inner.autoport = 1;
}
else
{
/* NAT port is the same as source port */
nat_mapping->inner.nat_port_n = nat_tuple->src_port_n;
}
add_in_port_hash = IP_TRUE;
}
else
add_in_port_hash = IP_FALSE;
}
/* Assign state and timeout */
ipnet_nat_assign_timeout(incoming, nat_mapping, nat_tuple, timeout);
/* Add mapping to the hash tables */
if(add_in_port_hash == IP_TRUE)
if(ipcom_hash_add(ipnet->nat.hash_out_port, nat_mapping) != IPCOM_SUCCESS)
goto cleanup;
if(ipcom_hash_add(ipnet->nat.hash_out_noport, nat_mapping) != IPCOM_SUCCESS)
goto cleanup;
if(add_in_port_hash == IP_TRUE)
if(ipcom_hash_add(ipnet->nat.hash_in_port, nat_mapping) != IPCOM_SUCCESS)
goto cleanup;
if(ipcom_hash_add(ipnet->nat.hash_in_noport, nat_mapping) != IPCOM_SUCCESS)
goto cleanup;
ipcom_list_insert_last(&ipnet->nat.head_mapping, &nat_mapping->list);
#ifdef IPNET_DEBUG
IPCOM_LOG0(DEBUG, "ipnet_nat_add_mapping() :: Mapping added");
ipnet_nat_syslog_mapping(nat_mapping);
#endif
ipnet->nat.inner.stats.mappings_added++;
return nat_mapping;
cleanup:
ipnet_nat_clean_mapping(nat_mapping);
return IP_NULL;
}
/*
*===========================================================================
* ipnet_nat_update_mapping
*===========================================================================
* Description: Updates the state of a mapping and schedules new timeout.
* Parameters: incoming - IP_TRUE for incoming packets.
* nat_mapping - pointer to the nat_mapping to be updated.
* nat_tuple - pointer to the packet tuple.
* Returns: nothing.
*/
IP_GLOBAL void
ipnet_nat_update_mapping(Ip_bool incoming,
Ipnet_nat_mapping *nat_mapping,
Ipnet_nat_tuple *nat_tuple)
{
/* Adjust states and set timeout */
if (nat_mapping->inner.tuple.protocol == IP_IPPROTO_TCP)
{
if (nat_tuple->fragoff == 0)
{
/* Check SYN flag */
if(IP_BIT_ISSET(nat_tuple->tcp_flags, IPNET_NAT_TCPFLAG_SYN))
{
IPCOM_LOG0(DEBUG, "ipnet_nat_update_mapping() :: (SYN)");
if(incoming == IP_TRUE)
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_SYN;
else
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_SYN;
/* Go to established */
if(nat_mapping->inner.outer_state == IPNET_NAT_MAPPING_SYN &&
nat_mapping->inner.inner_state == IPNET_NAT_MAPPING_SYN)
{
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_EST;
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_EST;
nat_mapping->inner.tmo_int_sec = ipnet->nat.inner.timeout_tcp;
}
}
/* Check FIN flag */
if(IP_BIT_ISSET(nat_tuple->tcp_flags, IPNET_NAT_TCPFLAG_FIN))
{
IPCOM_LOG0(DEBUG, "ipnet_nat_update_mapping() :: (FIN)");
if(incoming == IP_TRUE)
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_FIN;
else
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_FIN;
}
/* Check RST flag */
if(IP_BIT_ISSET(nat_tuple->tcp_flags, IPNET_NAT_TCPFLAG_RESET))
{
IPCOM_LOG0(DEBUG, "ipnet_nat_update_mapping() :: (RST)");
nat_mapping->inner.outer_state = IPNET_NAT_MAPPING_FIN;
nat_mapping->inner.inner_state = IPNET_NAT_MAPPING_FIN;
}
if (nat_mapping->inner.outer_state != IPNET_NAT_MAPPING_EST ||
nat_mapping->inner.inner_state != IPNET_NAT_MAPPING_EST)
{
nat_mapping->inner.tmo_int_sec = ipnet->nat.inner.timeout_other;
}
}
}
else if (nat_tuple->protocol == IP_IPPROTO_ICMP)
{
/* Do not update the mapping for ICMP error messages */
if (IPNET_NAT_IS_ICMP_ERROR(nat_tuple->icmp_type))
return;
}
/* Reset the timeout */
ipnet_nat_reset_mapping_timeout(nat_mapping);
#ifdef IPNET_DEBUG
IPCOM_LOG0(DEBUG, "ipnet_nat_update_mapping() :: Mapping updated");
ipnet_nat_syslog_mapping(nat_mapping);
#endif
}
/*
*===========================================================================
* ipnet_nat_embedded_tuple
*===========================================================================
* Description: Find an embedded tuple in an ICMP error message.
* Parameters: pkt - pointer to the packet.
* nat_tuple - pointer to the incoming tuple.
* embedded_tuple - pointer to the returned embedded tuple.
* Returns: 0 = Found tuple.
* -1 = No embedded tuple found.
*/
IP_STATIC Ip_s32
ipnet_nat_embedded_tuple(Ipcom_pkt *pkt, Ipnet_nat_tuple *nat_tuple, Ipnet_nat_tuple *embedded_tuple)
{
Ipnet_pkt_icmp *icmp;
Ipnet_pkt_icmp *icmp_embed;
Ipcom_ip_header *iph_embed;
Ipcom_udp_header *udp_embed;
Ipcom_tcp_header *tcp_embed;
Ip_u16 hlen_embed;
icmp = (Ipnet_pkt_icmp *)&pkt->data[pkt->start];
iph_embed = (Ipcom_ip_header *)icmp->data.failing_pkt.ip;
hlen_embed = (Ip_u16)((iph_embed->vers_len & 0x0f)<<2);
if(nat_tuple->fragoff != 0 || IPNET_NAT_IS_ICMP_ERROR(nat_tuple->icmp_type) == IP_FALSE)
{
return -1;
}
ipcom_memset(embedded_tuple, 0, sizeof(*embedded_tuple));
embedded_tuple->family = IP_AF_INET;
embedded_tuple->dst_n = IP_GET_32ON16((void *)&iph_embed->src.addr32);
embedded_tuple->src_n = IP_GET_32ON16((void *)&iph_embed->dst.addr32);
embedded_tuple->protocol = iph_embed->protocol;
if(embedded_tuple->protocol == IP_IPPROTO_UDP)
{
udp_embed = (Ipcom_udp_header *)((Ip_u8 *)iph_embed + hlen_embed);
embedded_tuple->dst_port_n = udp_embed->src_port;
embedded_tuple->src_port_n = udp_embed->dst_port;
}
else if(embedded_tuple->protocol == IP_IPPROTO_TCP)
{
tcp_embed = (Ipcom_tcp_header *)((Ip_u8 *)iph_embed + hlen_embed);
embedded_tuple->dst_port_n = tcp_embed->src_port;
embedded_tuple->src_port_n = tcp_embed->dst_port;
}
else if(embedded_tuple->protocol == IP_IPPROTO_ICMP)
{
/* Pretend reception of a reply */
icmp_embed = (Ipnet_pkt_icmp *)((Ip_u8 *)iph_embed + hlen_embed);
embedded_tuple->icmp_type = icmp_embed->type;
switch(icmp_embed->type)
{
case IPCOM_ICMP_ECHO_REQUEST:
embedded_tuple->icmp_type = IPCOM_ICMP_ECHO_REPLY;
break;
case IPCOM_ICMP_ECHO_REPLY:
embedded_tuple->icmp_type = IPCOM_ICMP_ECHO_REQUEST;
break;
case IPCOM_ICMP_TSTAMP_REQUEST:
embedded_tuple->icmp_type = IPCOM_ICMP_TSTAMP_REPLY;
break;
case IPCOM_ICMP_TSTAMP_REPLY:
embedded_tuple->icmp_type = IPCOM_ICMP_TSTAMP_REQUEST;
break;
case IPCOM_ICMP_INFO_REQUEST:
embedded_tuple->icmp_type = IPCOM_ICMP_INFO_REPLY;
break;
case IPCOM_ICMP_INFO_REPLY:
embedded_tuple->icmp_type = IPCOM_ICMP_INFO_REQUEST;
break;
case IPCOM_ICMP_MASK_REQUEST:
embedded_tuple->icmp_type = IPCOM_ICMP_MASK_REPLY;
break;
case IPCOM_ICMP_MASK_REPLY:
embedded_tuple->icmp_type = IPCOM_ICMP_MASK_REQUEST;
break;
default:
return 0;
}
embedded_tuple->icmp_id_n = icmp_embed->data.echo.id;
}
return 0;
}
/*
*===========================================================================
* ipnet_nat_find_mapping
*===========================================================================
* Description: Searches for a matching NAT mapping based on a packet.
* Parameters: incoming - IP_TRUE for incoming packets.
* pkt - pointer to the packet.
* nat_tuple - pointer to the tuple.
* Returns: pointer to the nat_mapping if found or IP_NULL if not found.
*/
IP_GLOBAL Ipnet_nat_mapping *
ipnet_nat_find_mapping(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_tuple *nat_tuple)
{
Ipnet_nat_tuple embedded_tuple;
Ipnet_nat_mapping *nat_mapping;
Ipnet_nat_hash_key nat_key;
if (ipnet->nat.head_mapping.size == 0)
{
ip_assert(ipnet->nat.hash_out_port->elem == 0);
ip_assert(ipnet->nat.hash_out_noport->elem == 0);
ip_assert(ipnet->nat.hash_in_port->elem == 0);
ip_assert(ipnet->nat.hash_in_noport->elem == 0);
return IP_NULL;
}
if (nat_tuple->protocol == IP_IPPROTO_ICMP)
{
/* Extract the embedded IPv4 header and first part of datagram */
if (pkt != IP_NULL && ipnet_nat_embedded_tuple(pkt, nat_tuple, &embedded_tuple) == 0)
nat_tuple = &embedded_tuple;
}
/* Create the hask key and try to find in hash tables */
if(incoming == IP_TRUE)
{
nat_key.protocol = nat_tuple->protocol;
nat_key.dst_n = nat_tuple->src_n;
nat_key.nat_addr_n = nat_tuple->dst_n;
if(nat_tuple->fragoff == 0 &&
(nat_tuple->protocol == IP_IPPROTO_TCP || nat_tuple->protocol == IP_IPPROTO_UDP))
{
nat_key.dst_port_n = nat_tuple->src_port_n;
nat_key.nat_port_n = nat_tuple->dst_port_n;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_in_port, &nat_key);
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit destination port */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for INCOMING packet"
" when trying with explicit address and port");
nat_key.dst_port_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_in_port, &nat_key);
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit destination port and destination address */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for INCOMING packet"
" when trying with implicit destination port = 0");
nat_key.dst_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_in_port, &nat_key);
}
if(nat_mapping != IP_NULL)
{
/* Remove the mapping from the hash tables */
ip_assert(nat_mapping->inner.tuple.dst_port_n == 0);
(void)ipcom_hash_remove(ipnet->nat.hash_out_port, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_out_noport, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_port, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_noport, nat_mapping);
/* Update the destination port */
nat_mapping->inner.tuple.dst_port_n = nat_tuple->src_port_n;
/* Update the destination address */
nat_mapping->inner.dstmap_addr = nat_mapping->inner.tuple.dst_n = nat_tuple->src_n;
/* Add mapping to the hash tables */
if(ipcom_hash_add(ipnet->nat.hash_out_port, nat_mapping) != IPCOM_SUCCESS
|| ipcom_hash_add(ipnet->nat.hash_out_noport, nat_mapping) != IPCOM_SUCCESS
|| ipcom_hash_add(ipnet->nat.hash_in_port, nat_mapping) != IPCOM_SUCCESS
|| ipcom_hash_add(ipnet->nat.hash_in_noport, nat_mapping) != IPCOM_SUCCESS)
{
ipnet_nat_clean_mapping(nat_mapping);
nat_mapping = IP_NULL;
}
}
else
{
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for INCOMING packet"
" when trying with implicit destination port = 0 and destination address = 0");
}
}
}
else if(nat_tuple->fragoff == 0
&& nat_tuple->protocol == IP_IPPROTO_ICMP
&& (IPNET_NAT_IS_ICMP_REQUEST(nat_tuple->icmp_type) || IPNET_NAT_IS_ICMP_REPLY(nat_tuple->icmp_type)))
{
nat_key.dst_port_n = 0;
nat_key.nat_port_n = nat_tuple->icmp_id_n;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_in_port, &nat_key);
}
else
{
nat_mapping = ipcom_hash_get(ipnet->nat.hash_in_noport, &nat_key);
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit address */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for INCOMING packet"
" when trying with explicit adress");
nat_key.dst_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_in_noport, &nat_key);
if(nat_mapping != IP_NULL)
{
/* Remove the mapping from the hash tables */
ip_assert(nat_mapping->inner.tuple.dst_n == 0);
(void)ipcom_hash_remove(ipnet->nat.hash_out_noport, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_noport, nat_mapping);
/* Update the destination address */
/* The address is assigned right in this way */
/* coverity[copy_paste_error] */
nat_mapping->inner.tuple.dst_n = nat_tuple->src_n;
/* Add mapping to the hash tables */
if(ipcom_hash_add(ipnet->nat.hash_out_noport, nat_mapping) != IPCOM_SUCCESS ||
ipcom_hash_add(ipnet->nat.hash_in_noport, nat_mapping) != IPCOM_SUCCESS)
{
ipnet_nat_clean_mapping(nat_mapping);
nat_mapping = IP_NULL;
}
}
else
{
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for INCOMING packet"
" when trying with implicit adress = 0");
}
}
}
}
else
{
nat_key.protocol = nat_tuple->protocol;
nat_key.dst_n = nat_tuple->dst_n;
nat_key.src_n = nat_tuple->src_n;
if(nat_tuple->fragoff == 0 &&
(nat_tuple->protocol == IP_IPPROTO_TCP || nat_tuple->protocol == IP_IPPROTO_UDP))
{
nat_key.dst_port_n = nat_tuple->dst_port_n;
nat_key.src_port_n = nat_tuple->src_port_n;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_port, &nat_key);
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit source port */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with explicit addresses and ports");
nat_key.src_port_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_port, &nat_key);
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit source port and source address */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with implicit source port = 0");
nat_key.src_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_port, &nat_key);
}
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit destination port */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with implicit source port = 0 and source address = 0");
nat_key.src_n = nat_tuple->src_n;
nat_key.src_port_n = nat_tuple->src_port_n;
nat_key.dst_port_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_port, &nat_key);
}
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit destination port and destination address */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with implicit destination port = 0");
nat_key.dst_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_port, &nat_key);
}
if(nat_mapping != IP_NULL)
{
/* Remove the mapping from the hash tables */
ip_assert(nat_mapping->inner.tuple.src_port_n == 0 || nat_mapping->inner.tuple.dst_port_n == 0);
(void)ipcom_hash_remove(ipnet->nat.hash_out_port, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_out_noport, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_port, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_noport, nat_mapping);
/* Update the mapping */
nat_mapping->inner.tuple.src_port_n = nat_tuple->src_port_n;
nat_mapping->inner.tuple.src_n = nat_tuple->src_n;
nat_mapping->inner.tuple.dst_port_n = nat_tuple->dst_port_n;
nat_mapping->inner.tuple.dst_n = nat_tuple->dst_n;
/* Add mapping to the hash tables */
if(ipcom_hash_add(ipnet->nat.hash_out_port, nat_mapping) != IPCOM_SUCCESS
|| ipcom_hash_add(ipnet->nat.hash_out_noport, nat_mapping) != IPCOM_SUCCESS
|| ipcom_hash_add(ipnet->nat.hash_in_port, nat_mapping) != IPCOM_SUCCESS
|| ipcom_hash_add(ipnet->nat.hash_in_noport, nat_mapping) != IPCOM_SUCCESS)
{
ipnet_nat_clean_mapping(nat_mapping);
nat_mapping = IP_NULL;
}
}
else
{
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with implicit destination port = 0 and destination address = 0");
}
}
}
else if(nat_tuple->fragoff == 0
&& nat_tuple->protocol == IP_IPPROTO_ICMP
&& (IPNET_NAT_IS_ICMP_REQUEST(nat_tuple->icmp_type) || IPNET_NAT_IS_ICMP_REPLY(nat_tuple->icmp_type)))
{
nat_key.dst_port_n = 0;
nat_key.src_port_n = nat_tuple->icmp_id_n;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_port, &nat_key);
}
else
{
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_noport, &nat_key);
if(nat_mapping == IP_NULL)
{
/* Check for mappings with implicit address */
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with explicit adress");
nat_key.src_n = 0;
nat_mapping = ipcom_hash_get(ipnet->nat.hash_out_noport, &nat_key);
if(nat_mapping != IP_NULL)
{
/* Remove the mapping from the hash tables */
ip_assert(nat_mapping->inner.tuple.src_n == 0);
(void)ipcom_hash_remove(ipnet->nat.hash_out_noport, nat_mapping);
(void)ipcom_hash_remove(ipnet->nat.hash_in_noport, nat_mapping);
/* Update the source address */
nat_mapping->inner.tuple.src_n = nat_tuple->src_n;
/* Add mapping to the hash tables */
if(ipcom_hash_add(ipnet->nat.hash_out_noport, nat_mapping) != IPCOM_SUCCESS ||
ipcom_hash_add(ipnet->nat.hash_in_noport, nat_mapping) != IPCOM_SUCCESS)
{
ipnet_nat_clean_mapping(nat_mapping);
nat_mapping = IP_NULL;
}
}
else
{
IPCOM_LOG0(DEBUG2, "ipnet_nat_find_mapping() :: no mapping found for OUTGOING packet"
" when trying with implicit adress = 0");
}
}
}
}
#ifdef IPNET_DEBUG
if(nat_mapping != IP_NULL)
{
IPCOM_LOG1(DEBUG, "ipnet_nat_find_mapping() :: %s packet matched mapping",
incoming == IP_TRUE ? "INCOMING" : "OUTGOING");
ipnet_nat_syslog_mapping(nat_mapping);
}
#endif
return nat_mapping;
}
/*
*===========================================================================
* ipnet_nat_getsockopt
*===========================================================================
* Description: Dispatcher for the IP_IP_NAT socket option.
* Parameters: sock - pointer to socket.
* optname - option name.
* optval - pointer to option value.
* optlen - pointer to option length.
* Returns: 0 = success.
* IP_ERRNO_EINVAL = error.
*/
IP_GLOBAL Ip_s32
ipnet_nat_getsockopt(Ipnet_socket *sock, int optname, void *optval,
Ip_socklen_t *optlen)
{
Ip_s32 ret = -1;
Ipnet_nat_ctrl *ctrl;
(void)sock;
(void)optname;
if(*optlen < sizeof(*ctrl))
goto exit;
ctrl = (Ipnet_nat_ctrl *)optval;
switch(ctrl->cmd)
{
case IPNET_NAT_CTRL_GET_RULE:
ret = ipnet_nat_internal_get_rule(ctrl, optlen);
break;
case IPNET_NAT_CTRL_GET_MAPPING:
ret = ipnet_nat_internal_get_mapping(ctrl, optlen);
break;
case IPNET_NAT_CTRL_GET_PROXY:
ret = ipnet_nat_internal_get_proxy(ctrl, optlen);
break;
case IPNET_NAT_CTRL_GET_INFO:
ret = ipnet_nat_internal_get_info(ctrl, optlen);
break;
default:
break;
}
exit:
return ret != 0 ? -IP_ERRNO_EINVAL : 0;
}
/*
*===========================================================================
* ipnet_nat_setsockopt
*===========================================================================
* Description: Dispatcher for the IP_IP_NAT socket option.
* Parameters: sock - pointer to socket.
* optname - option name.
* optval - pointer to option value.
* optlen - option length.
* Returns: 0 = success.
* IP_ERRNO_EINVAL = error.
*/
IP_GLOBAL Ip_s32
ipnet_nat_setsockopt(Ipnet_socket *sock, int optname, IP_CONST void *optval,
Ip_socklen_t optlen)
{
Ipnet_nat_ctrl *ctrl;
Ip_s32 ret = -1;
(void)sock;
(void)optname;
if(optlen < sizeof(*ctrl))
goto exit;
ctrl = (Ipnet_nat_ctrl *)optval;
/* Must invalidate the routing cache since NAT can affect routing */
ipnet_route_cache_invalidate();
switch(ctrl->cmd)
{
case IPNET_NAT_CTRL_ENABLE:
ret = ipnet_nat_internal_enable();
break;
case IPNET_NAT_CTRL_DISABLE:
ret = ipnet_nat_internal_disable();
break;
case IPNET_NAT_CTRL_ADD_RULE:
ret = ipnet_nat_internal_add_rule(&ctrl->type.rule_info, ctrl->seqno);
break;
case IPNET_NAT_CTRL_DEL_RULE:
ret = ipnet_nat_internal_remove_rule(&ctrl->type.rule_info);
break;
case IPNET_NAT_CTRL_FLUSH_RULES:
ret = ipnet_nat_internal_flush_rules();
break;
case IPNET_NAT_CTRL_FLUSH_MAPPINGS:
ret = ipnet_nat_internal_flush_mappings();
break;
case IPNET_NAT_CTRL_CLEAR_STATS:
ret = ipnet_nat_internal_clear_stats();
break;
default:
break;
}
exit:
return ret != 0 ? -IP_ERRNO_EINVAL : 0;
}
/*
*===========================================================================
* ipnet_nat_init
*===========================================================================
* Description: Initialize NAT.
* Parameters: none.
* Returns: IPCOM_ERR_NO_MEMORY
* IPCOM_ERR_FAILED
* IPCOM_SUCCESS
*/
IP_GLOBAL Ip_err
ipnet_nat_init(void)
{
Ip_err errval;
/* Clear NAT global memory */
ipcom_memset(&ipnet->nat, 0, sizeof(ipnet->nat));
/* Initialize lists */
ipcom_list_init(&ipnet->nat.head_rule);
ipcom_list_init(&ipnet->nat.head_proxy);
/* Create hash tables */
ipnet->nat.hash_out_port = ipcom_hash_new( (Ipcom_hash_obj_func)ipnet_nat_obj_hash_out_port,
(Ipcom_hash_key_func)ipnet_nat_key_hash_out_port,
(Ipcom_hash_cmp_func)ipnet_nat_cmp_hash_out_port);
ipnet->nat.hash_out_noport = ipcom_hash_new((Ipcom_hash_obj_func)ipnet_nat_obj_hash_out_noport,
(Ipcom_hash_key_func)ipnet_nat_key_hash_out_noport,
(Ipcom_hash_cmp_func)ipnet_nat_cmp_hash_out_noport);
ipnet->nat.hash_in_port = ipcom_hash_new( (Ipcom_hash_obj_func)ipnet_nat_obj_hash_in_port,
(Ipcom_hash_key_func)ipnet_nat_key_hash_in_port,
(Ipcom_hash_cmp_func)ipnet_nat_cmp_hash_in_port);
ipnet->nat.hash_in_noport = ipcom_hash_new( (Ipcom_hash_obj_func)ipnet_nat_obj_hash_in_noport,
(Ipcom_hash_key_func)ipnet_nat_key_hash_in_noport,
(Ipcom_hash_cmp_func)ipnet_nat_cmp_hash_in_noport);
ipcom_list_init(&ipnet->nat.head_mapping);
/* Status check */
if(ipnet->nat.hash_out_port == IP_NULL
|| ipnet->nat.hash_out_noport == IP_NULL
|| ipnet->nat.hash_in_port == IP_NULL
|| ipnet->nat.hash_in_noport == IP_NULL)
{
errval = IPCOM_ERR_NO_MEMORY;
goto fail;
}
/* Add application proxies */
#ifdef IPNET_USE_NAT_FTP_ALG
errval = ipnet_nat_add_proxy("ftp", "tcp", ipnet_nat_proxy_ftp, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add FTP ALG");
goto fail;
}
/* Test code */
errval = ipnet_nat_remove_proxy("ftp", "tcp");
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to remove FTP ALG");
goto fail;
}
errval = ipnet_nat_add_proxy("ftp", "tcp", ipnet_nat_proxy_ftp, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add FTP ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_FTP_ALG */
#ifdef IPNET_USE_NAT_H323_ALG
errval = ipnet_nat_add_proxy("h323", "tcp", ipnet_nat_proxy_h323, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add H.323 ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_H323_ALG */
#ifdef IPNET_USE_NAT_SIP_ALG
errval = ipnet_nat_add_proxy("sip", "udp", ipnet_nat_proxy_sip, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add SIP ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_SIP_ALG */
#ifdef IPNET_USE_NAT_PPTP_ALG
errval = ipnet_nat_add_proxy("pptp", "gre", ipnet_nat_proxy_pptp, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add PPTP ALG");
goto fail;
}
errval = ipnet_nat_add_proxy("pptp", "tcp", ipnet_nat_proxy_pptp, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add PPTP ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_PPTP_ALG */
#ifdef IPNET_USE_NAT_DNS_ALG
errval = ipnet_nat_add_proxy("dns", "udp", ipnet_nat_proxy_dns, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add DNS ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_DNS_ALG */
#ifdef IPNET_USE_NAT_IPSEC_ALG
errval = ipnet_nat_add_proxy("ipsec", "esp", ipnet_nat_proxy_ipsec, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add IPSEC ALG");
goto fail;
}
errval = ipnet_nat_add_proxy("ipsec", "udp", ipnet_nat_proxy_ipsec, IP_NULL);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add IPSEC ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_IPSEC_ALG */
#ifdef IPNET_USE_NAT_TRIGGER_ALG
errval = ipnet_nat_add_proxy("example_tcp", "tcp", ipnet_nat_proxy_trigger, &example_trigger);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add TRIGGER ALG");
goto fail;
}
errval = ipnet_nat_add_proxy("example_udp", "udp", ipnet_nat_proxy_trigger, &example_trigger);
if (errval != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipnet_nat_init() :: Failed to add TRIGGER ALG");
goto fail;
}
#endif /* #ifdef IPNET_USE_NAT_TRIGGER_ALG */
/* Enable NAT */
if(ipnet_nat_internal_enable() != 0)
{
errval = IPCOM_ERR_FAILED;
goto fail;
}
#ifdef IPNET_DEBUG
ipnet_timeout_to_string((Ipnet_timeout_handler) ipnet_nat_mapping_timeout,
(Ipnet_timeout_to_string_f) ipnet_nat_mapping_to_string);
#endif
return IPCOM_SUCCESS;
fail:
if(ipnet->nat.hash_out_port != IP_NULL)
ipcom_hash_delete(ipnet->nat.hash_out_port);
if(ipnet->nat.hash_out_noport != IP_NULL)
ipcom_hash_delete(ipnet->nat.hash_out_noport);
if(ipnet->nat.hash_in_port != IP_NULL)
ipcom_hash_delete(ipnet->nat.hash_in_port);
if(ipnet->nat.hash_in_noport != IP_NULL)
ipcom_hash_delete(ipnet->nat.hash_in_noport);
return errval;
}
/*
*===========================================================================
* ipnet_nat_input_hook
*===========================================================================
* Description: Input hook for NAT.
* Parameters: pktp - pointer to pointer to packet.
* param - pointer to the output parameters.
* Returns: IPNET_NAT_PT_TRANSLATED
* IPNET_NAT_TRANSLATED
* IPNET_NAT_NOMATCH
* IPNET_NAT_INVALID
* IPNET_NAT_DROP
*/
IP_GLOBAL Ip_s32
ipnet_nat_input_hook(Ipcom_pkt **pktp)
{
Ip_s32 ret;
/* Check that nat is enabled and that least one rule is defined */
if(ipnet->nat.inner.open == 0 || ipnet->nat.head_rule.size == 0)
return IPNET_NAT_NOMATCH;
ret = ipnet_nat_loop_rules(IP_TRUE, pktp);
return ret;
}
/*
*===========================================================================
* ipnet_nat_output_hook
*===========================================================================
* Description: Output hook for NAT.
* Parameters: pktp - pointer to pointer to packet.
* and thus only match against existing mappings.
* Returns: IPNET_NAT_PT_TRANSLATED
* IPNET_NAT_TRANSLATED
* IPNET_NAT_NOMATCH
* IPNET_NAT_INVALID
* IPNET_NAT_DROP
*/
IP_GLOBAL Ip_s32
ipnet_nat_output_hook(Ipcom_pkt **pktp)
{
Ip_s32 ret;
/* Check that nat is enabled and that least one rule is defined */
if(ipnet->nat.inner.open == 0 || ipnet->nat.head_rule.size == 0)
return IPNET_NAT_NOMATCH;
(*pktp)->start = (int)((*pktp)->ipstart + (((*pktp)->data[(*pktp)->ipstart] & 0x0F) << 2));
ret = ipnet_nat_loop_rules(IP_FALSE, pktp);
(*pktp)->start = (*pktp)->ipstart;
return ret;
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipnet_nat_proxy_get_time
*===========================================================================
* Description: Get elapsed time since boot in seconds.
* Parameters: none.
* Returns: The number of seconds since boot.
*/
IP_PUBLIC Ip_u32
ipnet_nat_proxy_get_time(void)
{
return ipnet_sec_now();
}
/*
*===========================================================================
* ipnet_nat_proxy_timeout_schedule
*===========================================================================
* Description: Schedules a function that will be called after sec
* seconds.
* Parameters: sec - seconds until the timeout will occur.
* handler - the timeout handler or IP_NULL if no action should
* be done when the timer expires.
* cookie - the user defined argument to the handler.
* ptmo - the location where the timeout handler should be stored
* Returns: 0 = success, <0 = error.
*/
IP_PUBLIC int
ipnet_nat_proxy_timeout_schedule(Ip_u32 sec,
Ipnet_nat_proxy_timeout_handler handler,
void *cookie,
void **ptmo)
{
return ipnet_timeout_schedule(sec * 1000, handler, cookie, (struct Ipnet_timeout_struct **)ptmo);
}
/*
*===========================================================================
* ipnet_nat_proxy_timeout_cancel
*===========================================================================
* Description: Cancels a scheduled timeout.
* Parameters: tmo - the timeout event to remove.
* Returns: nothing.
*/
IP_PUBLIC void
ipnet_nat_proxy_timeout_cancel(void *tmo)
{
ip_assert(tmo != IP_NULL);
ipnet_timeout_cancel(tmo);
}
/*
*===========================================================================
* ipnet_nat_proxy_timeout_reschedule
*===========================================================================
* Description: Reschedules a running timer with a new timeout.
* Parameters: sec - seconds until the timeout will occur.
* handler - the timeout handler or IP_NULL if no action should
* be done when the timer expires.
* cookie - the user defined argument to the handler.
* ptmo - the location where the timeout handler should be stored
* Returns: 0 = success, <0 = error.
*/
IP_PUBLIC int
ipnet_nat_proxy_timeout_reschedule(Ip_u32 sec,
Ipnet_nat_proxy_timeout_handler handler,
void *cookie,
void **ptmo)
{
ipnet_nat_proxy_timeout_cancel(*ptmo);
return ipnet_nat_proxy_timeout_schedule(sec, handler, cookie, ptmo);
}
/*
*===========================================================================
* ipnet_nat_proxy_add_mapping
*===========================================================================
* Description: This function adds a mapping between a host on the private
* side of the NAT and a host on the public side of the NAT.
* This is typically used to open a port through the NAT which
* is typically closed for incoming connections. It can
* also be used to open an outgoing path through the NAT
* if there is no matching rule.
*
* The 'proxy_tuple' parameter specifies the private and public
* addresses and ports as well as the protocol. If the source
* port of the connecting host is unknown it can be set to zero
* to allow any source port. If the source address of the
* connecting host is unknown it can be set to zero to allow
* any host. Once the mapping has been hit it can only be used
* by that specific host. Wildcarding both the private and public
* side is not allowed. The protocol can be any protocol but ICMP.
* For non TCP/UDP protcols the ports must be set to zero.
* Additionally, the source address can be set to zero to allow any
* host to connect.
*
* The source port will be the same on the private host and the NAT gateway
* unless 'use_napt' is IP_TRUE which has the effect that a new port is
* automatically allocated and port translation will take place.
*
* Optionally packets matching the mapping can be configured to call
* an application proxy if the 'proxy_func' and 'proxy_cookie' parameters
* are set.
*
* Parameters: proxy_tuple - pointer to the proxy tuple.
* timeout - timeout until the port is closed if no packets
* arrive (0 means that default values will be applied)
* parent - Pointer to the NAT mapping that caused the call
* to the proxy function. Must be the same pointer
* value as received in param->mapping.
* use_napt - IP_TRUE to use port translation.
* use_inbound - IP_TRUE to indicate that session will start inbound.
* proxy_func - Proxy function (optional).
* proxy_cookie - Proxy cookie (optional).
*
* Returns: The NAT port or -1 if failed
*/
IP_PUBLIC int
ipnet_nat_proxy_add_mapping(Ipnet_nat_proxy_tuple *proxy_tuple,
Ip_u32 timeout,
void *parent,
Ip_bool use_napt,
Ip_bool use_inbound,
Ipnet_nat_proxy_func proxy_func,
void *proxy_cookie)
{
Ipnet_nat_mapping *nat_mapping;
Ipnet_nat_tuple nat_tuple;
#if IPCOM_SYSLOG_ENABLE(1)
char protostr[16];
#endif
/* Check protocol */
if (proxy_tuple->protocol == IP_IPPROTO_ICMP)
{
IPCOM_LOG1(ERR, "ipnet_nat_proxy_add_mapping() :: invalid protocol (%d)", proxy_tuple->protocol);
return -1;
}
/* Check ports and addresses for sanity */
if (proxy_tuple->protocol == IP_IPPROTO_TCP || proxy_tuple->protocol == IP_IPPROTO_UDP)
{
/* Both ports must not be zero */
if (proxy_tuple->private_port == 0 && proxy_tuple->public_port == 0)
{
IPCOM_LOG3(ERR, "ipnet_nat_proxy_add_mapping() :: invalid private port/public port (%d/%d) for protocol=%s",
proxy_tuple->private_port,
proxy_tuple->public_port,
ipnet_nat_protocol_name(proxy_tuple->protocol, protostr, sizeof(protostr)));
return -1;
}
}
else
{
/* Both ports must be zero */
if (proxy_tuple->private_port != 0 || proxy_tuple->public_port != 0)
{
IPCOM_LOG3(ERR, "ipnet_nat_proxy_add_mapping() :: invalid private port/public port (%d/%d) for protocol=%s",
proxy_tuple->private_port,
proxy_tuple->public_port,
ipnet_nat_protocol_name(proxy_tuple->protocol, protostr, sizeof(protostr)));
return -1;
}
}
/* Both addresses must not be zero */
if (proxy_tuple->private_addr == 0 && proxy_tuple->public_addr == 0)
{
IPCOM_LOG3(ERR, "ipnet_nat_proxy_add_mapping() :: invalid private address/public address (0x%08x/0x%08x) for protocol=%s",
proxy_tuple->private_addr,
proxy_tuple->public_addr,
ipnet_nat_protocol_name(proxy_tuple->protocol, protostr, sizeof(protostr)));
return -1;
}
if (proxy_tuple->private_port != 0 && proxy_tuple->private_addr == 0)
{
IPCOM_LOG3(ERR, "ipnet_nat_proxy_add_mapping() :: invalid private port/private address (%d/0x%08x) for protocol=%s",
proxy_tuple->private_port,
proxy_tuple->private_addr,
ipnet_nat_protocol_name(proxy_tuple->protocol, protostr, sizeof(protostr)));
return -1;
}
if (proxy_tuple->public_port != 0 && proxy_tuple->public_addr == 0)
{
IPCOM_LOG3(ERR, "ipnet_nat_proxy_add_mapping() :: invalid public port/public address (%d/0x%08x) for protocol=%s",
proxy_tuple->public_port,
proxy_tuple->public_addr,
ipnet_nat_protocol_name(proxy_tuple->protocol, protostr, sizeof(protostr)));
return -1;
}
/* Prepare tuple */
ipcom_memset(&nat_tuple, 0, sizeof(nat_tuple));
nat_tuple.protocol = proxy_tuple->protocol;
nat_tuple.src_port_n = ip_htons(proxy_tuple->private_port);
nat_tuple.dst_port_n = ip_htons(proxy_tuple->public_port);
nat_tuple.src_n = ip_htonl(proxy_tuple->private_addr);
nat_tuple.dst_n = ip_htonl(proxy_tuple->public_addr);
#ifdef IPNET_USE_NAT_PT
ipcom_memcpy(&nat_tuple.pt_dst, &((Ipnet_nat_mapping *)parent)->inner.tuple.pt_dst, sizeof(nat_tuple.pt_dst));
ipcom_memcpy(&nat_tuple.pt_src, &((Ipnet_nat_mapping *)parent)->inner.tuple.pt_src, sizeof(nat_tuple.pt_src));
nat_tuple.pt_src.in6.addr32[3] = ip_htonl(proxy_tuple->private_addr);
#endif
/* Try to find the mapping */
nat_mapping = ipnet_nat_find_mapping(IP_FALSE, IP_NULL, &nat_tuple);
if (nat_mapping != IP_NULL)
{
/* Mapping already added - just reset the timeout */
IPCOM_LOG0(DEBUG, "ipnet_nat_proxy_add_mapping() :: mapping already added - reset timeout only");
ipnet_nat_reset_mapping_timeout(nat_mapping);
return (int)ip_ntohs(nat_mapping->inner.nat_port_n);
}
/* Add the mapping */
nat_mapping = ipnet_nat_add_mapping(use_inbound,
((Ipnet_nat_mapping *)parent)->rule,
&nat_tuple,
timeout,
(Ipnet_nat_mapping *)parent,
use_napt,
proxy_func,
proxy_cookie);
if(nat_mapping == IP_NULL)
{
IPCOM_LOG0(ERR, "ipnet_nat_proxy_add_mapping() :: Failed");
return -1;
}
return (int)ip_ntohs(nat_mapping->inner.nat_port_n);
}
/*
*===========================================================================
* ipnet_nat_proxy_set_mapping_timeout
*===========================================================================
* Description: Sets the timeout of a mapping.
* Parameters: sec - Seconds until the mapping will timeout.
* mapping - Pointer to the NAT mapping that caused the call
* to the proxy function. Must be the same pointer
* value as received in param->mapping.
* Returns: nothing.
*/
IP_PUBLIC void
ipnet_nat_proxy_set_mapping_timeout(Ip_u32 sec, void *mapping)
{
Ipnet_nat_mapping *nat_mapping;
nat_mapping = (Ipnet_nat_mapping *)mapping;
nat_mapping->inner.tmo_int_sec = sec;
ipnet_nat_reset_mapping_timeout(nat_mapping);
}
/*
*===========================================================================
* ipnet_nat_add_proxy
*===========================================================================
* Description: Adds a proxy (ALG) to the NAT.
* Parameters: label - ascii string identifier
* proto - The IP protocol the proxy applies to. Either a
* protocol name or a numerical string is allowed.
* func - Pointer to the ALG function
* cookie - A cookie that will be supplied in the call to
* the proxy function. The memory location referred to
* by the cookie must be valid as long as the proxy
* has not been removed.
* Returns: IPCOM_SUCCESS - Proxy successfully added.
* IPCOM_ERR_INVALID_ARG - Invalid arguments.
* IPCOM_ERR_NO_MEMORY - Out of memory.
* IPCOM_ERR_DUPLICATE - Proxy already added.
* IPCOM_ERR_REACHED_MAX - Too many proxies.
* IPCOM_ERR_FAILED - General failure.
*/
IP_PUBLIC Ip_err
ipnet_nat_add_proxy(const char *label, const char *proto,
Ipnet_nat_proxy_func func, void *cookie)
{
Ipnet_nat_proxy *nat_proxy;
long protocol;
char *suff;
void *suspend_handle;
Ip_err err = IPCOM_SUCCESS;
if (label == IP_NULL || proto == IP_NULL || func == IP_NULL)
{
err = IPCOM_ERR_INVALID_ARG;
IPCOM_LOG1(ERR, "ipnet_nat_add_proxy() :: Failed (%s)", ipcom_err_string(err));
return err;
}
if(ipnet->nat.head_proxy.size >= IPNET_NAT_MAX_PROXIES)
{
err = IPCOM_ERR_REACHED_MAX;
IPCOM_LOG1(ERR, "ipnet_nat_add_proxy() :: Failed (%s)", ipcom_err_string(err));
return err;
}
protocol = ipnet_nat_protocol_number(proto);
if (protocol < 0)
{
protocol = ipcom_strtol(proto, &suff, 10);
if(protocol < 0 || protocol > 255 || proto == suff || *suff != '\0')
{
err = IPCOM_ERR_FAILED;
IPCOM_LOG3(ERR, "ipnet_nat_add_proxy() :: Failed (%s): %s %s",
ipcom_err_string(err), label, proto);
return err;
}
}
if (protocol == IP_IPPROTO_ICMP || protocol == IP_IPPROTO_ICMPV6)
{
err = IPCOM_ERR_INVALID_ARG;
IPCOM_LOG1(ERR, "ipnet_nat_add_proxy() :: Failed (Invalid protocol): %s", proto);
return err;
}
/* Check for duplicates */
suspend_handle = ipnet_suspend_stack();
if (suspend_handle == IP_NULL)
{
err = IPCOM_ERR_FAILED;
IPCOM_LOG0(ERR, "ipnet_nat_add_proxy() :: Failed (Could not suspend stack)");
return err;
}
for (nat_proxy = IPCOM_LIST_FIRST(&ipnet->nat.head_proxy);
nat_proxy != IP_NULL;
nat_proxy = IPCOM_LIST_NEXT(&nat_proxy->list_proxy))
{
if (ipcom_strcmp(nat_proxy->inner.label, label) == 0
&& nat_proxy->inner.protocol == (Ip_u8)protocol)
{
err = IPCOM_ERR_DUPLICATE;
IPCOM_LOG3(ERR, "ipnet_nat_add_proxy() :: Failed (%s): %s %s",
ipcom_err_string(err), label, proto);
ipnet_resume_stack(suspend_handle);
return err;
}
}
/* Allocate space for the proxy and insert in list */
nat_proxy = ipcom_malloc(sizeof(*nat_proxy));
if(nat_proxy == IP_NULL)
{
err = IPCOM_ERR_NO_MEMORY;
IPCOM_LOG1(ERR, "ipnet_nat_add_proxy() :: Failed (%s)", ipcom_err_string(err));
ipnet_resume_stack(suspend_handle);
return err;
}
ipcom_memset(nat_proxy, 0, sizeof(*nat_proxy));
ipcom_strncpy(nat_proxy->inner.label, label, sizeof(nat_proxy->inner.label)-1);
nat_proxy->inner.protocol = (Ip_u8)protocol;
nat_proxy->func = func;
nat_proxy->cookie = cookie;
ipcom_list_insert_last(&ipnet->nat.head_proxy, &nat_proxy->list_proxy);
ipnet_resume_stack(suspend_handle);
return err;
}
/*
*===========================================================================
* ipnet_nat_remove_proxy
*===========================================================================
* Description: Removes a proxy (ALG) from the NAT
* Parameters: label - ascii string identifier
* proto - The IP protocol the proxy applies to. Either
* a protocol name or a numerical string is allowed.
* Returns: IPCOM_SUCCESS - Proxy successfully added.
* IPCOM_ERR_INVALID_ARG - Invalid arguments.
* IPCOM_ERR_NOT_FOUND - Did not find proxy.
* IPCOM_ERR_FAILED - General failure.
*/
IP_PUBLIC Ip_err
ipnet_nat_remove_proxy(const char *label, const char *proto)
{
Ipnet_nat_proxy *nat_proxy;
long protocol;
char *suff;
void *suspend_handle;
Ip_err err = IPCOM_SUCCESS;
if (label == IP_NULL || proto == IP_NULL)
{
err = IPCOM_ERR_INVALID_ARG;
IPCOM_LOG1(ERR, "ipnet_nat_remove_proxy() :: Failed (%s)", ipcom_err_string(err));
return err;
}
/* Convert protocol */
protocol = ipnet_nat_protocol_number(proto);
if (protocol < 0)
{
protocol = ipcom_strtol(proto, &suff, 10);
if(protocol < 0 || protocol > 255 || proto == suff || *suff != '\0')
{
err = IPCOM_ERR_FAILED;
IPCOM_LOG3(ERR, "ipnet_nat_remove_proxy() :: Failed (%s): %s %s",
ipcom_err_string(err), label, proto);
return err;
}
}
/* Locate proxy */
suspend_handle = ipnet_suspend_stack();
if (suspend_handle == IP_NULL)
{
err = IPCOM_ERR_FAILED;
IPCOM_LOG0(ERR, "ipnet_nat_remove_proxy() :: Failed (Could not suspend stack)");
return err;
}
for (nat_proxy = IPCOM_LIST_FIRST(&ipnet->nat.head_proxy);
nat_proxy != IP_NULL;
nat_proxy = IPCOM_LIST_NEXT(&nat_proxy->list_proxy))
{
if (ipcom_strcmp(nat_proxy->inner.label, label) == 0
&& nat_proxy->inner.protocol == (Ip_u8)protocol)
{
break;
}
}
if (nat_proxy == IP_NULL)
{
err = IPCOM_ERR_NOT_FOUND;
IPCOM_LOG3(ERR, "ipnet_nat_remove_proxy() :: Failed (%s): %s %s",
ipcom_err_string(err), label, proto);
ipnet_resume_stack(suspend_handle);
return err;
}
/* Remove proxy and free memeory */
ipcom_list_remove(&nat_proxy->list_proxy);
ipcom_free(nat_proxy);
ipnet_resume_stack(suspend_handle);
return err;
}
#else
int ipnet_nat_empty_file;
#endif /* IPNET_USE_NAT */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/