From 754319efc2e2adc404493ced589a08095b8a5eb3 Mon Sep 17 00:00:00 2001 From: Michal Zima Date: Fri, 27 Apr 2012 16:43:31 +0200 Subject: [PATCH] Handling of TCPv4 fragments With support for refragmenting too big fragments --- src/nat.c | 94 +++++++++++++++++++++++++++++++++++++++- src/nat.h | 8 +++- src/tcp.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 224 insertions(+), 5 deletions(-) diff --git a/src/nat.c b/src/nat.c index eeb2aac..d250a1e 100644 --- a/src/nat.c +++ b/src/nat.c @@ -41,8 +41,14 @@ struct s_radixtree_nat4 { unsigned char zeros; /* unused space */ } __attribute__ ((__packed__)); +struct s_radixtree_fragments4 { + struct s_ipv4_addr addr; + unsigned short id; +} __attribute__ ((__packed__)); + radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp, - *nat4_tcp, *nat4_udp, *nat4_icmp; + *nat4_tcp, *nat4_udp, *nat4_icmp, + *nat4_tcp_fragments; void nat_init(void) { @@ -53,6 +59,8 @@ void nat_init(void) nat4_tcp = radixtree_create(); nat4_udp = radixtree_create(); nat4_icmp = radixtree_create(); + + nat4_tcp_fragments = radixtree_create(); } void nat_quit(void) @@ -66,6 +74,9 @@ void nat_quit(void) radixtree_destroy(nat4_tcp, 12); radixtree_destroy(nat4_udp, 12); radixtree_destroy(nat4_icmp, 12); + + /* 32 + 16 = 48 / 6 = 8 */ + radixtree_destroy(nat4_tcp_fragments, 8); } struct s_nat *nat_out(radixtree_t *nat_proto6, radixtree_t *nat_proto4, @@ -147,3 +158,84 @@ struct s_nat *nat_in(radixtree_t *nat_proto4, struct s_ipv4_addr ipv4_src, return result; } } + +/** + * 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 + */ +void nat_in_fragments_clenup(radixtree_t *nat_proto4, + struct s_ipv4_addr ipv4_src, unsigned short id) +{ + /* 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)); + } +} diff --git a/src/nat.h b/src/nat.h index 278e28e..79121a2 100644 --- a/src/nat.h +++ b/src/nat.h @@ -38,7 +38,8 @@ struct s_nat { }; extern radixtree_t *nat6_tcp, *nat6_udp, *nat6_icmp, - *nat4_tcp, *nat4_udp, *nat4_icmp; + *nat4_tcp, *nat4_udp, *nat4_icmp, + *nat4_tcp_fragments; void nat_init(void); void nat_quit(void); @@ -49,5 +50,10 @@ 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_clenup(radixtree_t *nat_proto4, + struct s_ipv4_addr ipv4_src, unsigned short id); #endif /* NAT_H */ diff --git a/src/tcp.c b/src/tcp.c index 3232b34..3b62d5d 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -93,6 +93,12 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, return 1; } + /* 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); + } + /* allocate enough memory for translated packet */ if ((packet = (unsigned char *) malloc( payload_size > MTU - sizeof(struct s_ipv6) ? @@ -190,9 +196,124 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload, sizeof(struct s_ipv6) + payload_size); } } else { - /* TODO: handle all fragments */ - printf("[Debug] Can't handle fragments :c(\n"); - return 1; + /* find connection in fragments table */ + connection = nat_in_fragments(nat4_tcp_fragments, ip4->ip_src, + ip4->id, NULL); + + if (connection == NULL) { + printf("[Debug] Incoming connection wasn't found in " + "fragments table\n"); + return 1; + } + + /* allocate enough memory for translated packet */ + if ((packet = (unsigned char *) malloc( + payload_size > MTU - sizeof(struct s_ipv6) - + sizeof(struct s_ipv6_fragment) ? + MTU + sizeof(struct s_ethernet) : + sizeof(struct s_ethernet) + sizeof(struct s_ipv6) + + sizeof(struct s_ipv6_fragment) + payload_size)) == NULL) { + fprintf(stderr, "[Error] Lack of free memory\n"); + return 1; + } + eth6 = (struct s_ethernet *) packet; + ip6 = (struct s_ipv6 *) (packet + sizeof(struct s_ethernet)); + frag = (struct s_ipv6_fragment *) ((unsigned char *) ip6 + + sizeof(struct s_ipv6)); + + /* build ethernet header */ + eth6->dest = connection->mac; + eth6->src = mac; + eth6->type = htons(ETHERTYPE_IPV6); + + /* build IPv6 header */ + ip6->ver = 0x60 | (ip4->tos >> 4); + ip6->traffic_class = ip4->tos << 4; + ip6->flow_label = 0x0; + ip6->hop_limit = ip4->ttl; + ip6->next_header = IPPROTO_FRAGMENT; + ipv4_to_ipv6(&ip4->ip_src, &ip6->ip_src); + memcpy(&ip6->ip_dest, &connection->ipv6, + sizeof(struct s_ipv6_addr)); + + /* build IPv6 fragment header */ + frag->next_header = IPPROTO_TCP; + frag->zeros = 0x0; + frag->id = htonl(htons(ip4->id)); + + /* fragment the fragment or not? */ + if (payload_size > MTU - sizeof(struct s_ipv6) - + sizeof(struct s_ipv6_fragment)) { + /* fill in missing IPv6 header fields */ + ip6->len = htons(FRAGMENT_LEN + + sizeof(struct s_ipv6_fragment)); + + /* fill in missing IPv6 fragment header fields */ + frag->offset_flag = htons((htons(ip4->flags_offset) << 3) | + IPV6_FLAG_MORE_FRAGMENTS); + + /* copy the payload data */ + memcpy((unsigned char *) frag + + sizeof(struct s_ipv6_fragment), + payload, FRAGMENT_LEN); + + /* send translated packet */ + transmit_raw(packet, sizeof(struct s_ethernet) + MTU); + + /* create the second fragment */ + ip6->len = htons(payload_size + + sizeof(struct s_ipv6_fragment) - + FRAGMENT_LEN); + frag->offset_flag = htons((htons(ip4->flags_offset) + + FRAGMENT_LEN / 8) << 3); + if (ip4->flags_offset & + htons(IPV4_FLAG_MORE_FRAGMENTS)) { + frag->offset_flag |= + htons(IPV6_FLAG_MORE_FRAGMENTS); + } + + /* copy the payload data */ + memcpy((unsigned char *) frag + + sizeof(struct s_ipv6_fragment), + payload + FRAGMENT_LEN, + payload_size - FRAGMENT_LEN); + + /* send translated packet */ + transmit_raw(packet, sizeof(struct s_ethernet) + + sizeof(struct s_ipv6) + + sizeof(struct s_ipv6_fragment) - + FRAGMENT_LEN + payload_size); + } else { + /* fill in missing IPv6 header fields */ + ip6->len = htons(payload_size + + sizeof(struct s_ipv6_fragment)); + + /* fill in missing IPv6 fragment header fields */ + frag->offset_flag = htons(htons(ip4->flags_offset) << 3); + if (ip4->flags_offset & + htons(IPV4_FLAG_MORE_FRAGMENTS)) { + frag->offset_flag |= + htons(IPV6_FLAG_MORE_FRAGMENTS); + } + + /* copy the payload data */ + memcpy((unsigned char *) ip6 + sizeof(struct s_ipv6) + + sizeof(struct s_ipv6_fragment), + payload, payload_size); + + /* send translated packet */ + transmit_raw(packet, sizeof(struct s_ethernet) + + sizeof(struct s_ipv6) + + 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))) { + printf("[Debug] Removing fragment entry\n"); + nat_in_fragments_clenup(nat4_tcp_fragments, + ip4->ip_src, ip4->id); + } } /* clean-up */