2010-02-22 15:55:18 +02:00
|
|
|
/*
|
|
|
|
* WrapSix
|
2012-07-03 12:15:10 +03:00
|
|
|
* Copyright (C) 2008-2012 Michal Zima <xhire@mujmalysvet.cz>
|
2010-02-22 15:55:18 +02:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as
|
|
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h> /* malloc */
|
|
|
|
#include <time.h> /* time */
|
|
|
|
|
2012-04-01 20:08:28 +03:00
|
|
|
#include "ethernet.h"
|
2012-03-24 09:26:13 +02:00
|
|
|
#include "ipv4.h"
|
|
|
|
#include "ipv6.h"
|
|
|
|
#include "nat.h"
|
2010-02-22 15:55:18 +02:00
|
|
|
#include "radixtree.h"
|
2012-03-24 09:26:13 +02:00
|
|
|
#include "wrapper.h"
|
2010-02-22 15:55:18 +02:00
|
|
|
|
|
|
|
struct s_radixtree_nat6 {
|
|
|
|
struct s_ipv6_addr ipv6;
|
|
|
|
struct s_ipv4_addr ipv4;
|
|
|
|
unsigned short port_src;
|
|
|
|
unsigned short port_dst;
|
|
|
|
} __attribute__ ((__packed__));
|
|
|
|
|
|
|
|
struct s_radixtree_nat4 {
|
|
|
|
struct s_ipv4_addr addr;
|
|
|
|
unsigned short port_src;
|
|
|
|
unsigned short port_dst;
|
|
|
|
unsigned char zeros; /* unused space */
|
|
|
|
} __attribute__ ((__packed__));
|
|
|
|
|
2012-04-27 17:43:31 +03:00
|
|
|
struct s_radixtree_fragments4 {
|
|
|
|
struct s_ipv4_addr addr;
|
|
|
|
unsigned short id;
|
|
|
|
} __attribute__ ((__packed__));
|
|
|
|
|
2010-02-22 15:55:18 +02:00
|
|
|
radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp,
|
2012-04-27 17:43:31 +03:00
|
|
|
*nat4_tcp, *nat4_udp, *nat4_icmp,
|
|
|
|
*nat4_tcp_fragments;
|
2010-02-22 15:55:18 +02:00
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Initialization of NAT tables.
|
|
|
|
*/
|
2012-03-24 09:26:13 +02:00
|
|
|
void nat_init(void)
|
2010-02-22 15:55:18 +02:00
|
|
|
{
|
|
|
|
nat6_tcp = radixtree_create();
|
|
|
|
nat6_udp = radixtree_create();
|
|
|
|
nat6_icmp = radixtree_create();
|
|
|
|
|
|
|
|
nat4_tcp = radixtree_create();
|
|
|
|
nat4_udp = radixtree_create();
|
|
|
|
nat4_icmp = radixtree_create();
|
2012-04-27 17:43:31 +03:00
|
|
|
|
|
|
|
nat4_tcp_fragments = radixtree_create();
|
2010-02-22 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Clean-up of NAT tables.
|
|
|
|
*/
|
2012-03-24 09:26:13 +02:00
|
|
|
void nat_quit(void)
|
2010-02-22 15:55:18 +02:00
|
|
|
{
|
2012-03-24 09:26:13 +02:00
|
|
|
/* 128 + 16 + 32 + 16 = 192 / 6 = 32 */
|
|
|
|
radixtree_destroy(nat6_tcp, 32);
|
|
|
|
radixtree_destroy(nat6_udp, 32);
|
|
|
|
radixtree_destroy(nat6_icmp, 32);
|
2010-02-22 15:55:18 +02:00
|
|
|
|
|
|
|
/* 32 + 16 + 16 + 8 = 72 / 6 = 12 */
|
2012-03-24 09:26:13 +02:00
|
|
|
radixtree_destroy(nat4_tcp, 12);
|
|
|
|
radixtree_destroy(nat4_udp, 12);
|
|
|
|
radixtree_destroy(nat4_icmp, 12);
|
2012-04-27 17:43:31 +03:00
|
|
|
|
|
|
|
/* 32 + 16 = 48 / 6 = 8 */
|
|
|
|
radixtree_destroy(nat4_tcp_fragments, 8);
|
2010-02-22 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Lookup or create NAT connection for outgoing IPv6 packet.
|
|
|
|
*
|
|
|
|
* @param nat_proto6 IPv6 NAT table
|
|
|
|
* @param nat_proto4 IPv4 NAT table
|
|
|
|
* @param eth_src Source MAC address
|
|
|
|
* @param ipv6_src Source IPv6 address
|
|
|
|
* @param ipv6_dst Destination IPv6 address
|
|
|
|
* @param port_src Source port
|
|
|
|
* @param port_dst Destination port
|
|
|
|
*
|
|
|
|
* @return NULL when it wasn't possible to create connection
|
|
|
|
* @return pointer to connection structure otherwise
|
|
|
|
*/
|
2010-02-22 15:55:18 +02:00
|
|
|
struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4,
|
2012-04-01 20:08:28 +03:00
|
|
|
struct s_mac_addr eth_src,
|
2010-02-22 15:55:18 +02:00
|
|
|
struct s_ipv6_addr ipv6_src, struct s_ipv6_addr ipv6_dst,
|
|
|
|
unsigned short port_src, unsigned short port_dst)
|
|
|
|
{
|
2012-03-24 09:26:13 +02:00
|
|
|
struct s_nat *result, *connection;
|
2010-02-22 15:55:18 +02:00
|
|
|
|
2012-03-24 09:26:13 +02:00
|
|
|
struct s_radixtree_nat4 radixsearch4;
|
2010-02-22 15:55:18 +02:00
|
|
|
struct s_radixtree_nat6 radixsearch6;
|
2012-03-24 09:26:13 +02:00
|
|
|
|
|
|
|
/* create structure to search in the tree */
|
2010-02-22 15:55:18 +02:00
|
|
|
radixsearch6.ipv6 = ipv6_src;
|
2012-03-24 09:26:13 +02:00
|
|
|
ipv6_to_ipv4(&ipv6_dst, &radixsearch6.ipv4);
|
2010-02-22 15:55:18 +02:00
|
|
|
radixsearch6.port_src = port_src;
|
|
|
|
radixsearch6.port_dst = port_dst;
|
|
|
|
|
2012-04-01 20:08:28 +03:00
|
|
|
if ((result = (struct s_nat *) radixtree_lookup(nat_proto6,
|
2012-04-27 16:25:29 +03:00
|
|
|
radixtree_chunker, &radixsearch6, sizeof(radixsearch6))) == NULL) {
|
2012-03-24 09:26:13 +02:00
|
|
|
/* if no connection is found, let's create one */
|
2012-04-01 20:08:28 +03:00
|
|
|
if ((connection =
|
|
|
|
(struct s_nat *) malloc(sizeof(struct s_nat))) == NULL) {
|
2010-02-22 15:55:18 +02:00
|
|
|
fprintf(stderr, "[Error] Lack of free memory\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-01 20:08:28 +03:00
|
|
|
connection->mac = eth_src;
|
2010-02-22 15:55:18 +02:00
|
|
|
connection->ipv6 = ipv6_src;
|
|
|
|
connection->ipv4 = radixsearch6.ipv4;
|
|
|
|
connection->ipv6_port_src = port_src;
|
|
|
|
connection->ipv4_port_dst = port_dst;
|
2012-03-24 09:26:13 +02:00
|
|
|
connection->last_packet = time(NULL);
|
2010-02-22 15:55:18 +02:00
|
|
|
|
2012-04-01 20:08:28 +03:00
|
|
|
radixsearch4.addr = radixsearch6.ipv4;
|
|
|
|
radixsearch4.port_src = port_dst;
|
|
|
|
radixsearch4.zeros = 0x0;
|
|
|
|
|
2010-02-22 15:55:18 +02:00
|
|
|
/* generate some outgoing port */
|
|
|
|
do {
|
2012-04-01 20:08:28 +03:00
|
|
|
/* returns port from range 1024 - 65535 */
|
|
|
|
radixsearch4.port_dst = (rand() % 64511) + 1024;
|
2010-02-22 15:55:18 +02:00
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
result = radixtree_lookup(nat_proto4, radixtree_chunker, &radixsearch4, sizeof(radixsearch4));
|
2010-02-22 15:55:18 +02:00
|
|
|
} while (result != NULL);
|
|
|
|
|
2012-04-01 20:08:28 +03:00
|
|
|
connection->ipv4_port_src = radixsearch4.port_dst;
|
2010-02-22 15:55:18 +02:00
|
|
|
|
2012-04-01 20:08:28 +03:00
|
|
|
/* save this connection to the NAT table (to *both* of them) */
|
2012-04-27 16:25:29 +03:00
|
|
|
radixtree_insert(nat_proto6, radixtree_chunker, &radixsearch6, sizeof(radixsearch6), connection);
|
|
|
|
radixtree_insert(nat_proto4, radixtree_chunker, &radixsearch4, sizeof(radixsearch4), connection);
|
2010-02-22 15:55:18 +02:00
|
|
|
|
|
|
|
return connection;
|
|
|
|
} else {
|
2012-03-24 09:26:13 +02:00
|
|
|
/* when connection is found, refresh it and return */
|
2010-02-22 15:55:18 +02:00
|
|
|
result->last_packet = time(NULL);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-03 12:15:10 +03:00
|
|
|
/**
|
|
|
|
* Lookup NAT connection for incoming IPv4 packet.
|
|
|
|
*
|
|
|
|
* @param nat_proto4 NAT table
|
|
|
|
* @param ipv4_src Source IPv4 address
|
|
|
|
* @param port_src Source port
|
|
|
|
* @param port_dst Destination port
|
|
|
|
*
|
|
|
|
* @return NULL when no connection was found
|
|
|
|
* @return pointer to connection structure otherwise
|
|
|
|
*/
|
2012-03-24 09:26:13 +02:00
|
|
|
struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src,
|
2010-02-22 15:55:18 +02:00
|
|
|
unsigned short port_src, unsigned short port_dst)
|
|
|
|
{
|
2012-03-24 09:26:13 +02:00
|
|
|
struct s_nat *result;
|
2010-02-22 15:55:18 +02:00
|
|
|
|
|
|
|
/* create structure to search in the tree */
|
2012-03-24 09:26:13 +02:00
|
|
|
struct s_radixtree_nat4 radixsearch4;
|
|
|
|
radixsearch4.addr = ipv4_src;
|
|
|
|
radixsearch4.port_src = port_src;
|
|
|
|
radixsearch4.port_dst = port_dst;
|
|
|
|
radixsearch4.zeros = 0x0;
|
|
|
|
|
2012-04-27 16:25:29 +03:00
|
|
|
if ((result = (struct s_nat *) radixtree_lookup(nat_proto4, radixtree_chunker, &radixsearch4, sizeof(radixsearch4))) == NULL) {
|
2012-03-24 09:26:13 +02:00
|
|
|
/* when connection is not found, drop the packet */
|
2010-02-22 15:55:18 +02:00
|
|
|
return NULL;
|
|
|
|
} else {
|
2012-03-24 09:26:13 +02:00
|
|
|
/* when connection is found, refresh it and return */
|
2010-02-22 15:55:18 +02:00
|
|
|
result->last_packet = time(NULL);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
2012-04-27 17:43:31 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Save and retrieve NATted connections via fragment identification.
|
|
|
|
*
|
|
|
|
* @param nat_proto4 Radix tree of fragments
|
|
|
|
* @param ipv4_src Source IPv4 address
|
|
|
|
* @param id Fragment identification
|
|
|
|
* @param nat Connection to save
|
|
|
|
*
|
|
|
|
* @return Connection
|
|
|
|
*/
|
|
|
|
struct s_nat *nat_in_fragments(radixtree_t *nat_proto4,
|
|
|
|
struct s_ipv4_addr ipv4_src,
|
|
|
|
unsigned short id, struct s_nat *nat)
|
|
|
|
{
|
|
|
|
struct s_nat *result;
|
|
|
|
|
|
|
|
/* create structure to search in the tree */
|
|
|
|
struct s_radixtree_fragments4 radixsearch4;
|
|
|
|
radixsearch4.addr = ipv4_src;
|
|
|
|
radixsearch4.id = id;
|
|
|
|
|
|
|
|
if ((result = (struct s_nat *) radixtree_lookup(nat_proto4,
|
|
|
|
radixtree_chunker, &radixsearch4, sizeof(radixsearch4))) == NULL) {
|
|
|
|
if (nat != NULL) {
|
|
|
|
/* when fragmentation is not found, add one */
|
|
|
|
radixtree_insert(nat_proto4, radixtree_chunker,
|
|
|
|
&radixsearch4, sizeof(radixsearch4),
|
|
|
|
nat);
|
|
|
|
return nat;
|
|
|
|
} else {
|
|
|
|
/* nothing found and nothing to be added */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (nat != NULL) {
|
|
|
|
/* when fragmentation is found, is it the same? */
|
|
|
|
if (result == nat) {
|
|
|
|
/* OK, return */
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
/* sender determines usage of IDs, so this one
|
|
|
|
* shouldn't be used anymore for that
|
|
|
|
* connection */
|
|
|
|
radixtree_delete(nat_proto4, radixtree_chunker,
|
|
|
|
&radixsearch4,
|
|
|
|
sizeof(radixsearch4));
|
|
|
|
radixtree_insert(nat_proto4, radixtree_chunker,
|
|
|
|
&radixsearch4,
|
|
|
|
sizeof(radixsearch4), nat);
|
|
|
|
return nat;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* refresh it's connection and return */
|
|
|
|
result->last_packet = time(NULL);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove one entry from "fragment NAT".
|
|
|
|
*
|
|
|
|
* @param nat_proto4 Radix tree of fragments
|
|
|
|
* @param ipv4_src Source IPv4 address
|
|
|
|
* @param id Fragment identification
|
|
|
|
*/
|
2012-07-03 12:15:10 +03:00
|
|
|
void nat_in_fragments_cleanup(radixtree_t *nat_proto4,
|
|
|
|
struct s_ipv4_addr ipv4_src, unsigned short id)
|
2012-04-27 17:43:31 +03:00
|
|
|
{
|
|
|
|
/* create structure to search in the tree */
|
|
|
|
struct s_radixtree_fragments4 radixsearch4;
|
|
|
|
radixsearch4.addr = ipv4_src;
|
|
|
|
radixsearch4.id = id;
|
|
|
|
|
|
|
|
if (radixtree_lookup(nat_proto4, radixtree_chunker, &radixsearch4,
|
|
|
|
sizeof(radixsearch4)) != NULL) {
|
|
|
|
radixtree_delete(nat_proto4, radixtree_chunker, &radixsearch4,
|
|
|
|
sizeof(radixsearch4));
|
|
|
|
}
|
|
|
|
}
|