1
0
mirror of https://code.semirocket.science/wrapsix synced 2024-11-28 09:00:59 +02:00

Handling of TCPv4 fragments

With support for refragmenting too big fragments
This commit is contained in:
Michal Zima 2012-04-27 16:43:31 +02:00
parent 9ac06588a8
commit 754319efc2
3 changed files with 224 additions and 5 deletions

View File

@ -41,8 +41,14 @@ struct s_radixtree_nat4 {
unsigned char zeros; /* unused space */ unsigned char zeros; /* unused space */
} __attribute__ ((__packed__)); } __attribute__ ((__packed__));
struct s_radixtree_fragments4 {
struct s_ipv4_addr addr;
unsigned short id;
} __attribute__ ((__packed__));
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;
void nat_init(void) void nat_init(void)
{ {
@ -53,6 +59,8 @@ void nat_init(void)
nat4_tcp = radixtree_create(); nat4_tcp = radixtree_create();
nat4_udp = radixtree_create(); nat4_udp = radixtree_create();
nat4_icmp = radixtree_create(); nat4_icmp = radixtree_create();
nat4_tcp_fragments = radixtree_create();
} }
void nat_quit(void) void nat_quit(void)
@ -66,6 +74,9 @@ void nat_quit(void)
radixtree_destroy(nat4_tcp, 12); radixtree_destroy(nat4_tcp, 12);
radixtree_destroy(nat4_udp, 12); radixtree_destroy(nat4_udp, 12);
radixtree_destroy(nat4_icmp, 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, 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; 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));
}
}

View File

@ -38,7 +38,8 @@ struct s_nat {
}; };
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;
void nat_init(void); void nat_init(void);
void nat_quit(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); unsigned short port_src, unsigned short port_dst);
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);
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 */ #endif /* NAT_H */

125
src/tcp.c
View File

@ -93,6 +93,12 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
return 1; 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 */ /* allocate enough memory for translated packet */
if ((packet = (unsigned char *) malloc( if ((packet = (unsigned char *) malloc(
payload_size > MTU - sizeof(struct s_ipv6) ? payload_size > MTU - sizeof(struct s_ipv6) ?
@ -190,11 +196,126 @@ int tcp_ipv4(struct s_ethernet *eth4, struct s_ipv4 *ip4, char *payload,
sizeof(struct s_ipv6) + payload_size); sizeof(struct s_ipv6) + payload_size);
} }
} else { } else {
/* TODO: handle all fragments */ /* find connection in fragments table */
printf("[Debug] Can't handle fragments :c(\n"); 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; 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 */ /* clean-up */
free(packet); free(packet);