Browse files

Handling of TCPv4 fragments

With support for refragmenting too big fragments
  • Loading branch information...
1 parent 9ac0658 commit 754319efc2e2adc404493ced589a08095b8a5eb3 @xHire committed Apr 27, 2012
Showing with 224 additions and 5 deletions.
  1. +93 −1 src/nat.c
  2. +7 −1 src/nat.h
  3. +124 −3 src/tcp.c
View
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));
+ }
+}
View
8 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 */
View
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 */

0 comments on commit 754319e

Please sign in to comment.