1
0
mirror of https://code.semirocket.science/wrapsix synced 2024-09-19 15:01:06 +03:00

Expiration of connections and fragments

This commit is contained in:
Michal Zima 2012-07-21 21:13:15 +02:00
parent 24b09b4d93
commit 95bfbe93e2
11 changed files with 464 additions and 143 deletions

View File

@ -21,7 +21,7 @@ AC_ARG_ENABLE([debug],
[debug=no]) [debug=no])
if test "x$debug" = "xyes"; then if test "x$debug" = "xyes"; then
AM_CFLAGS="${AM_CFLAGS} -g -ggdb -O0 -pipe -pedantic -Wshadow -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wold-style-definition -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wformat-nonliteral -Wformat-security -Wswitch-enum -Wswitch-default -Winit-self -Wmissing-include-dirs -Wundef -Waggregate-return -Wnested-externs -Wunsafe-loop-optimizations" AM_CFLAGS="${AM_CFLAGS} -g -ggdb -O0 -pipe -pedantic -Wshadow -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wold-style-definition -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wformat-nonliteral -Wformat-security -Wswitch-enum -Winit-self -Wmissing-include-dirs -Wundef -Waggregate-return -Wnested-externs -Wunsafe-loop-optimizations"
AC_DEFINE([DEBUG]) AC_DEFINE([DEBUG])
else else
AM_CFLAGS="${AM_CFLAGS} -O2" AM_CFLAGS="${AM_CFLAGS} -O2"

View File

@ -24,6 +24,7 @@
#include "checksum.h" #include "checksum.h"
#include "icmp.h" #include "icmp.h"
#include "ipv6.h" #include "ipv6.h"
#include "linkedlist.h"
#include "log.h" #include "log.h"
#include "nat.h" #include "nat.h"
#include "transmitter.h" #include "transmitter.h"
@ -86,6 +87,8 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4,
return 1; return 1;
} }
linkedlist_move2end(timeout_icmp, connection->llnode);
echo->id = connection->ipv6_port_src; echo->id = connection->ipv6_port_src;
/* override information in original ICMP header */ /* override information in original ICMP header */
@ -189,7 +192,7 @@ int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
connection = nat_out(nat6_icmp, nat4_icmp, connection = nat_out(nat6_icmp, nat4_icmp,
eth6->src, eth6->src,
ip6->ip_src, ip6->ip_dest, ip6->ip_src, ip6->ip_dest,
echo->id, 0); echo->id, 0, 1);
if (connection == NULL) { if (connection == NULL) {
log_warn("Outgoing connection wasn't " log_warn("Outgoing connection wasn't "
@ -197,6 +200,15 @@ int icmp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
return 1; return 1;
} }
if (connection->llnode == NULL) {
connection->llnode =
linkedlist_append(timeout_icmp,
connection);
} else {
linkedlist_move2end(timeout_icmp,
connection->llnode);
}
echo->id = connection->ipv4_port_src; echo->id = connection->ipv4_port_src;
/* override information in original ICMP header */ /* override information in original ICMP header */
@ -280,7 +292,6 @@ int icmp_ndp(struct s_ethernet *ethq, struct s_ipv6 *ipq,
/* first check whether the request belongs to us */ /* first check whether the request belongs to us */
if (memcmp(&wrapsix_ipv6_prefix, &ndp_ns->target, 12) != 0) { if (memcmp(&wrapsix_ipv6_prefix, &ndp_ns->target, 12) != 0) {
log_debug("This is unfamiliar NDP packet");
return 1; return 1;
} }

View File

@ -17,6 +17,7 @@
*/ */
#include <stdlib.h> /* free, malloc */ #include <stdlib.h> /* free, malloc */
#include <time.h> /* time, time_t */
#include "linkedlist.h" #include "linkedlist.h"
#include "log.h" #include "log.h"
@ -65,27 +66,28 @@ void linkedlist_destroy(linkedlist_t *root)
* @param root Root of the linked list * @param root Root of the linked list
* @param data Data to append to the list * @param data Data to append to the list
* *
* @return 0 for success * @return pointer to created node if succeeded
* @return 1 for failure * @return NULL if failed
*/ */
int linkedlist_append(linkedlist_t *root, void *data) linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data)
{ {
linkedlist_node_t *node; linkedlist_node_t *node;
if ((node = (linkedlist_node_t *) malloc(sizeof(linkedlist_node_t))) == if ((node = (linkedlist_node_t *) malloc(sizeof(linkedlist_node_t))) ==
NULL) { NULL) {
log_error("Lack of free memory"); log_error("Lack of free memory");
return 1; return NULL;
} }
node->data = data; node->data = data;
node->time = time(NULL);
node->prev = root->last.prev; node->prev = root->last.prev;
node->next = &root->last; node->next = &root->last;
root->last.prev->next = node; root->last.prev->next = node;
root->last.prev = node; root->last.prev = node;
return 0; return node;
} }
/** /**
@ -102,3 +104,25 @@ void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node)
free(node); free(node);
node = NULL; node = NULL;
} }
/**
* Move a node to the end of linked list. It's possible to even move it to
* another linked list.
*
* @param root Root of the linked list
* @param node Node to move to the end
*/
void linkedlist_move2end(linkedlist_t *root, linkedlist_node_t *node)
{
/* take the node out */
node->next->prev = node->prev;
node->prev->next = node->next;
/* put it to the end */
node->prev = root->last.prev;
node->next = &root->last;
root->last.prev->next = node;
root->last.prev = node;
node->time = time(NULL);
}

View File

@ -19,11 +19,14 @@
#ifndef LINKEDLIST_H #ifndef LINKEDLIST_H
#define LINKEDLIST_H #define LINKEDLIST_H
#include <time.h> /* time_t */
/* Linked list node structure */ /* Linked list node structure */
typedef struct s_linkedlist_node { typedef struct s_linkedlist_node {
struct s_linkedlist_node *prev; struct s_linkedlist_node *prev;
struct s_linkedlist_node *next; struct s_linkedlist_node *next;
void *data; void *data;
time_t time;
} linkedlist_node_t; } linkedlist_node_t;
/* Linked list root structure */ /* Linked list root structure */
@ -34,7 +37,8 @@ typedef struct s_linkedlist {
linkedlist_t *linkedlist_create(void); linkedlist_t *linkedlist_create(void);
void linkedlist_destroy(linkedlist_t *root); void linkedlist_destroy(linkedlist_t *root);
int linkedlist_append(linkedlist_t *root, void *data); linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data);
void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node); void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node);
void linkedlist_move2end(linkedlist_t *root, linkedlist_node_t *node);
#endif /* LINKEDLIST_H */ #endif /* LINKEDLIST_H */

309
src/nat.c
View File

@ -48,7 +48,16 @@ struct s_radixtree_fragments4 {
radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp, radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp,
*nat4_tcp, *nat4_udp, *nat4_icmp, *nat4_tcp, *nat4_udp, *nat4_icmp,
*nat4_tcp_fragments, *nat4_saved_fragments; *nat4_tcp_fragments;
/* Linked lists for handling timeouts of connections */
linkedlist_t *timeout_icmp, *timeout_udp,
*timeout_tcp_est, *timeout_tcp_trans,
*timeout_tcp_fragments;
/* Declarations */
void nat_delete_connection(radixtree_t *nat_proto4, radixtree_t *nat_proto6,
struct s_nat *connection);
/** /**
* Initialization of NAT tables. * Initialization of NAT tables.
@ -64,7 +73,12 @@ void nat_init(void)
nat4_icmp = radixtree_create(); nat4_icmp = radixtree_create();
nat4_tcp_fragments = radixtree_create(); nat4_tcp_fragments = radixtree_create();
nat4_saved_fragments = radixtree_create();
timeout_icmp = linkedlist_create();
timeout_udp = linkedlist_create();
timeout_tcp_est = linkedlist_create();
timeout_tcp_trans = linkedlist_create();
timeout_tcp_fragments = linkedlist_create();
} }
/** /**
@ -84,27 +98,34 @@ void nat_quit(void)
/* 32 + 16 = 48 / 6 = 8 */ /* 32 + 16 = 48 / 6 = 8 */
radixtree_destroy(nat4_tcp_fragments, 8); radixtree_destroy(nat4_tcp_fragments, 8);
radixtree_destroy(nat4_saved_fragments, 8);
linkedlist_destroy(timeout_icmp);
linkedlist_destroy(timeout_udp);
linkedlist_destroy(timeout_tcp_est);
linkedlist_destroy(timeout_tcp_trans);
linkedlist_destroy(timeout_tcp_fragments);
} }
/** /**
* Lookup or create NAT connection for outgoing IPv6 packet. * Lookup or create NAT connection for outgoing IPv6 packet.
* *
* @param nat_proto6 IPv6 NAT table * @param nat_proto6 IPv6 NAT table
* @param nat_proto4 IPv4 NAT table * @param nat_proto4 IPv4 NAT table
* @param eth_src Source MAC address * @param eth_src Source MAC address
* @param ipv6_src Source IPv6 address * @param ipv6_src Source IPv6 address
* @param ipv6_dst Destination IPv6 address * @param ipv6_dst Destination IPv6 address
* @param port_src Source port * @param port_src Source port
* @param port_dst Destination port * @param port_dst Destination port
* @param create Whether or not to create new NAT entry
* *
* @return NULL when it wasn't possible to create connection * @return NULL when it wasn't possible to create connection
* @return pointer to connection structure otherwise * @return pointer to connection structure otherwise
*/ */
struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4, struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4,
struct s_mac_addr eth_src, struct s_mac_addr eth_src,
struct s_ipv6_addr ipv6_src, struct s_ipv6_addr ipv6_dst, struct s_ipv6_addr ipv6_src, struct s_ipv6_addr ipv6_dst,
unsigned short port_src, unsigned short port_dst) unsigned short port_src, unsigned short port_dst,
unsigned char create)
{ {
struct s_nat *result, *connection; struct s_nat *result, *connection;
@ -119,42 +140,55 @@ struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4,
if ((result = (struct s_nat *) radixtree_lookup(nat_proto6, if ((result = (struct s_nat *) radixtree_lookup(nat_proto6,
radixtree_chunker, &radixsearch6, sizeof(radixsearch6))) == NULL) { radixtree_chunker, &radixsearch6, sizeof(radixsearch6))) == NULL) {
/* if no connection is found, let's create one */ if (create > 0) {
if ((connection = /* if no connection is found, let's create one */
(struct s_nat *) malloc(sizeof(struct s_nat))) == NULL) { if ((connection =
log_error("Lack of free memory"); (struct s_nat *) malloc(sizeof(struct s_nat))) ==
NULL) {
log_error("Lack of free memory");
return NULL;
}
connection->mac = eth_src;
connection->ipv6 = ipv6_src;
connection->ipv4 = radixsearch6.ipv4;
connection->ipv6_port_src = port_src;
connection->ipv4_port_dst = port_dst;
connection->state = 1;
connection->llnode = NULL;
radixsearch4.addr = radixsearch6.ipv4;
radixsearch4.port_src = port_dst;
radixsearch4.zeros = 0x0;
/* generate some outgoing port */
do {
/* returns port from range 1024 - 65535 */
radixsearch4.port_dst = (rand() % 64511) + 1024;
result = radixtree_lookup(nat_proto4,
radixtree_chunker,
&radixsearch4,
sizeof(radixsearch4));
} while (result != NULL);
connection->ipv4_port_src = radixsearch4.port_dst;
/* save this connection to the NAT table (to *both* of
* them) */
radixtree_insert(nat_proto6, radixtree_chunker,
&radixsearch6, sizeof(radixsearch6),
connection);
radixtree_insert(nat_proto4, radixtree_chunker,
&radixsearch4, sizeof(radixsearch4),
connection);
return connection;
} else {
return NULL; return NULL;
} }
connection->mac = eth_src;
connection->ipv6 = ipv6_src;
connection->ipv4 = radixsearch6.ipv4;
connection->ipv6_port_src = port_src;
connection->ipv4_port_dst = port_dst;
connection->last_packet = time(NULL);
radixsearch4.addr = radixsearch6.ipv4;
radixsearch4.port_src = port_dst;
radixsearch4.zeros = 0x0;
/* generate some outgoing port */
do {
/* returns port from range 1024 - 65535 */
radixsearch4.port_dst = (rand() % 64511) + 1024;
result = radixtree_lookup(nat_proto4, radixtree_chunker, &radixsearch4, sizeof(radixsearch4));
} while (result != NULL);
connection->ipv4_port_src = radixsearch4.port_dst;
/* save this connection to the NAT table (to *both* of them) */
radixtree_insert(nat_proto6, radixtree_chunker, &radixsearch6, sizeof(radixsearch6), connection);
radixtree_insert(nat_proto4, radixtree_chunker, &radixsearch4, sizeof(radixsearch4), connection);
return connection;
} else { } else {
/* when connection is found, refresh it and return */ /* when connection is found, return it */
result->last_packet = time(NULL);
return result; return result;
} }
} }
@ -162,13 +196,13 @@ struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4,
/** /**
* Lookup NAT connection for incoming IPv4 packet. * Lookup NAT connection for incoming IPv4 packet.
* *
* @param nat_proto4 NAT table * @param nat_proto4 NAT table
* @param ipv4_src Source IPv4 address * @param ipv4_src Source IPv4 address
* @param port_src Source port * @param port_src Source port
* @param port_dst Destination port * @param port_dst Destination port
* *
* @return NULL when no connection was found * @return NULL when no connection was found
* @return pointer to connection structure otherwise * @return pointer to connection structure otherwise
*/ */
struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src,
unsigned short port_src, unsigned short port_dst) unsigned short port_src, unsigned short port_dst)
@ -182,30 +216,32 @@ struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src,
radixsearch4.port_dst = port_dst; radixsearch4.port_dst = port_dst;
radixsearch4.zeros = 0x0; radixsearch4.zeros = 0x0;
if ((result = (struct s_nat *) radixtree_lookup(nat_proto4, radixtree_chunker, &radixsearch4, sizeof(radixsearch4))) == NULL) { if ((result = (struct s_nat *) radixtree_lookup(nat_proto4,
radixtree_chunker, &radixsearch4, sizeof(radixsearch4))) == NULL) {
/* when connection is not found, drop the packet */ /* when connection is not found, drop the packet */
return NULL; return NULL;
} else { } else {
/* when connection is found, refresh it and return */ /* when connection is found, return it */
result->last_packet = time(NULL);
return result; return result;
} }
} }
/** /**
* Save and retrieve data (e.g. NATted connections) via fragment identification. * Retrieve or create data structure via fragment identification.
* *
* @param nat_proto4 Radix tree of fragments * @param nat_proto4 Radix tree of fragments
* @param ipv4_src Source IPv4 address * @param ipv4_src Source IPv4 address
* @param id Fragment identification * @param id Fragment identification
* @param data Data to save
* *
* @return Data (or NULL when nothing found) * @return Structure for fragments (either retrieved or created)
* @return NULL when structure for fragments couldn't be created
*/ */
void *nat_in_fragments(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, struct s_nat_fragments *nat_in_fragments(radixtree_t *nat_proto4,
unsigned short id, void *data) linkedlist_t *nat_timeout,
struct s_ipv4_addr ipv4_src,
unsigned short id)
{ {
void *result; struct s_nat_fragments *result;
/* create structure to search in the tree */ /* create structure to search in the tree */
struct s_radixtree_fragments4 radixsearch4; struct s_radixtree_fragments4 radixsearch4;
@ -213,38 +249,26 @@ void *nat_in_fragments(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src,
radixsearch4.id = id; radixsearch4.id = id;
if ((result = radixtree_lookup(nat_proto4, radixtree_chunker, if ((result = radixtree_lookup(nat_proto4, radixtree_chunker,
&radixsearch4, sizeof(radixsearch4))) == NULL) { &radixsearch4, sizeof(radixsearch4))) != NULL) {
if (data != NULL) { return result;
/* when fragmentation is not found, add one */ } else {
radixtree_insert(nat_proto4, radixtree_chunker, /* when fragmentation is not found, add one */
&radixsearch4, sizeof(radixsearch4), if ((result = (struct s_nat_fragments *)
data); malloc(sizeof(struct s_nat_fragments))) == NULL) {
return data; log_error("Lack of free memory");
} else {
/* nothing found and nothing to be added */
return NULL; return NULL;
} }
} else {
if (data != NULL) { result->id = id;
/* when fragmentation is found, is it the same? */ result->connection = NULL;
if (result == data) { result->queue = NULL;
/* OK, return */
return result; radixtree_insert(nat_proto4, radixtree_chunker,
} else { &radixsearch4, sizeof(radixsearch4),
/* sender determines usage of IDs, so this one result);
* shouldn't be used anymore for that linkedlist_append(nat_timeout, result);
* connection */
radixtree_delete(nat_proto4, radixtree_chunker, return result;
&radixsearch4,
sizeof(radixsearch4));
radixtree_insert(nat_proto4, radixtree_chunker,
&radixsearch4,
sizeof(radixsearch4), data);
return data;
}
} else {
return result;
}
} }
} }
@ -269,3 +293,106 @@ void nat_in_fragments_cleanup(radixtree_t *nat_proto4,
sizeof(radixsearch4)); sizeof(radixsearch4));
} }
} }
/**
* Delete a NAT connection.
*
* @param nat_proto4 Relevant NAT4 table
* @param nat_proto6 Relevant NAT6 table
* @param connection Connection to be deleted
*/
void nat_delete_connection(radixtree_t *nat_proto4, radixtree_t *nat_proto6,
struct s_nat *connection)
{
/* create structure to search in the tree */
struct s_radixtree_nat4 radixsearch4;
struct s_radixtree_nat6 radixsearch6;
radixsearch6.ipv6 = connection->ipv6;
radixsearch6.ipv4 = connection->ipv4;
radixsearch6.port_src = connection->ipv6_port_src;
radixsearch6.port_dst = connection->ipv4_port_dst;
radixsearch4.addr = connection->ipv4;
radixsearch4.port_src = connection->ipv4_port_dst;
radixsearch4.port_dst = connection->ipv4_port_src;
radixsearch4.zeros = 0x0;
if (radixtree_lookup(nat_proto4, radixtree_chunker, &radixsearch4,
sizeof(radixsearch4)) != NULL) {
radixtree_delete(nat_proto4, radixtree_chunker, &radixsearch4,
sizeof(radixsearch4));
}
if (radixtree_lookup(nat_proto6, radixtree_chunker, &radixsearch6,
sizeof(radixsearch6)) != NULL) {
radixtree_delete(nat_proto6, radixtree_chunker, &radixsearch6,
sizeof(radixsearch6));
}
free(connection);
connection = NULL;
}
/**
* Remove expired connections from NAT.
*/
void nat_cleaning(void)
{
linkedlist_node_t *tmp;
time_t curtime = time(NULL);
/* TCP FRAGMENTS [2 secs] */
tmp = timeout_tcp_fragments->first.next;
while (tmp->next != NULL && curtime - tmp->time >= 2) {
tmp = tmp->next;
/* destroy queue */
linkedlist_destroy(((struct s_nat_fragments *)
tmp->prev->data)->queue);
/* remove connection */
nat_in_fragments_cleanup(nat4_tcp_fragments,
((struct s_nat_fragments *)
tmp->prev->data)->connection->ipv4,
((struct s_nat_fragments *)
tmp->prev->data)->id);
free(((struct s_nat_fragments *) tmp->prev->data)->connection);
free(tmp->prev->data);
linkedlist_delete(timeout_tcp_fragments, tmp->prev);
}
/* ICMP [60 secs] */
tmp = timeout_icmp->first.next;
while (tmp->next != NULL && curtime - tmp->time >= 60) {
tmp = tmp->next;
nat_delete_connection(nat4_icmp, nat6_icmp, tmp->prev->data);
linkedlist_delete(timeout_icmp, tmp->prev);
}
/* TCP -- TRANS [4 mins] */
tmp = timeout_tcp_trans->first.next;
while (tmp->next != NULL && curtime - tmp->time >= 4 * 60) {
tmp = tmp->next;
nat_delete_connection(nat4_tcp, nat6_tcp, tmp->prev->data);
linkedlist_delete(timeout_tcp_trans, tmp->prev);
}
/* UDP [5 mins (minimum is 2 mins)] */
tmp = timeout_udp->first.next;
while (tmp->next != NULL && curtime - tmp->time >= 5 * 60) {
tmp = tmp->next;
nat_delete_connection(nat4_udp, nat6_udp, tmp->prev->data);
linkedlist_delete(timeout_udp, tmp->prev);
}
/* TCP -- EST [2 hrs and 4 mins] */
tmp = timeout_tcp_est->first.next;
while (tmp->next != NULL && curtime - tmp->time >= 124 * 60) {
tmp = tmp->next;
nat_delete_connection(nat4_tcp, nat6_tcp, tmp->prev->data);
linkedlist_delete(timeout_tcp_est, tmp->prev);
}
}

View File

@ -19,11 +19,10 @@
#ifndef NAT_H #ifndef NAT_H
#define NAT_H #define NAT_H
#include <time.h> /* time_t */
#include "ethernet.h" /* s_mac_addr */ #include "ethernet.h" /* s_mac_addr */
#include "ipv4.h" /* s_ipv4_addr */ #include "ipv4.h" /* s_ipv4_addr */
#include "ipv6.h" /* s_ipv6_addr */ #include "ipv6.h" /* s_ipv6_addr */
#include "linkedlist.h" /* linkedlist_h */
#include "radixtree.h" /* radixtree_t */ #include "radixtree.h" /* radixtree_t */
struct s_nat { struct s_nat {
@ -33,13 +32,23 @@ struct s_nat {
unsigned short ipv6_port_src; unsigned short ipv6_port_src;
unsigned short ipv4_port_src; unsigned short ipv4_port_src;
unsigned short ipv4_port_dst; unsigned short ipv4_port_dst;
time_t last_packet; /* time of processing last unsigned char state;
packet of the connection */ linkedlist_node_t *llnode;
};
struct s_nat_fragments {
unsigned short id;
struct s_nat *connection;
linkedlist_t *queue;
}; };
extern radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp, extern radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp,
*nat4_tcp, *nat4_udp, *nat4_icmp, *nat4_tcp, *nat4_udp, *nat4_icmp,
*nat4_tcp_fragments, *nat4_saved_fragments; *nat4_tcp_fragments;
extern linkedlist_t *timeout_icmp, *timeout_udp,
*timeout_tcp_est, *timeout_tcp_trans,
*timeout_tcp_fragments;
void nat_init(void); void nat_init(void);
void nat_quit(void); void nat_quit(void);
@ -47,12 +56,16 @@ void nat_quit(void);
struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4, struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4,
struct s_mac_addr eth_src, struct s_mac_addr eth_src,
struct s_ipv6_addr ipv6_src, struct s_ipv6_addr ipv6_dst, struct s_ipv6_addr ipv6_src, struct s_ipv6_addr ipv6_dst,
unsigned short port_src, unsigned short port_dst); unsigned short port_src, unsigned short port_dst,
unsigned char create);
struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src,
unsigned short port_src, unsigned short port_dst); unsigned short port_src, unsigned short port_dst);
void *nat_in_fragments(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, struct s_nat_fragments *nat_in_fragments(radixtree_t *nat,
unsigned short id, void *data); linkedlist_t *nat_timeout,
struct s_ipv4_addr ipv4_src,
unsigned short id);
void nat_in_fragments_cleanup(radixtree_t *nat_proto4, void nat_in_fragments_cleanup(radixtree_t *nat_proto4,
struct s_ipv4_addr ipv4_src, unsigned short id); struct s_ipv4_addr ipv4_src, unsigned short id);
void nat_cleaning(void);
#endif /* NAT_H */ #endif /* NAT_H */

View File

@ -82,7 +82,6 @@ void radixtree_insert(radixtree_t *root,
chunks = chunker(search_data, size, &chunk_count); chunks = chunker(search_data, size, &chunk_count);
tmp = root; tmp = root;
tmp->count++;
for (i = 0; i < chunk_count; i++) { for (i = 0; i < chunk_count; i++) {
unsigned char id; unsigned char id;
@ -91,15 +90,17 @@ void radixtree_insert(radixtree_t *root,
if (i == chunk_count - 1) { if (i == chunk_count - 1) {
tmp->array[id] = data; tmp->array[id] = data;
tmp->count++;
free(chunks);
return; return;
} }
if (tmp->array[id] == NULL) { if (tmp->array[id] == NULL) {
tmp->array[id] = radixtree_create(); tmp->array[id] = radixtree_create();
tmp->count++;
} }
tmp = tmp->array[id]; tmp = tmp->array[id];
tmp->count++;
} }
free(chunks); free(chunks);

157
src/tcp.c
View File

@ -53,9 +53,9 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
unsigned short tmp_short; unsigned short tmp_short;
unsigned char *packet; unsigned char *packet;
unsigned char *saved_packet; unsigned char *saved_packet;
linkedlist_t *queue; struct s_nat_fragments *frag_conn;
linkedlist_node_t *llnode; linkedlist_node_t *llnode;
struct s_ethernet *eth6; struct s_ethernet *eth6;
struct s_ipv6 *ip6; struct s_ipv6 *ip6;
@ -98,17 +98,74 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
return 1; return 1;
} }
/* TCP state machine */
switch (connection->state) {
case TCP_STATE_EST:
if (tcp->flags & TCP_FLAG_FIN) {
connection->state = TCP_STATE_FIN4;
break;
} else if (tcp->flags & TCP_FLAG_RST) {
connection->state = TCP_STATE_TRANS;
linkedlist_move2end(timeout_tcp_trans, connection->llnode);
break;
} else {
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
}
case TCP_STATE_INIT:
if (tcp->flags & TCP_FLAG_SYN) {
connection->state = TCP_STATE_EST;
linkedlist_move2end(timeout_tcp_est, connection->llnode);
}
break;
case TCP_STATE_FIN4:
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
case TCP_STATE_FIN6:
if (tcp->flags & TCP_FLAG_FIN) {
connection->state = TCP_STATE_FIN64;
linkedlist_move2end(timeout_tcp_trans, connection->llnode);
break;
} else {
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
}
case TCP_STATE_FIN64:
break;
case TCP_STATE_TRANS:
if (tcp->flags & TCP_FLAG_RST) {
break;
} else {
connection->state = TCP_STATE_EST;
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
}
}
/* if it's fragmented, save it to fragments table */ /* if it's fragmented, save it to fragments table */
if (ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) { if (ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) {
nat_in_fragments(nat4_tcp_fragments, ip4->ip_src, if ((frag_conn = nat_in_fragments(nat4_tcp_fragments,
ip4->id, connection); timeout_tcp_fragments, ip4->ip_src, ip4->id)) ==
NULL) {
return 1;
}
/* what is probability that there is already some other
* connection? if there is such connection then there is
* just a little chance to fix something as normally all
* our fragments are already processed at this moment */
frag_conn->connection = connection;
/* check if there are any saved fragments */ /* check if there are any saved fragments */
if ((queue = nat_in_fragments(nat4_saved_fragments, if (frag_conn->queue != NULL) {
ip4->ip_src, ip4->id, NULL)) != NULL) {
log_debug("Processing TCP fragments of %d", log_debug("Processing TCP fragments of %d",
ip4->id); ip4->id);
llnode = queue->first.next; llnode = frag_conn->queue->first.next;
while (llnode->next != NULL) { while (llnode->next != NULL) {
llnode = llnode->next; llnode = llnode->next;
memcpy(&tmp_short, llnode->prev->data, memcpy(&tmp_short, llnode->prev->data,
@ -127,7 +184,8 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
sizeof(struct s_ipv4)), sizeof(struct s_ipv4)),
tmp_short); tmp_short);
free(llnode->prev->data); free(llnode->prev->data);
linkedlist_delete(queue, llnode->prev); linkedlist_delete(frag_conn->queue,
llnode->prev);
} }
} }
} }
@ -230,10 +288,12 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
} }
} else { } else {
/* find connection in fragments table */ /* find connection in fragments table */
connection = nat_in_fragments(nat4_tcp_fragments, ip4->ip_src, if ((frag_conn = nat_in_fragments(nat4_tcp_fragments,
ip4->id, NULL); timeout_tcp_fragments, ip4->ip_src, ip4->id)) == NULL) {
return 1;
}
if (connection == NULL) { if (frag_conn->connection == NULL) {
log_debug("Incoming connection wasn't found in " log_debug("Incoming connection wasn't found in "
"fragments table -- saving it"); "fragments table -- saving it");
@ -244,19 +304,13 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
return 1; return 1;
} }
/* first lookup */
connection = nat_in_fragments(nat4_saved_fragments,
ip4->ip_src, ip4->id,
NULL);
/* if unsuccessful, create a queue and put into tree */ /* if unsuccessful, create a queue and put into tree */
if (connection == NULL) { if (frag_conn->queue == NULL) {
if ((queue = linkedlist_create()) == NULL) { if ((frag_conn->queue = linkedlist_create()) ==
NULL) {
free(saved_packet); free(saved_packet);
return 1; return 1;
} }
nat_in_fragments(nat4_saved_fragments,
ip4->ip_src, ip4->id, queue);
} }
/* save the packet and put it into the queue */ /* save the packet and put it into the queue */
@ -274,7 +328,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
sizeof(struct s_ethernet) + sizeof(struct s_ethernet) +
sizeof(struct s_ipv4)), payload, payload_size); sizeof(struct s_ipv4)), payload, payload_size);
linkedlist_append(queue, saved_packet); linkedlist_append(frag_conn->queue, saved_packet);
return 0; return 0;
} }
@ -295,7 +349,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
sizeof(struct s_ipv6)); sizeof(struct s_ipv6));
/* build ethernet header */ /* build ethernet header */
eth6->dest = connection->mac; eth6->dest = frag_conn->connection->mac;
eth6->src = mac; eth6->src = mac;
eth6->type = htons(ETHERTYPE_IPV6); eth6->type = htons(ETHERTYPE_IPV6);
@ -427,13 +481,68 @@ int tcp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
/* find connection in NAT */ /* find connection in NAT */
connection = nat_out(nat6_tcp, nat4_tcp, eth6->src, connection = nat_out(nat6_tcp, nat4_tcp, eth6->src,
ip6->ip_src, ip6->ip_dest, ip6->ip_src, ip6->ip_dest,
tcp->port_src, tcp->port_dest); tcp->port_src, tcp->port_dest,
tcp->flags & TCP_FLAG_SYN);
if (connection == NULL) { if (connection == NULL) {
log_warn("Outgoing connection wasn't found/created in NAT"); log_warn("Outgoing connection wasn't found/created in NAT");
return 1; return 1;
} }
/* TCP state machine */
switch (connection->state) {
case TCP_STATE_EST:
if (tcp->flags & TCP_FLAG_FIN) {
connection->state = TCP_STATE_FIN6;
break;
} else if (tcp->flags & TCP_FLAG_RST) {
connection->state = TCP_STATE_TRANS;
linkedlist_move2end(timeout_tcp_trans, connection->llnode);
break;
} else {
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
}
case TCP_STATE_INIT:
if (tcp->flags & TCP_FLAG_SYN) {
if (connection->llnode == NULL) {
connection->llnode = linkedlist_append(timeout_tcp_trans, connection);
break;
} else {
linkedlist_move2end(timeout_tcp_trans, connection->llnode);
break;
}
}
break;
case TCP_STATE_FIN4:
if (tcp->flags & TCP_FLAG_FIN) {
connection->state = TCP_STATE_FIN64;
linkedlist_move2end(timeout_tcp_trans, connection->llnode);
break;
} else {
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
}
case TCP_STATE_FIN6:
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
case TCP_STATE_FIN64:
break;
case TCP_STATE_TRANS:
if (tcp->flags & TCP_FLAG_RST) {
break;
} else {
connection->state = TCP_STATE_EST;
linkedlist_move2end(timeout_tcp_est, connection->llnode);
break;
}
}
/* allocate memory for translated packet */ /* allocate memory for translated packet */
if ((packet = (unsigned char *) malloc(sizeof(struct s_ipv4) + if ((packet = (unsigned char *) malloc(sizeof(struct s_ipv4) +
htons(ip6->len))) == NULL) { htons(ip6->len))) == NULL) {

View File

@ -19,6 +19,17 @@
#ifndef TCP_H #ifndef TCP_H
#define TCP_H #define TCP_H
#define TCP_FLAG_FIN 0x01
#define TCP_FLAG_SYN 0x02
#define TCP_FLAG_RST 0x04
#define TCP_STATE_INIT 1
#define TCP_STATE_EST 2
#define TCP_STATE_FIN4 3
#define TCP_STATE_FIN6 4
#define TCP_STATE_FIN64 5
#define TCP_STATE_TRANS 6
/* TCP header structure */ /* TCP header structure */
struct s_tcp { struct s_tcp {
unsigned short port_src; /* 16 b; source port */ unsigned short port_src; /* 16 b; source port */

View File

@ -26,6 +26,7 @@
#include "ipv4.h" #include "ipv4.h"
#include "ipv6.h" #include "ipv6.h"
#include "log.h" #include "log.h"
#include "linkedlist.h"
#include "nat.h" #include "nat.h"
#include "transmitter.h" #include "transmitter.h"
#include "udp.h" #include "udp.h"
@ -81,6 +82,8 @@ int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
return 1; return 1;
} }
linkedlist_move2end(timeout_udp, connection->llnode);
/* allocate memory for translated packet */ /* allocate memory for translated packet */
if ((packet = (unsigned char *) malloc(sizeof(struct s_ethernet) + if ((packet = (unsigned char *) malloc(sizeof(struct s_ethernet) +
sizeof(struct s_ipv6) + sizeof(struct s_ipv6) +
@ -175,13 +178,19 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
/* find connection in NAT */ /* find connection in NAT */
connection = nat_out(nat6_udp, nat4_udp, eth6->src, connection = nat_out(nat6_udp, nat4_udp, eth6->src,
ip6->ip_src, ip6->ip_dest, ip6->ip_src, ip6->ip_dest,
udp->port_src, udp->port_dest); udp->port_src, udp->port_dest, 1);
if (connection == NULL) { if (connection == NULL) {
log_warn("Outgoing connection wasn't found/created in NAT!"); log_warn("Outgoing connection wasn't found/created in NAT!");
return 1; return 1;
} }
if (connection->llnode == NULL) {
connection->llnode = linkedlist_append(timeout_udp, connection);
} else {
linkedlist_move2end(timeout_udp, connection->llnode);
}
/* allocate memory for translated packet */ /* allocate memory for translated packet */
packet_size = sizeof(struct s_ipv4) + htons(ip6->len); packet_size = sizeof(struct s_ipv4) + htons(ip6->len);
if ((packet = (unsigned char *) malloc(packet_size)) == NULL) { if ((packet = (unsigned char *) malloc(packet_size)) == NULL) {
@ -194,7 +203,7 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
ip4->ver_hdrlen = 0x45; /* ver 4, header length 20 B */ ip4->ver_hdrlen = 0x45; /* ver 4, header length 20 B */
ip4->tos = ((ip6->ver & 0x0f) << 4) | ip4->tos = ((ip6->ver & 0x0f) << 4) |
((ip6->traffic_class & 0xf0) >> 4); ((ip6->traffic_class & 0xf0) >> 4);
ip4->len = htons(sizeof(struct s_ipv4) + htons(ip6->len)); ip4->len = htons(packet_size);
ip4->id = 0x0; ip4->id = 0x0;
ip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); ip4->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT);
ip4->ttl = ip6->hop_limit; ip4->ttl = ip6->hop_limit;
@ -220,7 +229,7 @@ int udp_ipv6(struct s_ethernet *eth6, struct s_ipv6 *ip6, char *payload)
ip4->checksum = checksum(ip4, sizeof(struct s_ipv4)); ip4->checksum = checksum(ip4, sizeof(struct s_ipv4));
/* send translated packet */ /* send translated packet */
transmit_ipv4(&ip4->ip_dest, packet, htons(ip4->len)); transmit_ipv4(&ip4->ip_dest, packet, packet_size);
/* clean-up */ /* clean-up */
free(packet); free(packet);

View File

@ -25,7 +25,7 @@
#include <stdlib.h> /* srand */ #include <stdlib.h> /* srand */
#include <string.h> /* strncpy */ #include <string.h> /* strncpy */
#include <sys/ioctl.h> /* ioctl, SIOCGIFINDEX */ #include <sys/ioctl.h> /* ioctl, SIOCGIFINDEX */
#include <time.h> /* time */ #include <time.h> /* time, time_t */
#include <unistd.h> /* close */ #include <unistd.h> /* close */
#include "arp.h" #include "arp.h"
@ -56,13 +56,13 @@ int main(int argc, char **argv)
{ {
struct packet_mreq pmr; struct packet_mreq pmr;
struct sockaddr_ll addr;
size_t addr_size;
int sniff_sock; int sniff_sock;
int length; int length;
char buffer[PACKET_BUFFER]; char buffer[PACKET_BUFFER];
int i;
time_t prevtime, curtime;
log_info(PACKAGE_STRING " is starting"); log_info(PACKAGE_STRING " is starting");
/* initialize the socket for sniffing */ /* initialize the socket for sniffing */
@ -123,15 +123,27 @@ int main(int argc, char **argv)
/* initiate random numbers generator */ /* initiate random numbers generator */
srand((unsigned int) time(NULL)); srand((unsigned int) time(NULL));
/* initialize time */
prevtime = time(NULL);
/* sniff! :c) */ /* sniff! :c) */
for (;;) { for (i = 1;; i++) {
addr_size = sizeof(addr);
if ((length = recv(sniff_sock, buffer, PACKET_BUFFER, 0)) == -1) { if ((length = recv(sniff_sock, buffer, PACKET_BUFFER, 0)) == -1) {
log_error("Unable to retrieve data from socket"); log_error("Unable to retrieve data from socket");
return 1; return 1;
} }
process((char *) &buffer); process((char *) &buffer);
if (i % 250000) {
curtime = time(NULL);
/* 2 seconds is minimum normal timeout */
if ((curtime - prevtime) >= 2) {
nat_cleaning();
prevtime = curtime;
}
i = 0;
}
} }
/* clean-up */ /* clean-up */