1161 lines
43 KiB
C
1161 lines
43 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipnet_nat_pt.c,v $ $Revision: 1.23.10.1 $
|
|
* $Source: /home/interpeak/CVSRoot/ipnet2/src/ipnet_nat_pt.c,v $
|
|
* $Author: ulf $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Markus Carlstedt <markus@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* The NAT-PT implementation. Some NAT-PT code is common with NAT and is
|
|
* in the file ipnet_nat.c.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
#include "ipnet_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPNET_USE_NAT_PT
|
|
|
|
#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
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ICMPv6 error type
|
|
*===========================================================================
|
|
*/
|
|
#define IPNET_NAT_IS_ICMP6_ERROR(type) (type == IPNET_ICMP6_DST_UNREACH || \
|
|
type == IPNET_ICMP6_TIME_EXCEEDED || \
|
|
type == IPNET_ICMP6_PARAM_PROB)
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* Ipnet_nat_pt_pseudo_hdr
|
|
*===========================================================================
|
|
* Pseudo header for IPv6 used in UDP/TCP checksum calculations.
|
|
*
|
|
*/
|
|
typedef struct
|
|
{
|
|
struct Ip_in6_addr src_n;
|
|
struct Ip_in6_addr dst_n;
|
|
Ip_u32 length_n;
|
|
Ip_u8 mbz[3];
|
|
Ip_u8 protocol;
|
|
}
|
|
Ipnet_nat_pt_pseudo_hdr;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_scan_headers
|
|
*===========================================================================
|
|
* Description: Scans extension headers in an IPv6 packet.
|
|
* Parameters: pkt - pointer to the packet.
|
|
* nat_tuple - pointer to the NAT tuple (can be IP_NULL).
|
|
* proto - pointer to the returned protocol (can be IP_NULL).
|
|
* Returns: number of bytes of extension headers or -1 if failed.
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipnet_nat_pt_scan_headers(Ipcom_pkt *pkt, Ipnet_nat_tuple *nat_tuple, Ip_u8 *proto)
|
|
{
|
|
Ipnet_pkt_ip6 *ip6;
|
|
Ip_pkt_ip6_frag *frag;
|
|
Ip_u8 nxt;
|
|
int start, end;
|
|
|
|
ip6 = (Ipnet_pkt_ip6 *)&pkt->data[pkt->ipstart];
|
|
nxt = ip6->nxt;
|
|
start = pkt->ipstart + IPCOM_IP6_HEADER_LENGTH;
|
|
end = pkt->end;
|
|
while(nxt == IP_IPPROTO_HOPOPTS ||
|
|
nxt == IP_IPPROTO_DSTOPTS ||
|
|
nxt == IP_IPPROTO_ROUTING ||
|
|
nxt == IP_IPPROTO_ESP ||
|
|
nxt == IP_IPPROTO_AH ||
|
|
nxt == IP_IPPROTO_FRAGMENT)
|
|
{
|
|
/* Check that there are at least 2 bytes for the next header and length field */
|
|
if (end - start < 2)
|
|
return -1; /* Too short */
|
|
if(nxt == IP_IPPROTO_FRAGMENT)
|
|
{
|
|
/* Check that there are enough bytes for the fragment header */
|
|
if (end - start < (int)sizeof(Ip_pkt_ip6_frag))
|
|
return -1; /* Too short */
|
|
|
|
if (nat_tuple != IP_NULL)
|
|
{
|
|
/* Get fragment offset and flags */
|
|
frag = (Ip_pkt_ip6_frag *)&pkt->data[start];
|
|
nat_tuple->fragoff = (Ip_u16)((ip_ntohs(frag->ip6f_offlg) & 0xfff8)>>3);
|
|
nat_tuple->fragmf = (Ip_u8)(ip_ntohs(frag->ip6f_offlg) & 0x0001);
|
|
nat_tuple->fragid = IP_GET_32ON16((void *)&frag->ip6f_ident);
|
|
nat_tuple->fragid = ip_ntohl(frag->ip6f_ident);
|
|
}
|
|
}
|
|
nxt = pkt->data[start];
|
|
start += (pkt->data[start+1]+1)<<3;
|
|
/* Check that there are enough bytes for the extension header */
|
|
if (end - start < 0)
|
|
return -1; /* Too short */
|
|
}
|
|
|
|
if (proto != IP_NULL)
|
|
*proto = nxt;
|
|
return start - (pkt->ipstart + IPCOM_IP6_HEADER_LENGTH);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_get_tuple
|
|
*===========================================================================
|
|
* Description: Populates a NAT tuple structure from an IPv6 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_pt_get_tuple(Ipcom_pkt *pkt, Ipnet_nat_tuple *nat_tuple)
|
|
{
|
|
Ipnet_pkt_ip6 *ip6;
|
|
int length, start, end;
|
|
|
|
ip_assert(pkt);
|
|
|
|
ipcom_memset(nat_tuple, 0, sizeof(*nat_tuple));
|
|
|
|
ip6 = (Ipnet_pkt_ip6 *)&pkt->data[pkt->ipstart];
|
|
|
|
/* Family */
|
|
nat_tuple->family = IP_AF_INET6;
|
|
|
|
/* Get tos */
|
|
nat_tuple->tos = IPNET_IP6_GET_CLASS(ip6);
|
|
|
|
/* Get ttl */
|
|
nat_tuple->ttl = ip6->hlim;
|
|
|
|
/* Get addr */
|
|
ipcom_memcpy(&nat_tuple->pt_src, &ip6->src, sizeof(ip6->src));
|
|
ipcom_memcpy(&nat_tuple->pt_dst, &ip6->dst, sizeof(ip6->dst));
|
|
nat_tuple->src_n = nat_tuple->pt_src.in6.addr32[3];
|
|
nat_tuple->dst_n = nat_tuple->pt_dst.in6.addr32[3];
|
|
|
|
/* Scan extension headers */
|
|
length = ipnet_nat_pt_scan_headers(pkt, nat_tuple, &nat_tuple->protocol);
|
|
if (length < 0)
|
|
return -1;
|
|
start = pkt->ipstart + IPCOM_IP6_HEADER_LENGTH + length;
|
|
end = pkt->end;
|
|
|
|
/* Check transport length */
|
|
if (nat_tuple->fragoff == 0)
|
|
{
|
|
if (nat_tuple->protocol == IP_IPPROTO_ICMPV6)
|
|
{
|
|
Ipnet_pkt_icmp *icmp;
|
|
|
|
if(end - start < IPCOM_ICMP_HEADER_LENGTH)
|
|
return -1; /* Too short */
|
|
icmp = (Ipnet_pkt_icmp *)&pkt->data[start];
|
|
nat_tuple->icmp_type = icmp->type;
|
|
if (icmp->type == IPNET_ICMP6_ECHO_REQUEST)
|
|
{
|
|
if(end - start < IPNET_NAT_ICMP_QUERY_LENGTH)
|
|
return -1; /* Too short */
|
|
nat_tuple->icmp_id_n = icmp->data.echo.id;
|
|
}
|
|
else if (IPNET_NAT_IS_ICMP6_ERROR(icmp->type))
|
|
{
|
|
if(end - start < 8 + IPCOM_IP6_HEADER_LENGTH + 8)
|
|
return -1; /* Too short */
|
|
}
|
|
}
|
|
else if(nat_tuple->protocol == IP_IPPROTO_UDP)
|
|
{
|
|
Ipcom_udp_header *udp;
|
|
|
|
udp = (Ipcom_udp_header *)&pkt->data[start];
|
|
if(end - 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[start];
|
|
if(end - 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;
|
|
}
|
|
}
|
|
|
|
/* Move start to after extension headers */
|
|
pkt->start = start;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_icmp_checksum
|
|
*===========================================================================
|
|
* Description: Calculate ICMP checksum for NAT-PT
|
|
* Parameters: incoming - IP_TRUE for incoming packets.
|
|
* pkt - pointer to the packet.
|
|
* nat_mapping - pointer to NAT mapping.
|
|
* len - length of packet excluding ip header.
|
|
* Returns: nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_nat_pt_icmp_checksum(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping,
|
|
Ipnet_nat_tuple *nat_tuple, int len)
|
|
{
|
|
Ipnet_pkt_icmp *icmp;
|
|
Ipnet_nat_pt_pseudo_hdr nat_pt_pseudo_hdr;
|
|
|
|
icmp = (Ipnet_pkt_icmp *)&pkt->data[pkt->start];
|
|
if(incoming == IP_TRUE)
|
|
{
|
|
/* Ipv6 pseudo header */
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.src_n, &nat_mapping->inner.tuple.pt_dst, sizeof(nat_pt_pseudo_hdr.src_n));
|
|
nat_pt_pseudo_hdr.src_n.in6.addr32[3] = nat_tuple->src_n;
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_src, sizeof(nat_pt_pseudo_hdr.dst_n));
|
|
nat_pt_pseudo_hdr.length_n = ip_htonl(len);
|
|
ipcom_memset(nat_pt_pseudo_hdr.mbz, 0, sizeof(nat_pt_pseudo_hdr.mbz));
|
|
nat_pt_pseudo_hdr.protocol = IP_IPPROTO_ICMPV6;
|
|
|
|
icmp->cksum = 0;
|
|
icmp->cksum = (Ip_u16)(~ipcom_in_checksum(&nat_pt_pseudo_hdr, sizeof(nat_pt_pseudo_hdr)));
|
|
icmp->cksum = ipcom_in_checksum_pkt(pkt, 0);
|
|
}
|
|
else
|
|
{
|
|
icmp->cksum = 0;
|
|
icmp->cksum = ipcom_in_checksum_pkt(pkt, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_udp_checksum
|
|
*===========================================================================
|
|
* Description: Calculate UDP checksum for NAT-PT
|
|
* Parameters: incoming - IP_TRUE for incoming packets.
|
|
* pkt - pointer to the packet.
|
|
* nat_mapping - pointer to NAT mapping.
|
|
* len - length of packet excluding ip header.
|
|
* Returns: nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_nat_pt_udp_checksum(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping,
|
|
Ipnet_nat_tuple *nat_tuple, int len)
|
|
{
|
|
Ipcom_udp_header *udp;
|
|
Ipnet_nat_pseudo_hdr nat_pseudo_hdr;
|
|
Ipnet_nat_pt_pseudo_hdr nat_pt_pseudo_hdr;
|
|
|
|
udp = (Ipcom_udp_header *)&pkt->data[pkt->start];
|
|
if(incoming == IP_TRUE)
|
|
{
|
|
/* Ipv6 pseudo header */
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.src_n, &nat_mapping->inner.tuple.pt_dst, sizeof(nat_pt_pseudo_hdr.src_n));
|
|
nat_pt_pseudo_hdr.src_n.in6.addr32[3] = nat_tuple->src_n;
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_src, sizeof(nat_pt_pseudo_hdr.dst_n));
|
|
nat_pt_pseudo_hdr.length_n = ip_htonl(len);
|
|
ipcom_memset(nat_pt_pseudo_hdr.mbz, 0, sizeof(nat_pt_pseudo_hdr.mbz));
|
|
nat_pt_pseudo_hdr.protocol = IP_IPPROTO_UDP;
|
|
|
|
udp->checksum = 0;
|
|
udp->checksum = (Ip_u16)(~ipcom_in_checksum(&nat_pt_pseudo_hdr, sizeof(nat_pt_pseudo_hdr)));
|
|
udp->checksum = ipcom_in_checksum_pkt(pkt, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Ipv4 pseudo header */
|
|
ipcom_memcpy(&nat_pseudo_hdr.src_n, &nat_mapping->inner.nat_addr_n, sizeof(nat_pseudo_hdr.src_n));
|
|
ipcom_memcpy(&nat_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_dst.in6.addr32[3], sizeof(nat_pseudo_hdr.dst_n));
|
|
nat_pseudo_hdr.length_n = ip_htons((short)len);
|
|
ipcom_memset(&nat_pseudo_hdr.mbz, 0, sizeof(nat_pseudo_hdr.mbz));
|
|
nat_pseudo_hdr.protocol = IP_IPPROTO_UDP;
|
|
|
|
udp->checksum = 0;
|
|
udp->checksum = (Ip_u16)(~ipcom_in_checksum(&nat_pseudo_hdr, sizeof(nat_pseudo_hdr)));
|
|
udp->checksum = ipcom_in_checksum_pkt(pkt, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_tcp_checksum
|
|
*===========================================================================
|
|
* Description: Calculate TCP checksum for NAT-PT
|
|
* Parameters: incoming - IP_TRUE for incoming packets.
|
|
* pkt - pointer to the packet.
|
|
* nat_mapping - pointer to NAT mapping.
|
|
* len - length of packet excluding ip header.
|
|
* Returns: nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_nat_pt_tcp_checksum(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping,
|
|
Ipnet_nat_tuple *nat_tuple, int len)
|
|
{
|
|
Ipcom_tcp_header *tcp;
|
|
Ipnet_nat_pseudo_hdr nat_pseudo_hdr;
|
|
Ipnet_nat_pt_pseudo_hdr nat_pt_pseudo_hdr;
|
|
|
|
tcp = (Ipcom_tcp_header *)&pkt->data[pkt->start];
|
|
if(incoming == IP_TRUE)
|
|
{
|
|
/* Ipv6 pseudo header */
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.src_n, &nat_mapping->inner.tuple.pt_dst, sizeof(nat_pt_pseudo_hdr.src_n));
|
|
nat_pt_pseudo_hdr.src_n.in6.addr32[3] = nat_tuple->src_n;
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_src, sizeof(nat_pt_pseudo_hdr.dst_n));
|
|
nat_pt_pseudo_hdr.length_n = ip_htonl(len);
|
|
ipcom_memset(nat_pt_pseudo_hdr.mbz, 0, sizeof(nat_pt_pseudo_hdr.mbz));
|
|
nat_pt_pseudo_hdr.protocol = IP_IPPROTO_TCP;
|
|
|
|
tcp->checksum = 0;
|
|
tcp->checksum = (Ip_u16)(~ipcom_in_checksum(&nat_pt_pseudo_hdr, sizeof(nat_pt_pseudo_hdr)));
|
|
tcp->checksum = ipcom_in_checksum_pkt(pkt, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Ipv4 pseudo header */
|
|
ipcom_memcpy(&nat_pseudo_hdr.src_n, &nat_mapping->inner.nat_addr_n, sizeof(nat_pseudo_hdr.src_n));
|
|
ipcom_memcpy(&nat_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_dst.in6.addr32[3], sizeof(nat_pseudo_hdr.dst_n));
|
|
nat_pseudo_hdr.length_n = ip_htons((short)len);
|
|
ipcom_memset(&nat_pseudo_hdr.mbz, 0, sizeof(nat_pseudo_hdr.mbz));
|
|
nat_pseudo_hdr.protocol = IP_IPPROTO_TCP;
|
|
|
|
tcp->checksum = 0;
|
|
tcp->checksum = (Ip_u16)(~ipcom_in_checksum(&nat_pseudo_hdr, sizeof(nat_pseudo_hdr)));
|
|
tcp->checksum = ipcom_in_checksum_pkt(pkt, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_embedded_checksum_update
|
|
*===========================================================================
|
|
* Description: Smart update of TCP/UDP/ICMP checksum for NAT-PT.
|
|
* Parameters: nat_mapping - pointer to NAT mapping.
|
|
* len - length of packet excluding ip header.
|
|
* protocol - packet protocol number.
|
|
* checksum - pointer to the calculated checksum.
|
|
* Returns: nothing.
|
|
*/
|
|
IP_STATIC void
|
|
ipnet_nat_pt_embedded_checksum_update(Ipnet_nat_mapping *nat_mapping,
|
|
int len,
|
|
Ip_u8 protocol,
|
|
Ip_u16 *checksum)
|
|
|
|
{
|
|
Ipnet_nat_pseudo_hdr nat_pseudo_hdr;
|
|
Ipnet_nat_pt_pseudo_hdr nat_pt_pseudo_hdr;
|
|
|
|
if(protocol == IP_IPPROTO_TCP || protocol == IP_IPPROTO_UDP)
|
|
{
|
|
/* Remove IPv4 pseudo header */
|
|
ipcom_memcpy(&nat_pseudo_hdr.src_n, &nat_mapping->inner.nat_addr_n, sizeof(nat_pseudo_hdr.src_n));
|
|
ipcom_memcpy(&nat_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.dst_n, sizeof(nat_pseudo_hdr.dst_n));
|
|
nat_pseudo_hdr.length_n = ip_htons((short)len);
|
|
ipcom_memset(&nat_pseudo_hdr.mbz, 0, sizeof(nat_pseudo_hdr.mbz));
|
|
nat_pseudo_hdr.protocol = protocol;
|
|
|
|
/* Add IPv6 pseudo header */
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.src_n, &nat_mapping->inner.tuple.pt_src, sizeof(nat_pt_pseudo_hdr.src_n));
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_dst, sizeof(nat_pt_pseudo_hdr.dst_n));
|
|
nat_pt_pseudo_hdr.length_n = ip_htonl(len);
|
|
ipcom_memset(nat_pt_pseudo_hdr.mbz, 0, sizeof(nat_pt_pseudo_hdr.mbz));
|
|
nat_pt_pseudo_hdr.protocol = protocol;
|
|
|
|
/* Update the checksum */
|
|
ipnet_nat_checksum_update((Ip_u8 *)checksum,
|
|
(Ip_u8 *)&nat_pseudo_hdr,
|
|
sizeof(nat_pseudo_hdr),
|
|
(Ip_u8 *)&nat_pt_pseudo_hdr,
|
|
sizeof(nat_pt_pseudo_hdr));
|
|
}
|
|
else if(protocol == IP_IPPROTO_ICMP)
|
|
{
|
|
/* Add IPv6 pseudo header */
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.src_n, &nat_mapping->inner.tuple.pt_src, sizeof(nat_pt_pseudo_hdr.src_n));
|
|
ipcom_memcpy(&nat_pt_pseudo_hdr.dst_n, &nat_mapping->inner.tuple.pt_dst, sizeof(nat_pt_pseudo_hdr.dst_n));
|
|
nat_pt_pseudo_hdr.length_n = ip_htonl(len);
|
|
ipcom_memset(nat_pt_pseudo_hdr.mbz, 0, sizeof(nat_pt_pseudo_hdr.mbz));
|
|
nat_pt_pseudo_hdr.protocol = IP_IPPROTO_ICMPV6;
|
|
ipnet_nat_checksum_update((Ip_u8 *)checksum,
|
|
IP_NULL,
|
|
0,
|
|
(Ip_u8 *)&nat_pt_pseudo_hdr,
|
|
sizeof(nat_pt_pseudo_hdr));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_translate_embedded_icmp
|
|
*===========================================================================
|
|
* Description: Translate an embedded icmp error message for NAT-PT
|
|
* Parameters: incoming - IP_TRUE for incoming packets.
|
|
* len - pointer to the length of the packet.
|
|
* pkt - pointer to the packet.
|
|
* nat_mapping - pointer to the NAT mapping.
|
|
* Returns: 1 = Translated.
|
|
* 0 = No translation.
|
|
* -1 = Drop
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipnet_nat_pt_translate_embedded_icmp(Ip_bool incoming, int *len, Ipcom_pkt **pktp, Ipnet_nat_mapping *nat_mapping)
|
|
{
|
|
Ipcom_pkt *new_pkt;
|
|
Ipnet_pkt_icmp *icmp;
|
|
Ipnet_pkt_icmp *icmp_embed;
|
|
Ipcom_ip_header *iph_embed;
|
|
Ipnet_pkt_ip6 *ip6_embed;
|
|
Ipcom_udp_header *udp_embed;
|
|
Ipcom_tcp_header *tcp_embed;
|
|
Ip_u16 hlen_embed, oldsport;
|
|
Ip_u8 ttl, tos, oldtypecode[2], newtypecode[2];
|
|
int plen_embed, diff;
|
|
Ipcom_pkt *pkt_tail = IP_NULL;
|
|
|
|
icmp = (Ipnet_pkt_icmp *)&(*pktp)->data[(*pktp)->start];
|
|
if(incoming == IP_FALSE)
|
|
{
|
|
if (icmp->type == IPCOM_ICMP_ECHO_REQUEST)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (icmp->type == IPNET_ICMP6_ECHO_REPLY)
|
|
return 0;
|
|
}
|
|
|
|
switch(icmp->type)
|
|
{
|
|
case IPCOM_ICMP_UNREACH:
|
|
icmp->type = IPNET_ICMP6_DST_UNREACH;
|
|
icmp->data.failing_pkt.next_hop_mtu[0] = 0;
|
|
icmp->data.failing_pkt.next_hop_mtu[1] = 0;
|
|
switch(icmp->code)
|
|
{
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNREACH_NET:
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNKNOWN_NET:
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNKNOWN_HOST:
|
|
case IPNET_NAT_ICMP4_CODE_DST_ISOLATED:
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNREACH_TOS_NET:
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNREACH_TOS_HOST:
|
|
icmp->code = IPNET_ICMP6_DST_UNREACH_NOROUTE;
|
|
break;
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNREACH_HOST:
|
|
icmp->code = IPNET_ICMP6_DST_UNREACH_NOROUTE;
|
|
break;
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNREACH_PROTO:
|
|
icmp->type = IPNET_ICMP6_PARAM_PROB;
|
|
icmp->code = IPNET_ICMP6_PARAM_PROB_NXT_HDR;
|
|
IP_SET_HTONL(icmp->data.failing_pkt.next_hop_mtu, ip_offsetof(Ipnet_pkt_ip6, nxt));
|
|
break;
|
|
case IPNET_NAT_ICMP4_CODE_DST_UNREACH_PORT:
|
|
icmp->code = IPNET_ICMP6_DST_UNREACH_NOPORT;
|
|
break;
|
|
case IPNET_NAT_ICMP4_CODE_DST_NEEDFRAG:
|
|
return -1;
|
|
case IPNET_NAT_ICMP4_CODE_DST_SRCFAIL:
|
|
icmp->code = IPNET_ICMP6_DST_UNREACH_NOROUTE;
|
|
break;
|
|
case IPNET_NAT_ICMP4_CODE_DST_PROHIBITED_NET:
|
|
case IPNET_NAT_ICMP4_CODE_DST_PROHIBITED_HOST:
|
|
icmp->code = IPNET_ICMP6_DST_UNREACH_ADMIN;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
break;
|
|
case IPCOM_ICMP_TIMEXCEED:
|
|
icmp->type = IPNET_ICMP6_TIME_EXCEEDED;
|
|
break;
|
|
case IPCOM_ICMP_PARAMPROB:
|
|
icmp->type = IPNET_ICMP6_PARAM_PROB;
|
|
icmp->code = IPNET_ICMP6_PARAM_PROB_ERR_HDR;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
iph_embed = (Ipcom_ip_header *)icmp->data.failing_pkt.ip;
|
|
hlen_embed = (Ip_u16)((iph_embed->vers_len & 0x0f)<<2);
|
|
diff = IPCOM_IP6_HEADER_LENGTH - hlen_embed;
|
|
plen_embed = (Ip_u16)(ip_ntohs(iph_embed->length) - hlen_embed);
|
|
tos = iph_embed->tos;
|
|
ttl = iph_embed->ttl;
|
|
if(iph_embed->protocol == IP_IPPROTO_UDP)
|
|
{
|
|
udp_embed = (Ipcom_udp_header *)((Ip_u8 *)iph_embed + hlen_embed);
|
|
|
|
/* Update pseudo checksum */
|
|
ipnet_nat_pt_embedded_checksum_update(nat_mapping, plen_embed, iph_embed->protocol, &udp_embed->checksum);
|
|
|
|
/* Update source port */
|
|
oldsport = udp_embed->src_port;
|
|
udp_embed->src_port = nat_mapping->inner.tuple.src_port_n;
|
|
|
|
/* Update checksum for udp source port */
|
|
ipnet_nat_checksum_update((Ip_u8 *)&udp_embed->checksum,
|
|
(Ip_u8 *)&oldsport,
|
|
sizeof(oldsport),
|
|
(Ip_u8 *)&udp_embed->src_port,
|
|
sizeof(udp_embed->src_port));
|
|
}
|
|
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 (*len >= 8 + hlen_embed + IPCOM_TCP_HEADER_LENGTH)
|
|
{
|
|
/* Update pseudo checksum */
|
|
ipnet_nat_pt_embedded_checksum_update(nat_mapping, plen_embed, iph_embed->protocol, &tcp_embed->checksum);
|
|
}
|
|
|
|
/* Update source port */
|
|
oldsport = tcp_embed->src_port;
|
|
tcp_embed->src_port = nat_mapping->inner.tuple.src_port_n;
|
|
|
|
/* Check if packet contains enough data to update TCP checksum */
|
|
if (*len >= 8 + hlen_embed + IPCOM_TCP_HEADER_LENGTH)
|
|
{
|
|
/* Update checksum for tcp source port */
|
|
ipnet_nat_checksum_update((Ip_u8 *)&tcp_embed->checksum,
|
|
(Ip_u8 *)&oldsport,
|
|
sizeof(oldsport),
|
|
(Ip_u8 *)&tcp_embed->src_port,
|
|
sizeof(tcp_embed->src_port));
|
|
}
|
|
}
|
|
else if(iph_embed->protocol == IP_IPPROTO_ICMP)
|
|
{
|
|
icmp_embed = (Ipnet_pkt_icmp *)((Ip_u8 *)iph_embed + hlen_embed);
|
|
if(icmp_embed->type == IPCOM_ICMP_ECHO_REQUEST)
|
|
{
|
|
/* Update pseudo checksum */
|
|
ipnet_nat_pt_embedded_checksum_update(nat_mapping, plen_embed, iph_embed->protocol, &icmp_embed->cksum);
|
|
|
|
/* Update type */
|
|
ipcom_memcpy(oldtypecode, &icmp_embed->type, sizeof(oldtypecode));
|
|
icmp_embed->type = IPNET_ICMP6_ECHO_REQUEST;
|
|
ipcom_memcpy(newtypecode, &icmp_embed->type, sizeof(newtypecode));
|
|
/* Update checksum for type */
|
|
ipnet_nat_checksum_update((unsigned char *)&icmp_embed->cksum,
|
|
(unsigned char *)oldtypecode, sizeof(oldtypecode),
|
|
(unsigned char *)newtypecode, sizeof(newtypecode));
|
|
/* Update echo id */
|
|
oldsport = icmp_embed->data.echo.id;
|
|
icmp_embed->data.echo.id = nat_mapping->inner.tuple.icmp_id_n;
|
|
/* Update checksum for echo id */
|
|
ipnet_nat_checksum_update((unsigned char *)&icmp_embed->cksum,
|
|
(unsigned char *)&oldsport, sizeof(oldsport),
|
|
(unsigned char *)&icmp_embed->data.echo.id, sizeof(icmp_embed->data.echo.id));
|
|
}
|
|
}
|
|
|
|
/* Check if there is room for the increased packet size */
|
|
pkt_tail = (*pktp);
|
|
while (pkt_tail->next_part != IP_NULL)
|
|
{
|
|
pkt_tail = pkt_tail->next_part;
|
|
}
|
|
if (pkt_tail->end + diff > pkt_tail->maxlen)
|
|
{
|
|
new_pkt = ipcom_pkt_malloc(IPNET_NAT_LINK_HDR_SIZE + IPCOM_IP6_HEADER_LENGTH + *len + diff,
|
|
IP_FLAG_FC_STACKCONTEXT);
|
|
if(new_pkt == IP_NULL)
|
|
return -1;
|
|
|
|
new_pkt->ipstart = IPNET_NAT_LINK_HDR_SIZE;
|
|
new_pkt->start = new_pkt->ipstart + IPCOM_IP6_HEADER_LENGTH;
|
|
new_pkt->end = new_pkt->start + *len + diff;
|
|
new_pkt->ifindex = (*pktp)->ifindex;
|
|
new_pkt->flags = (*pktp)->flags;
|
|
new_pkt->end = new_pkt->start;
|
|
pkt_tail = *pktp;
|
|
while (pkt_tail != IP_NULL)
|
|
{
|
|
ipcom_memcpy(&new_pkt->data[new_pkt->end],
|
|
&pkt_tail->data[pkt_tail->start],
|
|
pkt_tail->end - pkt_tail->start);
|
|
new_pkt->end += (pkt_tail->end - pkt_tail->start);
|
|
pkt_tail = pkt_tail->next_part;
|
|
}
|
|
ipcom_pkt_free(*pktp);
|
|
*pktp = new_pkt;
|
|
ip_assert((*pktp)->end <= (*pktp)->maxlen);
|
|
/* Reset pointers */
|
|
icmp = (Ipnet_pkt_icmp *)&(*pktp)->data[(*pktp)->start];
|
|
iph_embed = (Ipcom_ip_header *)icmp->data.failing_pkt.ip;
|
|
}
|
|
else
|
|
{
|
|
pkt_tail->end += diff;
|
|
}
|
|
|
|
/* Move payload in order to fit an IPv6 header */
|
|
ipcom_memmove((Ip_u8 *)iph_embed + IPCOM_IP6_HEADER_LENGTH,
|
|
(Ip_u8 *)iph_embed + hlen_embed,
|
|
*len - 8 - hlen_embed);
|
|
|
|
/* Write Ipv6 header */
|
|
ip6_embed = (Ipnet_pkt_ip6 *)iph_embed;
|
|
IPNET_IP6_SET_VER_CLASS_FLOW(ip6_embed, 6, tos, 0);
|
|
ip6_embed->plen = ip_htons(plen_embed);
|
|
ip6_embed->nxt = iph_embed->protocol == IP_IPPROTO_ICMP ? IP_IPPROTO_ICMPV6 : iph_embed->protocol;
|
|
ip6_embed->hlim = ttl;
|
|
IPNET_IP6_SET_ADDR(&ip6_embed->src, &nat_mapping->inner.tuple.pt_src);
|
|
IPNET_IP6_SET_ADDR(&ip6_embed->dst, &nat_mapping->inner.tuple.pt_dst);
|
|
|
|
/* Signal the change in length */
|
|
*len += diff;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_translate_icmp6_id
|
|
*===========================================================================
|
|
* Description: Translate the ICMPv6 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_pt_translate_icmp6_id(Ip_bool incoming, Ipcom_pkt *pkt, Ipnet_nat_mapping *nat_mapping)
|
|
{
|
|
Ipnet_pkt_icmp *icmp;
|
|
|
|
icmp = (Ipnet_pkt_icmp *)&pkt->data[pkt->start];
|
|
if(incoming == IP_FALSE && icmp->type == IPNET_ICMP6_ECHO_REQUEST)
|
|
{
|
|
icmp->type = IPCOM_ICMP_ECHO_REQUEST;
|
|
icmp->data.echo.id = nat_mapping->inner.nat_port_n;
|
|
}
|
|
else if(incoming == IP_TRUE && icmp->type == IPCOM_ICMP_ECHO_REPLY)
|
|
{
|
|
icmp->type = IPNET_ICMP6_ECHO_REPLY;
|
|
icmp->data.echo.id = nat_mapping->inner.tuple.icmp_id_n;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_loop_rules
|
|
*===========================================================================
|
|
* Description: Loop through NAT-PT rules and match packets against mappings
|
|
* and rules.
|
|
* Parameters: param4 - pointer to the output parameters.
|
|
* pktp - pointer to pointer to packet.
|
|
* Returns: IPNET_NAT_PT_TRANSLATED
|
|
* IPNET_NAT_NOMATCH
|
|
* IPNET_NAT_INVALID
|
|
* IPNET_NAT_DROP
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipnet_nat_pt_loop_rules(Ipcom_pkt **pktp)
|
|
{
|
|
Ip_s32 ret, i = 0;
|
|
Ipnet_nat_tuple nat_tuple;
|
|
Ipnet_nat_mapping *nat_mapping;
|
|
Ipnet_nat_rule *nat_rule;
|
|
int origstart;
|
|
|
|
/* Get the tuple */
|
|
origstart = (*pktp)->start;
|
|
ret = ipnet_nat_pt_get_tuple(*pktp, &nat_tuple);
|
|
if(ret < 0)
|
|
{
|
|
IPCOM_LOG0(INFO, "ipnet_nat_pt_loop_rules() :: INCOMING v6 packet is invalid");
|
|
ipnet->nat.inner.stats.invalid_in++;
|
|
(*pktp)->start = origstart;
|
|
return IPNET_NAT_INVALID; /* Packet is invalid in some way */
|
|
}
|
|
|
|
/* Change to IPv4 */
|
|
if(nat_tuple.protocol == IP_IPPROTO_ICMPV6)
|
|
nat_tuple.protocol = IP_IPPROTO_ICMP;
|
|
if(nat_tuple.fragoff == 0 && nat_tuple.icmp_type == IPNET_ICMP6_ECHO_REQUEST)
|
|
nat_tuple.icmp_type = IPCOM_ICMP_ECHO_REQUEST;
|
|
|
|
/* Search for existing mapping */
|
|
nat_mapping = ipnet_nat_find_mapping(IP_FALSE, *pktp, &nat_tuple);
|
|
if(nat_mapping != IP_NULL)
|
|
{
|
|
/* Perform the network address translation */
|
|
ipnet_nat_update_mapping(IP_FALSE, nat_mapping, &nat_tuple);
|
|
return ipnet_nat_pt_translate(IP_FALSE, nat_mapping, pktp, &nat_tuple);
|
|
}
|
|
|
|
/* 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(IP_FALSE, nat_rule, (*pktp)->ifindex, &nat_tuple);
|
|
if(ret == 1)
|
|
{
|
|
IPCOM_LOG1(DEBUG, "ipnet_nat_pt_loop_rules() :: INCOMING v6 packet matched rule @%d", 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(IP_FALSE,
|
|
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_pt_loop_rules() :: Failed to add mapping. (dropping packet)");
|
|
ipnet->nat.inner.stats.dropped_in++;
|
|
(*pktp)->start = origstart;
|
|
return IPNET_NAT_DROP;
|
|
}
|
|
return ipnet_nat_pt_translate(IP_FALSE, nat_mapping, pktp, &nat_tuple);
|
|
}
|
|
}
|
|
|
|
ipnet->nat.inner.stats.no_rule_match_in++;
|
|
(*pktp)->start = origstart;
|
|
return IPNET_NAT_NOMATCH;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_translate
|
|
*===========================================================================
|
|
* Description: Translate a packet for NAT-PT.
|
|
* Parameters: incoming - IP_TRUE for incoming packets.
|
|
* nat_mapping - pointer to NAT mapping.
|
|
* pktp - pointer to pointer to the packet.
|
|
* nat_tuple - pointer to packet tuple.
|
|
* param - pointer to the output parameters.
|
|
* Returns: IPNET_NAT_PT_TRANSLATED
|
|
* IPNET_NAT_DROP
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipnet_nat_pt_translate(Ip_bool incoming,
|
|
Ipnet_nat_mapping *nat_mapping,
|
|
Ipcom_pkt **pktp,
|
|
Ipnet_nat_tuple *nat_tuple)
|
|
{
|
|
Ipcom_ip_header *iph;
|
|
Ipnet_pkt_ip6 *ip6;
|
|
Ip_bool mod = IP_FALSE;
|
|
Ip_s32 ret, delta = 0;
|
|
int len, hlen;
|
|
#if IPCOM_SYSLOG_ENABLE(1)
|
|
char protostr[16];
|
|
#endif
|
|
Ipcom_pkt *tail = IP_NULL;
|
|
static struct Ip_in6_addr dstaddr6;
|
|
|
|
ip_assert(nat_mapping != IP_NULL);
|
|
ip_assert(pktp != IP_NULL);
|
|
ip_assert(*pktp != IP_NULL);
|
|
ip_assert(nat_tuple != IP_NULL);
|
|
|
|
/* Translate the tcp/udp port */
|
|
if(nat_tuple->fragoff == 0)
|
|
{
|
|
switch(nat_tuple->protocol)
|
|
{
|
|
case IP_IPPROTO_TCP:
|
|
ipnet_nat_translate_tcpudp_port(incoming, *pktp, nat_mapping);
|
|
break;
|
|
case IP_IPPROTO_UDP:
|
|
ipnet_nat_translate_tcpudp_port(incoming, *pktp, nat_mapping);
|
|
break;
|
|
case IP_IPPROTO_ICMP:
|
|
ipnet_nat_pt_translate_icmp6_id(incoming, *pktp, nat_mapping);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Call the application proxy */
|
|
if(nat_mapping->proxy_func != IP_NULL && nat_tuple->protocol != IP_IPPROTO_ICMP)
|
|
{
|
|
/* Options can be skipped since the header is not used anyway */
|
|
hlen = incoming == IP_TRUE ? IPCOM_IP_HEADER_LENGTH : IPNET_IP6_HDR_SIZE;
|
|
ret = ipnet_nat_proxy(incoming, pktp, nat_tuple, nat_mapping, hlen, &mod, &delta);
|
|
if (ret < 0)
|
|
{
|
|
/* Drop packet */
|
|
IPCOM_LOG1(INFO, "ipnet_nat_pt_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;
|
|
}
|
|
}
|
|
|
|
len = ipcom_pkt_get_length(*pktp);
|
|
if(nat_tuple->fragoff == 0)
|
|
{
|
|
if(nat_tuple->protocol == IP_IPPROTO_TCP)
|
|
{
|
|
Ipcom_tcp_header *tcp = (Ipcom_tcp_header *)&(*pktp)->data[(*pktp)->start];
|
|
|
|
/* Save, translate and update the sequence number */
|
|
if(delta != 0)
|
|
{
|
|
Ip_u32 seq_num;
|
|
|
|
seq_num = IP_GET_32ON16((void *)&tcp->seq_num);
|
|
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);
|
|
|
|
if (nat_tuple->fragmf == 0)
|
|
{
|
|
/* Calculate checksum */
|
|
ipnet_nat_pt_tcp_checksum(incoming, *pktp, nat_mapping, nat_tuple, len);
|
|
}
|
|
else
|
|
{
|
|
ip_assert(incoming == IP_FALSE);
|
|
IPCOM_LOG1(INFO, "ipnet_nat_pt_translate() :: Dropping %s fragment",
|
|
ipnet_nat_protocol_name(nat_tuple->protocol, protostr, sizeof(protostr)));
|
|
ipnet->nat.inner.stats.dropped_out++;
|
|
return IPNET_NAT_DROP;
|
|
}
|
|
}
|
|
else if(nat_tuple->protocol == IP_IPPROTO_UDP)
|
|
{
|
|
Ipcom_udp_header *udp = (Ipcom_udp_header *)&(*pktp)->data[(*pktp)->start];
|
|
|
|
/* Fix UDP length */
|
|
if(delta != 0)
|
|
{
|
|
Ip_u16 tmp;
|
|
|
|
tmp = udp->length;
|
|
tmp = ip_ntohs(tmp);
|
|
tmp = (Ip_u16)((Ip_s32)tmp + delta);
|
|
ip_assert(tmp == (Ip_u16)len);
|
|
udp->length = ip_htons(tmp);
|
|
}
|
|
|
|
if (nat_tuple->fragmf == 0)
|
|
{
|
|
/* Calculate checksum */
|
|
ipnet_nat_pt_udp_checksum(incoming, *pktp, nat_mapping, nat_tuple, len);
|
|
}
|
|
else
|
|
{
|
|
/* Handle UDP fragments by setting checksum to zero */
|
|
udp->checksum = 0;
|
|
}
|
|
}
|
|
else if(nat_tuple->protocol == IP_IPPROTO_ICMP)
|
|
{
|
|
#if IPCOM_SYSLOG_ENABLE(1)
|
|
Ipnet_pkt_icmp *icmp = (Ipnet_pkt_icmp *)&(*pktp)->data[(*pktp)->start];
|
|
#endif
|
|
|
|
/* Translate the icmp error embedded datagram */
|
|
ret = ipnet_nat_pt_translate_embedded_icmp(incoming, &len, pktp, nat_mapping);
|
|
if (ret < 0)
|
|
{
|
|
IPCOM_LOG3(INFO, "ipnet_nat_pt_translate() :: Dropping %s icmp error message. type = %d code = %d",
|
|
incoming == IP_FALSE ? "OUTGOING" : "INCOMING", icmp->type, icmp->code);
|
|
if(incoming == IP_FALSE)
|
|
ipnet->nat.inner.stats.dropped_out++;
|
|
else
|
|
ipnet->nat.inner.stats.dropped_in++;
|
|
return IPNET_NAT_DROP;
|
|
}
|
|
|
|
if (nat_tuple->fragmf == 0)
|
|
{
|
|
/* Calculate checksum */
|
|
ipnet_nat_pt_icmp_checksum(incoming, *pktp, nat_mapping, nat_tuple, len);
|
|
}
|
|
else
|
|
{
|
|
ip_assert(incoming == IP_FALSE);
|
|
IPCOM_LOG1(INFO, "ipnet_nat_pt_translate() :: Dropping %s fragment",
|
|
ipnet_nat_protocol_name(nat_tuple->protocol, protostr, sizeof(protostr)));
|
|
ipnet->nat.inner.stats.dropped_out++;
|
|
return IPNET_NAT_DROP;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ip_assert(incoming == IP_FALSE);
|
|
if (nat_tuple->protocol == IP_IPPROTO_TCP || nat_tuple->protocol == IP_IPPROTO_ICMP)
|
|
{
|
|
IPCOM_LOG1(INFO, "ipnet_nat_pt_translate() :: Dropping %s fragment",
|
|
ipnet_nat_protocol_name(nat_tuple->protocol, protostr, sizeof(protostr)));
|
|
ipnet->nat.inner.stats.dropped_out++;
|
|
return IPNET_NAT_DROP;
|
|
}
|
|
}
|
|
|
|
if(incoming == IP_FALSE)
|
|
{
|
|
/* Write Ipv4 header */
|
|
tail = *pktp;
|
|
while (tail->next_part != IP_NULL)
|
|
{
|
|
tail = tail->next_part;
|
|
}
|
|
tail->end += (len - ipcom_pkt_get_length(*pktp));
|
|
(*pktp)->start -= IPCOM_IP_HEADER_LENGTH;
|
|
(*pktp)->ipstart = (*pktp)->start;
|
|
iph = (Ipcom_ip_header *)&(*pktp)->data[(*pktp)->ipstart];
|
|
ipcom_memset(iph, 0, IPCOM_IP_HEADER_LENGTH);
|
|
iph->vers_len = 0x45;
|
|
iph->tos = nat_tuple->tos;
|
|
iph->length = ip_htons((Ip_u16)len + IPCOM_IP_HEADER_LENGTH);
|
|
if (nat_tuple->fragmf != 0 || nat_tuple->fragoff != 0)
|
|
{
|
|
/* Attempt to create a unique identifier from the fragment identifier */
|
|
iph->id = (Ip_u16)(nat_tuple->fragid & 0x000000ff);
|
|
iph->id += (Ip_u16)((nat_tuple->fragid & 0xff000000) >> 16);
|
|
}
|
|
else
|
|
{
|
|
iph->id = (Ip_u16) ipcom_atomic_get((Ipcom_atomic_t *)&ipnet->ip4.id);
|
|
}
|
|
iph->frag = ip_htons(nat_tuple->fragoff);
|
|
if (nat_tuple->fragmf)
|
|
IP_BIT_SET(iph->frag, IPNET_OFF_MF);
|
|
iph->ttl = nat_tuple->ttl;
|
|
iph->protocol = nat_tuple->protocol;
|
|
IPNET_IP4_SET_IPADDR(&iph->src, nat_mapping->inner.nat_addr_n);
|
|
IPNET_IP4_SET_IPADDR(&iph->dst, nat_mapping->inner.tuple.pt_dst.in6.addr32[3]);
|
|
iph->checksum = 0;
|
|
iph->checksum = ipcom_in_checksum(iph, IPCOM_IP_HEADER_LENGTH);
|
|
}
|
|
else
|
|
{
|
|
/* Write Ipv6 header */
|
|
if ((*pktp)->start - IPNET_IP6_HDR_SIZE < 0)
|
|
{
|
|
Ipcom_pkt *new_pkt;
|
|
|
|
new_pkt = ipcom_pkt_malloc(len + IPNET_IP6_HDR_SIZE,
|
|
IP_FLAG_FC_STACKCONTEXT);
|
|
if(new_pkt == IP_NULL)
|
|
{
|
|
IPCOM_LOG1(INFO, "ipnet_nat_pt_translate() :: Could not create new packet with length = %d",
|
|
len + IPNET_IP6_HDR_SIZE);
|
|
return IPNET_NAT_DROP;
|
|
}
|
|
new_pkt->start = IPNET_IP6_HDR_SIZE;
|
|
new_pkt->ifindex = (*pktp)->ifindex;
|
|
new_pkt->flags = (*pktp)->flags;
|
|
ipcom_memcpy(&new_pkt->data[new_pkt->start], &(*pktp)->data[(*pktp)->start], len);
|
|
ipcom_pkt_free(*pktp);
|
|
*pktp = new_pkt;
|
|
}
|
|
tail = *pktp;
|
|
while (tail->next_part != IP_NULL)
|
|
{
|
|
tail = tail->next_part;
|
|
}
|
|
tail->end += (len - ipcom_pkt_get_length(*pktp));
|
|
(*pktp)->start -= IPNET_IP6_HDR_SIZE;
|
|
(*pktp)->ipstart = (*pktp)->start;
|
|
ip6 = (Ipnet_pkt_ip6 *)&(*pktp)->data[(*pktp)->ipstart];
|
|
ipcom_memset(ip6, 0, IPNET_IP6_HDR_SIZE);
|
|
IPNET_IP6_SET_VER_CLASS_FLOW(ip6, 6, nat_tuple->tos, 0);
|
|
ip6->plen = ip_htons((Ip_u16)len);
|
|
ip6->nxt = nat_tuple->protocol == IP_IPPROTO_ICMP ? IP_IPPROTO_ICMPV6 : nat_tuple->protocol;
|
|
ip6->hlim = nat_tuple->ttl;
|
|
dstaddr6.in6.addr32[0] = nat_mapping->inner.tuple.pt_dst.in6.addr32[0];
|
|
dstaddr6.in6.addr32[1] = nat_mapping->inner.tuple.pt_dst.in6.addr32[1];
|
|
dstaddr6.in6.addr32[2] = nat_mapping->inner.tuple.pt_dst.in6.addr32[2];
|
|
dstaddr6.in6.addr32[3] = nat_tuple->src_n;
|
|
IPNET_IP6_SET_ADDR(&ip6->src, &dstaddr6);
|
|
IPNET_IP6_SET_ADDR(&ip6->dst, &nat_mapping->inner.tuple.pt_src);
|
|
}
|
|
|
|
if(incoming == IP_FALSE)
|
|
ipnet->nat.inner.stats.translated_out++;
|
|
else
|
|
ipnet->nat.inner.stats.translated_in++;
|
|
|
|
IP_BIT_CLR((*pktp)->flags, IPCOM_PKT_FLAG_HW_CHECKSUM); /* Checksum already calculated above */
|
|
IP_BIT_SET((*pktp)->flags, IPCOM_PKT_FLAG_HAS_IP_HDR);
|
|
IP_BIT_CLR((*pktp)->flags, IPCOM_PKT_FLAG_FORWARDED); /* Avoid a fastpath cache entry to be created */
|
|
IP_BIT_SET((*pktp)->flags, IPCOM_PKT_FLAG_NAT); /* Avoid NAT output hook to be called */
|
|
return IPNET_NAT_PT_TRANSLATED;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipnet_nat_pt_output_hook
|
|
*===========================================================================
|
|
* Description: Output hook for NAT-PT.
|
|
* Parameters: param4 - Ipv4 output parameters.
|
|
* pktp - pointer to pointer to packet.
|
|
* Returns: IPNET_NAT_PT_TRANSLATED
|
|
* IPNET_NAT_NOMATCH
|
|
* IPNET_NAT_INVALID
|
|
* IPNET_NAT_DROP
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipnet_nat_pt_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;
|
|
|
|
ret = ipnet_nat_pt_loop_rules(pktp);
|
|
return ret;
|
|
} /*lint !e550*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
#else
|
|
int ipnet_nat_pt_empty_file;
|
|
#endif /* #ifdef IPNET_USE_NAT_PT */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|