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:
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 */
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
125
src/tcp.c
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user