diff --git a/configure.ac b/configure.ac index 5abf3e1..913aa44 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_ARG_ENABLE([debug], [debug=no]) 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]) else AM_CFLAGS="${AM_CFLAGS} -O2" diff --git a/src/icmp.c b/src/icmp.c index 618bff2..cbb713f 100644 --- a/src/icmp.c +++ b/src/icmp.c @@ -24,6 +24,7 @@ #include "checksum.h" #include "icmp.h" #include "ipv6.h" +#include "linkedlist.h" #include "log.h" #include "nat.h" #include "transmitter.h" @@ -86,6 +87,8 @@ int icmp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, return 1; } + linkedlist_move2end(timeout_icmp, connection->llnode); + echo->id = connection->ipv6_port_src; /* 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, eth6->src, ip6->ip_src, ip6->ip_dest, - echo->id, 0); + echo->id, 0, 1); if (connection == NULL) { 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; } + if (connection->llnode == NULL) { + connection->llnode = + linkedlist_append(timeout_icmp, + connection); + } else { + linkedlist_move2end(timeout_icmp, + connection->llnode); + } + echo->id = connection->ipv4_port_src; /* 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 */ if (memcmp(&wrapsix_ipv6_prefix, &ndp_ns->target, 12) != 0) { - log_debug("This is unfamiliar NDP packet"); return 1; } diff --git a/src/linkedlist.c b/src/linkedlist.c index 5073b62..bda3729 100644 --- a/src/linkedlist.c +++ b/src/linkedlist.c @@ -17,6 +17,7 @@ */ #include /* free, malloc */ +#include /* time, time_t */ #include "linkedlist.h" #include "log.h" @@ -65,27 +66,28 @@ void linkedlist_destroy(linkedlist_t *root) * @param root Root of the linked list * @param data Data to append to the list * - * @return 0 for success - * @return 1 for failure + * @return pointer to created node if succeeded + * @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; if ((node = (linkedlist_node_t *) malloc(sizeof(linkedlist_node_t))) == NULL) { log_error("Lack of free memory"); - return 1; + return NULL; } node->data = data; + node->time = time(NULL); node->prev = root->last.prev; node->next = &root->last; root->last.prev->next = 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); 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); +} diff --git a/src/linkedlist.h b/src/linkedlist.h index 4b7d6a7..a1a911a 100644 --- a/src/linkedlist.h +++ b/src/linkedlist.h @@ -19,11 +19,14 @@ #ifndef LINKEDLIST_H #define LINKEDLIST_H +#include /* time_t */ + /* Linked list node structure */ typedef struct s_linkedlist_node { struct s_linkedlist_node *prev; struct s_linkedlist_node *next; void *data; + time_t time; } linkedlist_node_t; /* Linked list root structure */ @@ -34,7 +37,8 @@ typedef struct s_linkedlist { linkedlist_t *linkedlist_create(void); 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_move2end(linkedlist_t *root, linkedlist_node_t *node); #endif /* LINKEDLIST_H */ diff --git a/src/nat.c b/src/nat.c index 4d64cda..70f86fb 100644 --- a/src/nat.c +++ b/src/nat.c @@ -48,7 +48,16 @@ struct s_radixtree_fragments4 { radixtree_t *nat6_tcp, *nat6_udp, *nat6_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. @@ -64,7 +73,12 @@ void nat_init(void) nat4_icmp = 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 */ 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. * - * @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 + * @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 + * @param create Whether or not to create new NAT entry * - * @return NULL when it wasn't possible to create connection - * @return pointer to connection structure otherwise + * @return NULL when it wasn't possible to create connection + * @return pointer to connection structure otherwise */ struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4, struct s_mac_addr eth_src, 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; @@ -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, radixtree_chunker, &radixsearch6, sizeof(radixsearch6))) == NULL) { - /* if no connection is found, let's create one */ - if ((connection = - (struct s_nat *) malloc(sizeof(struct s_nat))) == NULL) { - log_error("Lack of free memory"); + if (create > 0) { + /* if no connection is found, let's create one */ + if ((connection = + (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; } - - 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 { - /* when connection is found, refresh it and return */ - result->last_packet = time(NULL); + /* when connection is found, return it */ 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. * - * @param nat_proto4 NAT table - * @param ipv4_src Source IPv4 address - * @param port_src Source port - * @param port_dst Destination port + * @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 + * @return NULL when no connection was found + * @return pointer to connection structure otherwise */ struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, 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.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 */ return NULL; } else { - /* when connection is found, refresh it and return */ - result->last_packet = time(NULL); + /* when connection is found, return it */ 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 ipv4_src Source IPv4 address * @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, - unsigned short id, void *data) +struct s_nat_fragments *nat_in_fragments(radixtree_t *nat_proto4, + 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 */ 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; if ((result = radixtree_lookup(nat_proto4, radixtree_chunker, - &radixsearch4, sizeof(radixsearch4))) == NULL) { - if (data != NULL) { - /* when fragmentation is not found, add one */ - radixtree_insert(nat_proto4, radixtree_chunker, - &radixsearch4, sizeof(radixsearch4), - data); - return data; - } else { - /* nothing found and nothing to be added */ + &radixsearch4, sizeof(radixsearch4))) != NULL) { + return result; + } else { + /* when fragmentation is not found, add one */ + if ((result = (struct s_nat_fragments *) + malloc(sizeof(struct s_nat_fragments))) == NULL) { + log_error("Lack of free memory"); return NULL; } - } else { - if (data != NULL) { - /* when fragmentation is found, is it the same? */ - if (result == data) { - /* 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), data); - return data; - } - } else { - return result; - } + + result->id = id; + result->connection = NULL; + result->queue = NULL; + + radixtree_insert(nat_proto4, radixtree_chunker, + &radixsearch4, sizeof(radixsearch4), + result); + linkedlist_append(nat_timeout, result); + + return result; } } @@ -269,3 +293,106 @@ void nat_in_fragments_cleanup(radixtree_t *nat_proto4, 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); + } +} diff --git a/src/nat.h b/src/nat.h index 871521d..37231c9 100644 --- a/src/nat.h +++ b/src/nat.h @@ -19,11 +19,10 @@ #ifndef NAT_H #define NAT_H -#include /* time_t */ - #include "ethernet.h" /* s_mac_addr */ #include "ipv4.h" /* s_ipv4_addr */ #include "ipv6.h" /* s_ipv6_addr */ +#include "linkedlist.h" /* linkedlist_h */ #include "radixtree.h" /* radixtree_t */ struct s_nat { @@ -33,13 +32,23 @@ struct s_nat { unsigned short ipv6_port_src; unsigned short ipv4_port_src; unsigned short ipv4_port_dst; - time_t last_packet; /* time of processing last - packet of the connection */ + unsigned char state; + 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, *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_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_mac_addr eth_src, 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, unsigned short port_src, unsigned short port_dst); -void *nat_in_fragments(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, - unsigned short id, void *data); +struct s_nat_fragments *nat_in_fragments(radixtree_t *nat, + linkedlist_t *nat_timeout, + struct s_ipv4_addr ipv4_src, + unsigned short id); void nat_in_fragments_cleanup(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, unsigned short id); +void nat_cleaning(void); #endif /* NAT_H */ diff --git a/src/radixtree.c b/src/radixtree.c index 2093514..2549638 100644 --- a/src/radixtree.c +++ b/src/radixtree.c @@ -82,7 +82,6 @@ void radixtree_insert(radixtree_t *root, chunks = chunker(search_data, size, &chunk_count); tmp = root; - tmp->count++; for (i = 0; i < chunk_count; i++) { unsigned char id; @@ -91,15 +90,17 @@ void radixtree_insert(radixtree_t *root, if (i == chunk_count - 1) { tmp->array[id] = data; + tmp->count++; + free(chunks); return; } if (tmp->array[id] == NULL) { tmp->array[id] = radixtree_create(); + tmp->count++; } tmp = tmp->array[id]; - tmp->count++; } free(chunks); diff --git a/src/tcp.c b/src/tcp.c index f8c88a6..936cf06 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -53,9 +53,9 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, unsigned short tmp_short; unsigned char *packet; - unsigned char *saved_packet; - linkedlist_t *queue; - linkedlist_node_t *llnode; + unsigned char *saved_packet; + struct s_nat_fragments *frag_conn; + linkedlist_node_t *llnode; struct s_ethernet *eth6; struct s_ipv6 *ip6; @@ -98,17 +98,74 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, 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 (ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) { - nat_in_fragments(nat4_tcp_fragments, ip4->ip_src, - ip4->id, connection); + if ((frag_conn = nat_in_fragments(nat4_tcp_fragments, + 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 */ - if ((queue = nat_in_fragments(nat4_saved_fragments, - ip4->ip_src, ip4->id, NULL)) != NULL) { + if (frag_conn->queue != NULL) { log_debug("Processing TCP fragments of %d", ip4->id); - llnode = queue->first.next; + llnode = frag_conn->queue->first.next; while (llnode->next != NULL) { llnode = llnode->next; 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)), tmp_short); 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 { /* find connection in fragments table */ - connection = nat_in_fragments(nat4_tcp_fragments, ip4->ip_src, - ip4->id, NULL); + if ((frag_conn = nat_in_fragments(nat4_tcp_fragments, + 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 " "fragments table -- saving it"); @@ -244,19 +304,13 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, 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 (connection == NULL) { - if ((queue = linkedlist_create()) == NULL) { + if (frag_conn->queue == NULL) { + if ((frag_conn->queue = linkedlist_create()) == + NULL) { free(saved_packet); return 1; } - nat_in_fragments(nat4_saved_fragments, - ip4->ip_src, ip4->id, 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_ipv4)), payload, payload_size); - linkedlist_append(queue, saved_packet); + linkedlist_append(frag_conn->queue, saved_packet); return 0; } @@ -295,7 +349,7 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, sizeof(struct s_ipv6)); /* build ethernet header */ - eth6->dest = connection->mac; + eth6->dest = frag_conn->connection->mac; eth6->src = mac; 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 */ connection = nat_out(nat6_tcp, nat4_tcp, eth6->src, 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) { log_warn("Outgoing connection wasn't found/created in NAT"); 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 */ if ((packet = (unsigned char *) malloc(sizeof(struct s_ipv4) + htons(ip6->len))) == NULL) { diff --git a/src/tcp.h b/src/tcp.h index 8d06a14..f4f2493 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -19,6 +19,17 @@ #ifndef 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 */ struct s_tcp { unsigned short port_src; /* 16 b; source port */ diff --git a/src/udp.c b/src/udp.c index 685eee1..ad453a0 100644 --- a/src/udp.c +++ b/src/udp.c @@ -26,6 +26,7 @@ #include "ipv4.h" #include "ipv6.h" #include "log.h" +#include "linkedlist.h" #include "nat.h" #include "transmitter.h" #include "udp.h" @@ -81,6 +82,8 @@ int udp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, return 1; } + linkedlist_move2end(timeout_udp, connection->llnode); + /* allocate memory for translated packet */ if ((packet = (unsigned char *) malloc(sizeof(struct s_ethernet) + 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 */ connection = nat_out(nat6_udp, nat4_udp, eth6->src, ip6->ip_src, ip6->ip_dest, - udp->port_src, udp->port_dest); + udp->port_src, udp->port_dest, 1); if (connection == NULL) { log_warn("Outgoing connection wasn't found/created in NAT!"); 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 */ packet_size = sizeof(struct s_ipv4) + htons(ip6->len); 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->tos = ((ip6->ver & 0x0f) << 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->flags_offset = htons(IPV4_FLAG_DONT_FRAGMENT); 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)); /* send translated packet */ - transmit_ipv4(&ip4->ip_dest, packet, htons(ip4->len)); + transmit_ipv4(&ip4->ip_dest, packet, packet_size); /* clean-up */ free(packet); diff --git a/src/wrapper.c b/src/wrapper.c index 633e1a6..ffaa23a 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -25,7 +25,7 @@ #include /* srand */ #include /* strncpy */ #include /* ioctl, SIOCGIFINDEX */ -#include /* time */ +#include /* time, time_t */ #include /* close */ #include "arp.h" @@ -56,13 +56,13 @@ int main(int argc, char **argv) { struct packet_mreq pmr; - struct sockaddr_ll addr; - size_t addr_size; - int sniff_sock; int length; char buffer[PACKET_BUFFER]; + int i; + time_t prevtime, curtime; + log_info(PACKAGE_STRING " is starting"); /* initialize the socket for sniffing */ @@ -123,15 +123,27 @@ int main(int argc, char **argv) /* initiate random numbers generator */ srand((unsigned int) time(NULL)); + /* initialize time */ + prevtime = time(NULL); + /* sniff! :c) */ - for (;;) { - addr_size = sizeof(addr); + for (i = 1;; i++) { if ((length = recv(sniff_sock, buffer, PACKET_BUFFER, 0)) == -1) { log_error("Unable to retrieve data from socket"); return 1; } 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 */