mirror of
https://code.semirocket.science/wrapsix
synced 2024-11-24 11:48:25 +02:00
Handling of TCPv4 fragments
With support for refragmenting too big fragments
This commit is contained in:
parent
9ac06588a8
commit
754319efc2
94
src/nat.c
94
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));
|
||||
}
|
||||
}
|
||||
|
@ -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 */
|
||||
|
127
src/tcp.c
127
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user