From 24b09b4d9334ad6366e79d1af0f04793ba77a920 Mon Sep 17 00:00:00 2001 From: Michal Zima Date: Sat, 14 Jul 2012 11:38:13 +0200 Subject: [PATCH] Saving and processing unknown incoming fragments New data structure -- linked list Reduced few useless debug outputs --- src/Makefile.am | 1 + src/ipv4.c | 1 - src/ipv6.c | 1 - src/linkedlist.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ src/linkedlist.h | 40 ++++++++++++++++++ src/nat.c | 37 ++++++++--------- src/nat.h | 7 ++-- src/tcp.c | 93 ++++++++++++++++++++++++++++++++++++------ src/wrapper.c | 2 - 9 files changed, 246 insertions(+), 40 deletions(-) create mode 100644 src/linkedlist.c create mode 100644 src/linkedlist.h diff --git a/src/Makefile.am b/src/Makefile.am index aa18816..35c3bd2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,6 +7,7 @@ wrapsix_wrapper_SOURCES = \ icmp.c icmp.h \ ipv4.c ipv4.h \ ipv6.c ipv6.h \ + linkedlist.c linkedlist.h \ log.c log.h \ nat.c nat.h \ radixtree.c radixtree.h \ diff --git a/src/ipv4.c b/src/ipv4.c index f3e0486..b14993b 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -47,7 +47,6 @@ int ipv4(struct s_ethernet *eth, char *packet) /* test if this packet belongs to us */ if (memcmp(&wrapsix_ipv4_addr, &ip->ip_dest, 4) != 0) { - log_debug("This is unfamiliar IPv4 packet"); return 1; } diff --git a/src/ipv6.c b/src/ipv6.c index 7db7e9a..34dce3f 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -47,7 +47,6 @@ int ipv6(struct s_ethernet *eth, char *packet) /* test if this packet belongs to us */ if (memcmp(&wrapsix_ipv6_prefix, &ip->ip_dest, 12) != 0 && memcmp(&ndp_multicast_addr, &ip->ip_dest, 13) != 0) { - log_debug("This is unfamiliar IPv6 packet"); return 1; } diff --git a/src/linkedlist.c b/src/linkedlist.c new file mode 100644 index 0000000..5073b62 --- /dev/null +++ b/src/linkedlist.c @@ -0,0 +1,104 @@ +/* + * WrapSix + * Copyright (C) 2008-2012 Michal Zima + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include /* free, malloc */ + +#include "linkedlist.h" +#include "log.h" + +/** + * Creates root of a linked list. + * + * @return Root of new linked list + */ +linkedlist_t *linkedlist_create(void) +{ + linkedlist_t *linkedlist; + + if ((linkedlist = (linkedlist_t *) malloc(sizeof(linkedlist_t))) == + NULL) { + log_error("Lack of free memory"); + return NULL; + } + linkedlist->first.prev = NULL; + linkedlist->first.next = &linkedlist->last; + linkedlist->last.prev = &linkedlist->first; + linkedlist->last.next = NULL; + + return linkedlist; +} + +/** + * Destroys a linked list. + * + * @param root Root of the linked list to destroy + */ +void linkedlist_destroy(linkedlist_t *root) +{ + linkedlist_node_t *tmp; + + tmp = root->first.next; + while (tmp->next != NULL) { + tmp = tmp->next; + free(tmp->prev); + } +} + +/** + * Append new node at the end of the linked list. + * + * @param root Root of the linked list + * @param data Data to append to the list + * + * @return 0 for success + * @return 1 for failure + */ +int 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; + } + + node->data = data; + + node->prev = root->last.prev; + node->next = &root->last; + root->last.prev->next = node; + root->last.prev = node; + + return 0; +} + +/** + * Delete node from beginning of linked list. + * + * @param root Root of the linked list + * @param node Node to delete from the list + */ +void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node) +{ + root->first.next = node->next; + node->next->prev = node->prev; + + free(node); + node = NULL; +} diff --git a/src/linkedlist.h b/src/linkedlist.h new file mode 100644 index 0000000..4b7d6a7 --- /dev/null +++ b/src/linkedlist.h @@ -0,0 +1,40 @@ +/* + * WrapSix + * Copyright (C) 2008-2012 Michal Zima + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LINKEDLIST_H +#define LINKEDLIST_H + +/* Linked list node structure */ +typedef struct s_linkedlist_node { + struct s_linkedlist_node *prev; + struct s_linkedlist_node *next; + void *data; +} linkedlist_node_t; + +/* Linked list root structure */ +typedef struct s_linkedlist { + linkedlist_node_t first; + linkedlist_node_t last; +} linkedlist_t; + +linkedlist_t *linkedlist_create(void); +void linkedlist_destroy(linkedlist_t *root); +int linkedlist_append(linkedlist_t *root, void *data); +void linkedlist_delete(linkedlist_t *root, linkedlist_node_t *node); + +#endif /* LINKEDLIST_H */ diff --git a/src/nat.c b/src/nat.c index e562764..4d64cda 100644 --- a/src/nat.c +++ b/src/nat.c @@ -48,7 +48,7 @@ struct s_radixtree_fragments4 { radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp, *nat4_tcp, *nat4_udp, *nat4_icmp, - *nat4_tcp_fragments; + *nat4_tcp_fragments, *nat4_saved_fragments; /** * Initialization of NAT tables. @@ -64,6 +64,7 @@ void nat_init(void) nat4_icmp = radixtree_create(); nat4_tcp_fragments = radixtree_create(); + nat4_saved_fragments = radixtree_create(); } /** @@ -83,6 +84,7 @@ void nat_quit(void) /* 32 + 16 = 48 / 6 = 8 */ radixtree_destroy(nat4_tcp_fragments, 8); + radixtree_destroy(nat4_saved_fragments, 8); } /** @@ -191,42 +193,41 @@ struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, } /** - * Save and retrieve NATted connections via fragment identification. + * Save and retrieve data (e.g. 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 + * @param data Data to save * - * @return Connection + * @return Data (or NULL when nothing found) */ -struct s_nat *nat_in_fragments(radixtree_t *nat_proto4, - struct s_ipv4_addr ipv4_src, - unsigned short id, struct s_nat *nat) +void *nat_in_fragments(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, + unsigned short id, void *data) { - struct s_nat *result; + void *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) { + 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), - nat); - return nat; + data); + return data; } else { /* nothing found and nothing to be added */ return NULL; } } else { - if (nat != NULL) { + if (data != NULL) { /* when fragmentation is found, is it the same? */ - if (result == nat) { + if (result == data) { /* OK, return */ return result; } else { @@ -238,12 +239,10 @@ struct s_nat *nat_in_fragments(radixtree_t *nat_proto4, sizeof(radixsearch4)); radixtree_insert(nat_proto4, radixtree_chunker, &radixsearch4, - sizeof(radixsearch4), nat); - return nat; + sizeof(radixsearch4), data); + return data; } } else { - /* refresh it's connection and return */ - result->last_packet = time(NULL); return result; } } diff --git a/src/nat.h b/src/nat.h index 78683b4..871521d 100644 --- a/src/nat.h +++ b/src/nat.h @@ -39,7 +39,7 @@ struct s_nat { extern radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp, *nat4_tcp, *nat4_udp, *nat4_icmp, - *nat4_tcp_fragments; + *nat4_tcp_fragments, *nat4_saved_fragments; void nat_init(void); void nat_quit(void); @@ -50,9 +50,8 @@ struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4, unsigned short port_src, unsigned short port_dst); struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, unsigned short port_src, unsigned short port_dst); -struct s_nat *nat_in_fragments(radixtree_t *nat_proto4, - struct s_ipv4_addr ipv4_src, - unsigned short id, struct s_nat *nat); +void *nat_in_fragments(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, + unsigned short id, void *data); void nat_in_fragments_cleanup(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, unsigned short id); diff --git a/src/tcp.c b/src/tcp.c index b5ba834..f8c88a6 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -25,6 +25,7 @@ #include "ethernet.h" #include "ipv4.h" #include "ipv6.h" +#include "linkedlist.h" #include "log.h" #include "nat.h" #include "tcp.h" @@ -49,9 +50,13 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, { struct s_tcp *tcp; struct s_nat *connection; - unsigned short orig_checksum; + unsigned short tmp_short; unsigned char *packet; + unsigned char *saved_packet; + linkedlist_t *queue; + linkedlist_node_t *llnode; + struct s_ethernet *eth6; struct s_ipv6 *ip6; struct s_ipv6_fragment *frag; @@ -59,7 +64,8 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, /* full processing of unfragmented packet or the first fragment with * TCP header */ - if ((ip4->flags_offset & htons(IPV4_FLAG_DONT_FRAGMENT)) || + if ((ip4->flags_offset | htons(IPV4_FLAG_DONT_FRAGMENT)) == + htons(IPV4_FLAG_DONT_FRAGMENT) || ((ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) && (ip4->flags_offset & 0xff1f) == 0x0000 && payload_size >= sizeof(struct s_tcp))) { @@ -69,13 +75,13 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, /* checksum recheck -- only if the packet is unfragmented */ if ((ip4->flags_offset | htons(IPV4_FLAG_DONT_FRAGMENT)) == htons(IPV4_FLAG_DONT_FRAGMENT)) { - orig_checksum = tcp->checksum; + tmp_short = tcp->checksum; tcp->checksum = 0; tcp->checksum = checksum_ipv4(ip4->ip_src, ip4->ip_dest, payload_size, IPPROTO_TCP, (unsigned char *) tcp); - if (tcp->checksum != orig_checksum) { + if (tcp->checksum != tmp_short) { /* packet is corrupted and shouldn't be * processed */ log_debug("Wrong checksum"); @@ -96,6 +102,34 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, if (ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS)) { nat_in_fragments(nat4_tcp_fragments, ip4->ip_src, ip4->id, connection); + + /* check if there are any saved fragments */ + if ((queue = nat_in_fragments(nat4_saved_fragments, + ip4->ip_src, ip4->id, NULL)) != NULL) { + log_debug("Processing TCP fragments of %d", + ip4->id); + llnode = queue->first.next; + while (llnode->next != NULL) { + llnode = llnode->next; + memcpy(&tmp_short, llnode->prev->data, + sizeof(unsigned short)); + tcp_ipv4((struct s_ethernet *) ( + (char *) llnode->prev->data + + sizeof(unsigned short)), + (struct s_ipv4 *) ( + (char *) llnode->prev->data + + sizeof(unsigned short) + + sizeof(struct s_ethernet)), + (char *) ( + (char *) llnode->prev->data + + sizeof(unsigned short) + + sizeof(struct s_ethernet) + + sizeof(struct s_ipv4)), + tmp_short); + free(llnode->prev->data); + linkedlist_delete(queue, llnode->prev); + } + } } /* allocate enough memory for translated packet */ @@ -201,8 +235,48 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, if (connection == NULL) { log_debug("Incoming connection wasn't found in " - "fragments table"); - return 1; + "fragments table -- saving it"); + + if ((saved_packet = (unsigned char *) malloc( + sizeof(unsigned short) + sizeof(struct s_ethernet) + + sizeof(struct s_ipv4) + payload_size)) == NULL) { + log_error("Lack of free memory"); + 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) { + 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 */ + memcpy(saved_packet, &payload_size, + sizeof(unsigned short)); + memcpy((unsigned char *) (saved_packet + + sizeof(unsigned short)), eth4, + sizeof(struct s_ethernet)); + memcpy((unsigned char *) (saved_packet + + sizeof(unsigned short) + + sizeof(struct s_ethernet)), ip4, + sizeof(struct s_ipv4)); + memcpy((unsigned char *) (saved_packet + + sizeof(unsigned short) + + sizeof(struct s_ethernet) + + sizeof(struct s_ipv4)), payload, payload_size); + + linkedlist_append(queue, saved_packet); + + return 0; } /* allocate enough memory for translated packet */ @@ -307,13 +381,6 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, sizeof(struct s_ipv6_fragment) + payload_size); } - - /* if this is the last fragment, remove the entry from table */ - if (!(ip4->flags_offset & htons(IPV4_FLAG_MORE_FRAGMENTS))) { - log_debug("Removing fragment entry"); - nat_in_fragments_cleanup(nat4_tcp_fragments, - ip4->ip_src, ip4->id); - } } /* clean-up */ diff --git a/src/wrapper.c b/src/wrapper.c index fa1df34..633e1a6 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -164,10 +164,8 @@ int process(char *packet) switch (htons(eth->type)) { case ETHERTYPE_IP: - log_debug("HW Protocol: IPv4"); return ipv4(eth, payload); case ETHERTYPE_IPV6: - log_debug("HW Protocol: IPv6"); return ipv6(eth, payload); case ETHERTYPE_ARP: log_debug("HW Protocol: ARP");