summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clatd.c52
-rw-r--r--clatd.h1
-rw-r--r--dump.c1
-rw-r--r--ipv4.c173
-rw-r--r--ipv4.h4
-rw-r--r--ipv6.c167
-rw-r--r--ipv6.h4
-rw-r--r--translate.c407
-rw-r--r--translate.h41
9 files changed, 383 insertions, 467 deletions
diff --git a/clatd.c b/clatd.c
index edd8a2d..8dddc96 100644
--- a/clatd.c
+++ b/clatd.c
@@ -39,6 +39,7 @@
#include <linux/icmp.h>
#include <sys/capability.h>
+#include <sys/uio.h>
#include <linux/prctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>
@@ -48,6 +49,7 @@
#include "ipv4.h"
#include "ipv6.h"
+#include "translate.h"
#include "clatd.h"
#include "config.h"
#include "logging.h"
@@ -306,20 +308,43 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix,
* packet - packet
* packetsize - size of packet
*/
-void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet, size_t packetsize) {
- tun_header->proto = ntohs(tun_header->proto);
+void packet_handler(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet,
+ size_t packetsize) {
+ int fd;
+ int iov_len = 0;
+
+ // Allocate buffers for all packet headers.
+ struct tun_pi tun_targ;
+ char iphdr[sizeof(struct ip6_hdr)];
+ char transporthdr[MAX_TCP_HDR];
+
+ // iovec of the packets we'll send. This gets passed down to the translation functions.
+ clat_packet out = {
+ { &tun_targ, sizeof(tun_targ) }, // Tunnel header.
+ { iphdr, 0 }, // IP header.
+ { transporthdr, 0 }, // Transport layer header.
+ { NULL, 0 }, // Payload. No buffer, it's a pointer to the original payload.
+ };
if(tun_header->flags != 0) {
logmsg(ANDROID_LOG_WARN,"packet_handler: unexpected flags = %d", tun_header->flags);
}
- if(tun_header->proto == ETH_P_IP) {
- ip_packet(tunnel->fd6,packet,packetsize);
- } else if(tun_header->proto == ETH_P_IPV6) {
- ipv6_packet(tunnel->fd4,packet,packetsize);
+ if(ntohs(tun_header->proto) == ETH_P_IP) {
+ fd = tunnel->fd6;
+ fill_tun_header(&tun_targ, ETH_P_IPV6);
+ iov_len = ipv4_packet(out, POS_IPHDR, packet, packetsize);
+ } else if(ntohs(tun_header->proto) == ETH_P_IPV6) {
+ fd = tunnel->fd4;
+ fill_tun_header(&tun_targ, ETH_P_IP);
+ iov_len = ipv6_packet(out, POS_IPHDR, packet, packetsize);
} else {
logmsg(ANDROID_LOG_WARN,"packet_handler: unknown packet type = %x",tun_header->proto);
}
+
+ if (iov_len > 0) {
+ writev(fd, out, iov_len);
+ }
}
/* function: read_packet
@@ -351,9 +376,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) {
return;
}
- memcpy(&tun_header, packet, header_size);
-
- packet_handler(tunnel, &tun_header, packet+header_size, readlen-header_size);
+ packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size);
}
}
@@ -434,29 +457,30 @@ int main(int argc, char **argv) {
}
if(uplink_interface == NULL) {
- logmsg(ANDROID_LOG_FATAL,"clatd called without an interface");
+ logmsg(ANDROID_LOG_FATAL, "clatd called without an interface");
printf("I need an interface\n");
exit(1);
}
- logmsg(ANDROID_LOG_INFO,"Starting clat on %s", uplink_interface);
+ logmsg(ANDROID_LOG_INFO, "Starting clat version %s on %s", CLATD_VERSION, uplink_interface);
// open the tunnel device before dropping privs
tunnel.fd6 = tun_open();
if(tunnel.fd6 < 0) {
- logmsg(ANDROID_LOG_FATAL,"tun_open failed: %s",strerror(errno));
+ logmsg(ANDROID_LOG_FATAL, "tun_open failed: %s", strerror(errno));
exit(1);
}
tunnel.fd4 = tun_open();
if(tunnel.fd4 < 0) {
- logmsg(ANDROID_LOG_FATAL,"tun_open4 failed: %s",strerror(errno));
+ logmsg(ANDROID_LOG_FATAL, "tun_open4 failed: %s", strerror(errno));
exit(1);
}
// open the forwarding configuration before dropping privs
forwarding_fd = open("/proc/sys/net/ipv6/conf/all/forwarding", O_RDWR);
if(forwarding_fd < 0) {
- logmsg(ANDROID_LOG_FATAL,"open /proc/sys/net/ipv6/conf/all/forwarding failed: %s",strerror(errno));
+ logmsg(ANDROID_LOG_FATAL,"open /proc/sys/net/ipv6/conf/all/forwarding failed: %s",
+ strerror(errno));
exit(1);
}
diff --git a/clatd.h b/clatd.h
index 00a4094..3459b09 100644
--- a/clatd.h
+++ b/clatd.h
@@ -22,6 +22,7 @@
#define MAXMTU 1500
#define PACKETLEN (MAXMTU+sizeof(struct tun_pi))
+#define CLATD_VERSION "1.1"
// how frequently (in seconds) to poll for an address change while traffic is passing
#define INTERFACE_POLL_FREQUENCY 30
diff --git a/dump.c b/dump.c
index b2118a4..8567655 100644
--- a/dump.c
+++ b/dump.c
@@ -17,6 +17,7 @@
*/
#include <stdio.h>
#include <stdint.h>
+#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
diff --git a/ipv4.c b/ipv4.c
index 243f9d0..1d34e1e 100644
--- a/ipv4.c
+++ b/ipv4.c
@@ -31,127 +31,73 @@
#include "ipv4.h"
#include "logging.h"
#include "debug.h"
+#include "dump.h"
/* function: icmp_packet
- * takes an icmp packet and sets it up for translation
- * fd - tun interface fd
- * packet - ip payload
- * len - size of ip payload
- * ip - ip header
+ * translates an icmp packet
+ * out - output packet
+ * icmp - pointer to icmp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void icmp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) {
- struct icmphdr icmp;
+int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum,
+ size_t len) {
const char *payload;
size_t payload_size;
- if(len < sizeof(icmp)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"icmp_packet/(too small)");
- return;
+ if(len < sizeof(struct icmphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)");
+ return 0;
}
- memcpy(&icmp, packet, sizeof(icmp));
- payload = packet + sizeof(icmp);
- payload_size = len - sizeof(icmp);
+ payload = (const char *) (icmp + 1);
+ payload_size = len - sizeof(struct icmphdr);
- icmp_to_icmp6(fd,ip,&icmp,payload,payload_size);
+ return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size);
}
-/* function: tcp_packet
- * takes a tcp packet and sets it up for translation
- * fd - tun interface fd
- * packet - ip payload
- * len - size of ip payload
- * ip - ip header
- */
-void tcp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) {
- const struct tcphdr *tcp = (const struct tcphdr *) packet;
- const char *payload;
- size_t payload_size, header_size;
-
- if(len < sizeof(tcp)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)");
- return;
- }
-
- if(tcp->doff < 5) {
- logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff);
- return;
- }
-
- if((size_t) tcp->doff*4 > len) {
- logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff);
- return;
- }
-
- header_size = tcp->doff * 4;
- payload = packet + header_size;
- payload_size = len - header_size;
-
- tcp_to_tcp6(fd, ip, tcp, header_size, payload, payload_size);
-}
-
-/* function: udp_packet
- * takes a udp packet and sets it up for translation
- * fd - tun interface fd
- * packet - ip payload
- * len - size of ip payload
- * ip - ip header
- */
-void udp_packet(int fd, const char *packet, size_t len, const struct iphdr *ip) {
- struct udphdr udp;
- const char *payload;
- size_t payload_size;
-
- if(len < sizeof(udp)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
- return;
- }
-
- memcpy(&udp, packet, sizeof(udp));
- payload = packet + sizeof(udp);
- payload_size = len - sizeof(udp);
-
- udp_to_udp6(fd,ip,&udp,payload,payload_size);
-}
-
-/* function: ip_packet
- * takes an ip packet and hands it off to the layer 4 protocol function
- * fd - tun interface fd
+/* function: ipv4_packet
+ * translates an ipv4 packet
+ * out - output packet
* packet - packet data
* len - size of packet
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void ip_packet(int fd, const char *packet, size_t len) {
- struct iphdr header;
+int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) {
+ const struct iphdr *header = (struct iphdr *) packet;
+ struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base;
uint16_t frag_flags;
+ uint8_t nxthdr;
const char *next_header;
size_t len_left;
+ uint32_t checksum;
+ int iov_len;
- if(len < sizeof(header)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/too short for an ip header");
- return;
+ if(len < sizeof(struct iphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header");
+ return 0;
}
- memcpy(&header, packet, sizeof(header));
-
- frag_flags = ntohs(header.frag_off);
+ frag_flags = ntohs(header->frag_off);
if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't
- logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/more fragments set, dropping");
- return;
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping");
+ return 0;
}
- if(header.ihl < 5) {
- logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header length set to less than 5: %x",header.ihl);
- return;
+ if(header->ihl < 5) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl);
+ return 0;
}
- if((size_t)header.ihl*4 > len) { // ip header length larger than entire packet
- logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header length set too large: %x",header.ihl);
- return;
+ if((size_t) header->ihl * 4 > len) { // ip header length larger than entire packet
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set too large: %x", header->ihl);
+ return 0;
}
- if(header.version != 4) {
- logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header version not 4: %x",header.version);
- return;
+ if(header->version != 4) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version);
+ return 0;
}
/* rfc6145 - If any IPv4 options are present in the IPv4 packet, they MUST be
@@ -159,19 +105,40 @@ void ip_packet(int fd, const char *packet, size_t len) {
* translate the options.
*/
- next_header = packet + header.ihl*4;
- len_left = len - header.ihl*4;
+ next_header = packet + header->ihl*4;
+ len_left = len - header->ihl * 4;
+
+ nxthdr = header->protocol;
+ if (nxthdr == IPPROTO_ICMP) {
+ // ICMP and ICMPv6 have different protocol numbers.
+ nxthdr = IPPROTO_ICMPV6;
+ }
- if(header.protocol == IPPROTO_ICMP) {
- icmp_packet(fd,next_header,len_left,&header);
- } else if(header.protocol == IPPROTO_TCP) {
- tcp_packet(fd,next_header,len_left,&header);
- } else if(header.protocol == IPPROTO_UDP) {
- udp_packet(fd,next_header,len_left,&header);
+ /* Fill in the IPv6 header. We need to do this before we translate the packet because TCP and
+ * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
+ * know it yet.
+ */
+ fill_ip6_header(ip6_targ, 0, nxthdr, header);
+ out[pos].iov_len = sizeof(struct ip6_hdr);
+
+ // Calculate the pseudo-header checksum.
+ checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left);
+
+ if(nxthdr == IPPROTO_ICMPV6) {
+ iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left);
+ } else if(nxthdr == IPPROTO_TCP) {
+ iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left);
+ } else if(nxthdr == IPPROTO_UDP) {
+ iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left);
} else {
#if CLAT_DEBUG
- logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/unknown protocol: %x",header.protocol);
+ logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol);
logcat_hexdump("ipv4/protocol", packet, len);
#endif
+ return 0;
}
+
+ // Set the length.
+ ip6_targ->ip6_plen = htons(payload_length(out, pos));
+ return iov_len;
}
diff --git a/ipv4.h b/ipv4.h
index eef3f0c..00befd2 100644
--- a/ipv4.h
+++ b/ipv4.h
@@ -18,6 +18,8 @@
#ifndef __IPV4_H__
#define __IPV4_H__
-void ip_packet(int fd, const char *packet, size_t len);
+#include "translate.h"
+
+int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len);
#endif /* __IPV4_H__ */
diff --git a/ipv6.c b/ipv6.c
index 61013f2..8011ce9 100644
--- a/ipv6.c
+++ b/ipv6.c
@@ -25,6 +25,7 @@
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <linux/icmp.h>
+#include <arpa/inet.h>
#include "translate.h"
#include "checksum.h"
@@ -36,84 +37,26 @@
/* function: icmp6_packet
* takes an icmp6 packet and sets it up for translation
- * fd - tun interface fd
- * packet - ip payload
- * len - size of ip payload
- * ip6 - ip6 header
+ * out - output packet
+ * icmp6 - pointer to icmp6 header in packet
+ * checksum - pseudo-header checksum (unused)
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void icmp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) {
- struct icmp6_hdr icmp6;
+int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+ size_t len) {
const char *payload;
size_t payload_size;
- if(len < sizeof(icmp6)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"icmp6_packet/(too small)");
- return;
+ if(len < sizeof(struct icmp6_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)");
+ return 0;
}
- memcpy(&icmp6, packet, sizeof(icmp6));
- payload = packet + sizeof(icmp6);
- payload_size = len - sizeof(icmp6);
+ payload = (const char *) (icmp6 + 1);
+ payload_size = len - sizeof(struct icmp6_hdr);
- icmp6_to_icmp(fd, ip6, &icmp6, payload, payload_size);
-}
-
-/* function: tcp6_packet
- * takes a tcp packet and sets it up for translation
- * fd - tun interface fd
- * packet - ip payload
- * len - size of ip payload
- * ip6 - ip6 header
- */
-void tcp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) {
- const struct tcphdr *tcp = (const struct tcphdr *) packet;
- const char *payload;
- size_t payload_size, header_size;
-
- if(len < sizeof(tcp)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/(too small)");
- return;
- }
-
- if(tcp->doff < 5) {
- logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set to less than 5: %x", tcp->doff);
- return;
- }
-
- if((size_t) tcp->doff*4 > len) {
- logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/tcp header length set too large: %x", tcp->doff);
- return;
- }
-
- header_size = tcp->doff * 4;
- payload = packet + header_size;
- payload_size = len - header_size;
-
- tcp6_to_tcp(fd, ip6, tcp, header_size, payload, payload_size);
-}
-
-/* function: udp6_packet
- * takes a udp packet and sets it up for translation
- * fd - tun interface fd
- * packet - ip payload
- * len - size of ip payload
- * ip6 - ip6 header
- */
-void udp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) {
- struct udphdr udp;
- const char *payload;
- size_t payload_size;
-
- if(len < sizeof(udp)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"udp6_packet/(too small)");
- return;
- }
-
- memcpy(&udp, packet, sizeof(udp));
- payload = packet + sizeof(udp);
- payload_size = len - sizeof(udp);
-
- udp6_to_udp(fd,ip6,&udp,payload,payload_size);
+ return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size);
}
/* function: log_bad_address
@@ -132,53 +75,81 @@ void log_bad_address(const char *fmt, const struct in6_addr *badaddr) {
/* function: ipv6_packet
* takes an ipv6 packet and hands it off to the layer 4 protocol function
- * fd - tun interface fd
+ * out - output packet
* packet - packet data
* len - size of packet
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void ipv6_packet(int fd, const char *packet, size_t len) {
- struct ip6_hdr header;
+int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) {
+ const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet;
+ struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base;
+ uint8_t protocol;
const char *next_header;
size_t len_left;
+ uint32_t checksum;
+ int iov_len;
int i;
- if(len < sizeof(header)) {
- logmsg_dbg(ANDROID_LOG_ERROR,"ipv6_packet/too short for an ip6 header");
- return;
+ if(len < sizeof(struct ip6_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header");
+ return 0;
}
- memcpy(&header, packet, sizeof(header));
-
- next_header = packet + sizeof(header);
- len_left = len - sizeof(header);
-
- if(IN6_IS_ADDR_MULTICAST(&header.ip6_dst)) {
- log_bad_address("ipv6_packet/multicast %s", &header.ip6_dst);
- return; // silently ignore
+ if(IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ log_bad_address("ipv6_packet/multicast %s", &ip6->ip6_dst);
+ return 0; // silently ignore
}
for(i = 0; i < 3; i++) {
- if(header.ip6_src.s6_addr32[i] != Global_Clatd_Config.plat_subnet.s6_addr32[i]) {
- log_bad_address("ipv6_packet/wrong source address: %s", &header.ip6_src);
- return;
+ if(ip6->ip6_src.s6_addr32[i] != Global_Clatd_Config.plat_subnet.s6_addr32[i]) {
+ log_bad_address("ipv6_packet/wrong source address: %s", &ip6->ip6_src);
+ return 0;
}
}
- if(!IN6_ARE_ADDR_EQUAL(&header.ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) {
- log_bad_address("ipv6_packet/wrong destination address: %s", &header.ip6_dst);
- return;
+ if(!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) {
+ log_bad_address("ipv6_packet/wrong destination address: %s", &ip6->ip6_dst);
+ return 0;
+ }
+
+ next_header = packet + sizeof(struct ip6_hdr);
+ len_left = len - sizeof(struct ip6_hdr);
+
+ protocol = ip6->ip6_nxt;
+ if (protocol == IPPROTO_ICMPV6) {
+ // ICMP and ICMPv6 have different protocol numbers.
+ protocol = IPPROTO_ICMP;
}
+ /* Fill in the IPv4 header. We need to do this before we translate the packet because TCP and
+ * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
+ * know it yet.
+ */
+ fill_ip_header(ip_targ, 0, protocol, ip6);
+ out[pos].iov_len = sizeof(struct iphdr);
+
+ // Calculate the pseudo-header checksum.
+ checksum = ipv4_pseudo_header_checksum(0, ip_targ, len_left);
+
// does not support IPv6 extension headers, this will drop any packet with them
- if(header.ip6_nxt == IPPROTO_ICMPV6) {
- icmp6_packet(fd,next_header,len_left,&header);
- } else if(header.ip6_nxt == IPPROTO_TCP) {
- tcp6_packet(fd,next_header,len_left,&header);
- } else if(header.ip6_nxt == IPPROTO_UDP) {
- udp6_packet(fd,next_header,len_left,&header);
+ if(protocol == IPPROTO_ICMP) {
+ iov_len = icmp6_packet(out, pos + 1, (const struct icmp6_hdr *) next_header, checksum,
+ len_left);
+ } else if(ip6->ip6_nxt == IPPROTO_TCP) {
+ iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum,
+ len_left);
+ } else if(ip6->ip6_nxt == IPPROTO_UDP) {
+ iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum,
+ len_left);
} else {
#if CLAT_DEBUG
- logmsg(ANDROID_LOG_ERROR,"ipv6_packet/unknown next header type: %x",header.ip6_nxt);
+ logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x",ip6->ip6_nxt);
logcat_hexdump("ipv6/nxthdr", packet, len);
#endif
+ return 0;
}
+
+ // Set the length and calculate the checksum.
+ ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + payload_length(out, pos));
+ ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr));
+ return iov_len;
}
diff --git a/ipv6.h b/ipv6.h
index b898ad9..019de17 100644
--- a/ipv6.h
+++ b/ipv6.h
@@ -18,6 +18,8 @@
#ifndef __IPV6_H__
#define __IPV6_H__
-void ipv6_packet(int fd, const char *packet, size_t len);
+#include "translate.h"
+
+int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len);
#endif /* __IPV6_H__ */
diff --git a/translate.c b/translate.c
index 936c781..c0bd59a 100644
--- a/translate.c
+++ b/translate.c
@@ -16,7 +16,6 @@
* translate.c - CLAT functions / partial implementation of rfc6145
*/
#include <string.h>
-#include <sys/uio.h>
#include <netinet/in.h>
#include <netinet/ip.h>
@@ -27,12 +26,28 @@
#include <netinet/icmp6.h>
#include <linux/icmp.h>
+#include "translate.h"
#include "checksum.h"
#include "clatd.h"
#include "config.h"
#include "logging.h"
#include "debug.h"
+/* function: payload_length
+ * calculates the total length of the packet components after pos
+ * packet - packet to calculate the length of
+ * pos - position to start counting from
+ * returns: the total length of the packet components after pos
+ */
+uint16_t payload_length(clat_packet packet, int pos) {
+ size_t len = 0;
+ int i;
+ for (i = pos + 1; i < POS_MAX; i++) {
+ len += packet[i].iov_len;
+ }
+ return len;
+}
+
/* function: fill_tun_header
* fill in the header for the tun fd
* tun_header - tunnel header, already allocated
@@ -46,6 +61,7 @@ void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) {
/* function: ipv6_src_to_ipv4_src
* return the corresponding ipv4 address for the given ipv6 address
* sourceaddr - ipv6 source address
+ * returns: the IPv4 address
*/
uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) {
// assumes a /96 plat subnet
@@ -53,29 +69,28 @@ uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) {
}
/* function: fill_ip_header
- * generating an ipv4 header from an ipv6 header (called by the layer 4 protocol-specific functions)
- * ip_targ - (ipv4) target packet header, source addr: original ipv4 addr, dest addr: local subnet addr
+ * generate an ipv4 header from an ipv6 header
+ * ip_targ - (ipv4) target packet header, source: original ipv4 addr, dest: local subnet addr
* payload_len - length of other data inside packet
* protocol - protocol number (tcp, udp, etc)
- * old_header - (ipv6) source packet header, source addr: nat64 prefix, dest addr: local subnet prefix
+ * old_header - (ipv6) source packet header, source: nat64 prefix, dest: local subnet prefix
*/
-void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol, const struct ip6_hdr *old_header) {
- memset(ip_targ, 0, sizeof(ip_targ));
-
- ip_targ->ihl = 5;
- ip_targ->version = 4;
- ip_targ->tos = 0;
- ip_targ->tot_len = htons(sizeof(struct iphdr) + payload_len);
- ip_targ->id = 0;
- ip_targ->frag_off = htons(IP_DF);
- ip_targ->ttl = old_header->ip6_hlim;
- ip_targ->protocol = protocol;
- ip_targ->check = 0;
-
- ip_targ->saddr = ipv6_src_to_ipv4_src(&old_header->ip6_src);
- ip_targ->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr;
-
- ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr));
+void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
+ const struct ip6_hdr *old_header) {
+ memset(ip, 0, sizeof(struct iphdr));
+
+ ip->ihl = 5;
+ ip->version = 4;
+ ip->tos = 0;
+ ip->tot_len = htons(sizeof(struct iphdr) + payload_len);
+ ip->id = 0;
+ ip->frag_off = htons(IP_DF);
+ ip->ttl = old_header->ip6_hlim;
+ ip->protocol = protocol;
+ ip->check = 0;
+
+ ip->saddr = ipv6_src_to_ipv4_src(&old_header->ip6_src);
+ ip->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr;
}
/* function: ipv4_dst_to_ipv6_dst
@@ -93,13 +108,14 @@ struct in6_addr ipv4_dst_to_ipv6_dst(uint32_t destination) {
}
/* function: fill_ip6_header
- * generating an ipv6 header from an ipv4 header (called by the layer 4 protocol-specific functions)
- * ip6 - (ipv6) target packet header, source addr: local subnet prefix, dest addr: nat64 prefix
+ * generate an ipv6 header from an ipv4 header
+ * ip6 - (ipv6) target packet header, source: local subnet prefix, dest: nat64 prefix
* payload_len - length of other data inside packet
* protocol - protocol number (tcp, udp, etc)
- * old_header - (ipv4) source packet header, source addr: local subnet addr, dest addr: internet's ipv4 addr
+ * old_header - (ipv4) source packet header, source: local subnet addr, dest: internet's ipv4 addr
*/
-void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header) {
+void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
+ const struct iphdr *old_header) {
memset(ip6, 0, sizeof(struct ip6_hdr));
ip6->ip6_vfc = 6 << 4;
@@ -113,289 +129,196 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol
/* function: icmp_to_icmp6
* translate ipv4 icmp to ipv6 icmp (only currently supports echo/echo reply)
- * fd - tun interface fd
- * ip - source packet ipv4 header
+ * out - output packet
* icmp - source packet icmp header
+ * checksum - pseudo-header checksum
* payload - icmp payload
* payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size) {
- struct ip6_hdr ip6_targ;
- struct icmp6_hdr icmp6_targ;
- struct iovec io_targ[4];
- struct tun_pi tun_header;
+int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum,
+ const char *payload, size_t payload_size) {
+ struct icmp6_hdr *icmp6_targ = out[pos].iov_base;
uint32_t checksum_temp;
if((icmp->type != ICMP_ECHO) && (icmp->type != ICMP_ECHOREPLY)) {
- logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x",icmp->type);
- return;
+ logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x", icmp->type);
+ return 0;
}
- fill_tun_header(&tun_header,ETH_P_IPV6);
+ memset(icmp6_targ, 0, sizeof(struct icmp6_hdr));
+ icmp6_targ->icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY;
+ icmp6_targ->icmp6_code = 0;
+ icmp6_targ->icmp6_id = icmp->un.echo.id;
+ icmp6_targ->icmp6_seq = icmp->un.echo.sequence;
- fill_ip6_header(&ip6_targ,payload_size + sizeof(icmp6_targ),IPPROTO_ICMPV6,ip);
-
- memset(&icmp6_targ, 0, sizeof(icmp6_targ));
- icmp6_targ.icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY;
- icmp6_targ.icmp6_code = 0;
- icmp6_targ.icmp6_cksum = 0;
- icmp6_targ.icmp6_id = icmp->un.echo.id;
- icmp6_targ.icmp6_seq = icmp->un.echo.sequence;
-
- checksum_temp = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(icmp6_targ) + payload_size);
- checksum_temp = ip_checksum_add(checksum_temp, &icmp6_targ, sizeof(icmp6_targ));
- checksum_temp = ip_checksum_add(checksum_temp, payload, payload_size);
- icmp6_targ.icmp6_cksum = ip_checksum_finish(checksum_temp);
+ icmp6_targ->icmp6_cksum = 0;
+ checksum = ip_checksum_add(checksum, icmp6_targ, sizeof(struct icmp6_hdr));
+ checksum = ip_checksum_add(checksum, payload, payload_size);
+ icmp6_targ->icmp6_cksum = ip_checksum_finish(checksum);
- io_targ[0].iov_base = &tun_header;
- io_targ[0].iov_len = sizeof(tun_header);
- io_targ[1].iov_base = &ip6_targ;
- io_targ[1].iov_len = sizeof(ip6_targ);
- io_targ[2].iov_base = &icmp6_targ;
- io_targ[2].iov_len = sizeof(icmp6_targ);
- io_targ[3].iov_base = (char *)payload;
- io_targ[3].iov_len = payload_size;
+ out[pos].iov_len = sizeof(struct icmp6_hdr);
+ out[POS_PAYLOAD].iov_base = (char *) payload;
+ out[POS_PAYLOAD].iov_len = payload_size;
- writev(fd, io_targ, 4);
+ return POS_PAYLOAD + 1;
}
/* function: icmp6_to_icmp
* translate ipv6 icmp to ipv4 icmp (only currently supports echo/echo reply)
- * fd - tun interface fd
- * ip6 - source packet ipv6 header
+ * out - output packet
* icmp6 - source packet icmp6 header
+ * checksum - pseudo-header checksum (unused)
* payload - icmp6 payload
* payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) {
- struct iphdr ip_targ;
- struct icmphdr icmp_targ;
- struct iovec io_targ[4];
- struct tun_pi tun_header;
- uint32_t temp_icmp_checksum;
+int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+ const char *payload, size_t payload_size) {
+ struct icmphdr *icmp_targ = out[pos].iov_base;
if((icmp6->icmp6_type != ICMP6_ECHO_REQUEST) && (icmp6->icmp6_type != ICMP6_ECHO_REPLY)) {
logmsg_dbg(ANDROID_LOG_WARN,"icmp6_to_icmp/unhandled icmp6 type: 0x%x",icmp6->icmp6_type);
- return;
+ return 0;
}
- memset(&icmp_targ, 0, sizeof(icmp_targ));
+ memset(icmp_targ, 0, sizeof(struct icmphdr));
- fill_tun_header(&tun_header,ETH_P_IP);
- fill_ip_header(&ip_targ,sizeof(icmp_targ) + payload_size, IPPROTO_ICMP, ip6);
+ icmp_targ->type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY;
+ icmp_targ->code = 0x0;
+ icmp_targ->un.echo.id = icmp6->icmp6_id;
+ icmp_targ->un.echo.sequence = icmp6->icmp6_seq;
- icmp_targ.type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY;
- icmp_targ.code = 0x0;
- icmp_targ.checksum = 0;
- icmp_targ.un.echo.id = icmp6->icmp6_id;
- icmp_targ.un.echo.sequence = icmp6->icmp6_seq;
+ icmp_targ->checksum = 0;
+ checksum = ip_checksum_add(0, icmp_targ, sizeof(struct icmphdr));
+ checksum = ip_checksum_add(checksum, (void *)payload, payload_size);
+ icmp_targ->checksum = ip_checksum_finish(checksum);
- temp_icmp_checksum = ip_checksum_add(0, &icmp_targ, sizeof(icmp_targ));
- temp_icmp_checksum = ip_checksum_add(temp_icmp_checksum, (void *)payload, payload_size);
- icmp_targ.checksum = ip_checksum_finish(temp_icmp_checksum);
+ out[pos].iov_len = sizeof(struct icmphdr);
+ out[POS_PAYLOAD].iov_base = (char *) payload;
+ out[POS_PAYLOAD].iov_len = payload_size;
- io_targ[0].iov_base = &tun_header;
- io_targ[0].iov_len = sizeof(tun_header);
- io_targ[1].iov_base = &ip_targ;
- io_targ[1].iov_len = sizeof(ip_targ);
- io_targ[2].iov_base = &icmp_targ;
- io_targ[2].iov_len = sizeof(icmp_targ);
- io_targ[3].iov_base = (char *)payload;
- io_targ[3].iov_len = payload_size;
-
- writev(fd, io_targ, 4);
+ return POS_PAYLOAD + 1;
}
-/* function: udp_translate
- * common between ipv4/ipv6 - setup checksum and send udp packet
- * fd - tun interface fd
- * udp - source packet udp header
- * payload - udp payload
- * payload_size - size of payload
- * io_targ - iovec with tun and ipv4/ipv6 header (see below)
- * array position 0 - tun header
- * array position 1 - ipv4/ipv6 header
- * array position 2 - empty (will be udp header)
- * array position 3 - empty (will be payload)
- * checksum - partial checksum covering ipv4/ipv6 header
+/* function: udp_packet
+ * takes a udp packet and sets it up for translation
+ * out - output packet
+ * udp - pointer to udp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
*/
-void udp_translate(int fd, const struct udphdr *udp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum) {
- struct udphdr udp_targ;
-
- memcpy(&udp_targ, udp, sizeof(udp_targ));
- udp_targ.check = 0; // reset checksum, to be calculated
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) {
+ const char *payload;
+ size_t payload_size;
- checksum = ip_checksum_add(checksum, &udp_targ, sizeof(struct udphdr));
- checksum = ip_checksum_add(checksum, payload, payload_size);
- udp_targ.check = ip_checksum_finish(checksum);
+ if(len < sizeof(struct udphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
+ return 0;
+ }
- io_targ[2].iov_base = &udp_targ;
- io_targ[2].iov_len = sizeof(udp_targ);
- io_targ[3].iov_base = (char *)payload;
- io_targ[3].iov_len = payload_size;
+ payload = (const char *) (udp + 1);
+ payload_size = len - sizeof(struct udphdr);
- writev(fd, io_targ, 4);
+ return udp_translate(out, pos, udp, checksum, payload, payload_size);
}
-/* function: udp_to_udp6
- * translate ipv4 udp to ipv6 udp
- * fd - tun interface fd
- * ip - source packet ipv4 header
- * udp - source packet udp header
- * payload - udp payload
- * payload_size - size of payload
+/* function: tcp_packet
+ * takes a tcp packet and sets it up for translation
+ * out - output packet
+ * tcp - pointer to tcp header in packet
+ * checksum - pseudo-header checksum
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size) {
- struct ip6_hdr ip6_targ;
- struct iovec io_targ[4];
- struct tun_pi tun_header;
- uint32_t checksum;
+int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) {
+ const char *payload;
+ size_t payload_size, header_size;
- fill_tun_header(&tun_header,ETH_P_IPV6);
+ if(len < sizeof(struct tcphdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)");
+ return 0;
+ }
- fill_ip6_header(&ip6_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip);
+ if(tcp->doff < 5) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff);
+ return 0;
+ }
- checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, sizeof(*udp) + payload_size);
+ if((size_t) tcp->doff*4 > len) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff);
+ return 0;
+ }
- io_targ[0].iov_base = &tun_header;
- io_targ[0].iov_len = sizeof(tun_header);
- io_targ[1].iov_base = &ip6_targ;
- io_targ[1].iov_len = sizeof(ip6_targ);
+ header_size = tcp->doff * 4;
+ payload = ((const char *) tcp) + header_size;
+ payload_size = len - header_size;
- udp_translate(fd,udp,payload,payload_size,io_targ,checksum);
+ return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size);
}
-/* function: udp6_to_udp
- * translate ipv6 udp to ipv4 udp
- * fd - tun interface fd
- * ip6 - source packet ipv6 header
- * udp - source packet udp header
- * payload - udp payload
+/* function: udp_translate
+ * common between ipv4/ipv6 - setup checksum and send udp packet
+ * out - output packet
+ * udp - udp header
+ * checksum - pseudo-header checksum
+ * payload - tcp payload
* payload_size - size of payload
+ * returns: the highest position in the output clat_packet that's filled in
*/
-void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size) {
- struct iphdr ip_targ;
- struct iovec io_targ[4];
- struct tun_pi tun_header;
- uint32_t checksum;
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
+ const char *payload, size_t payload_size) {
+ struct udphdr *udp_targ = out[pos].iov_base;
- fill_tun_header(&tun_header,ETH_P_IP);
+ memcpy(udp_targ, udp, sizeof(struct udphdr));
+ udp_targ->check = 0; // reset checksum, to be calculated
- fill_ip_header(&ip_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip6);
-
- checksum = ipv4_pseudo_header_checksum(0, &ip_targ, sizeof(*udp) + payload_size);
+ checksum = ip_checksum_add(checksum, udp_targ, sizeof(struct udphdr));
+ checksum = ip_checksum_add(checksum, payload, payload_size);
+ udp_targ->check = ip_checksum_finish(checksum);
- io_targ[0].iov_base = &tun_header;
- io_targ[0].iov_len = sizeof(tun_header);
- io_targ[1].iov_base = &ip_targ;
- io_targ[1].iov_len = sizeof(ip_targ);
+ out[pos].iov_len = sizeof(struct udphdr);
+ out[POS_PAYLOAD].iov_base = (char *) payload;
+ out[POS_PAYLOAD].iov_len = payload_size;
- udp_translate(fd,udp,payload,payload_size,io_targ,checksum);
+ return POS_PAYLOAD + 1;
}
/* function: tcp_translate
* common between ipv4/ipv6 - setup checksum and send tcp packet
- * fd - tun interface fd
- * tcp - source packet tcp header
+ * out - output packet
+ * tcp - tcp header
+ * header_size - size of tcp header including options
+ * checksum - partial checksum covering ipv4/ipv6 header
* payload - tcp payload
* payload_size - size of payload
- * io_targ - iovec with tun and ipv4/ipv6 header (see below)
- * array position 0 - tun header
- * array position 1 - ipv4/ipv6 header
- * array position 2 - empty (will be tcp header)
- * array position 3 - empty (will be payload)
- * checksum - partial checksum covering ipv4/ipv6 header
+ * returns: the highest position in the output clat_packet that's filled in
*
* TODO: mss rewrite
* TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented)
*/
-void tcp_translate(int fd, const struct tcphdr *tcp, size_t header_size, const char *payload,
- size_t payload_size, struct iovec *io_targ, uint32_t checksum) {
- union {
- // Reserve space for the maximum size of the TCP header, including options. The TCP header
- // length field is 4 bits long and counts 4-byte words, so it can be at most 60 bytes.
- char buf[15 * 4];
- struct tcphdr tcp;
- } header;
-
- if (header_size > sizeof(header.buf)) {
- // A TCP header cannot be more than 60 bytes long, so this can never happen unless there is a
- // bug in the caller.
+int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
+ uint32_t checksum, const char *payload, size_t payload_size) {
+ struct tcphdr *tcp_targ = out[pos].iov_base;
+ out[pos].iov_len = header_size;
+
+ if (header_size > MAX_TCP_HDR) {
+ // A TCP header cannot be more than MAX_TCP_HDR bytes long because it's a 4-bit field that
+ // counts in 4-byte words. So this can never happen unless there is a bug in the caller.
logmsg(ANDROID_LOG_ERROR, "tcp_translate: header too long %d > %d, truncating",
- header_size, sizeof(header.buf));
- header_size = sizeof(header.buf);
+ header_size, MAX_TCP_HDR);
+ header_size = MAX_TCP_HDR;
}
- memcpy(&header, tcp, header_size);
+ memcpy(tcp_targ, tcp, header_size);
- header.tcp.check = 0;
- checksum = ip_checksum_add(checksum, &header, header_size);
+ tcp_targ->check = 0;
+ checksum = ip_checksum_add(checksum, tcp_targ, header_size);
checksum = ip_checksum_add(checksum, payload, payload_size);
- header.tcp.check = ip_checksum_finish(checksum);
-
- io_targ[2].iov_base = &header;
- io_targ[2].iov_len = header_size;
-
- io_targ[3].iov_base = (char *)payload;
- io_targ[3].iov_len = payload_size;
-}
-
-/* function: tcp_to_tcp6
- * translate ipv4 tcp to ipv6 tcp
- * fd - tun interface fd
- * ip - source packet ipv4 header
- * tcp - source packet tcp header
- * header_size - size of tcp header including options
- * payload - tcp payload
- * payload_size - size of payload
- */
-void tcp_to_tcp6(int fd, const struct iphdr *ip, const struct tcphdr *tcp, size_t header_size,
- const char *payload, size_t payload_size) {
- struct ip6_hdr ip6_targ;
- struct iovec io_targ[5];
- struct tun_pi tun_header;
- uint32_t checksum;
-
- fill_tun_header(&tun_header,ETH_P_IPV6);
-
- fill_ip6_header(&ip6_targ, header_size + payload_size, IPPROTO_TCP, ip);
-
- checksum = ipv6_pseudo_header_checksum(0, &ip6_targ, header_size + payload_size);
-
- io_targ[0].iov_base = &tun_header;
- io_targ[0].iov_len = sizeof(tun_header);
- io_targ[1].iov_base = &ip6_targ;
- io_targ[1].iov_len = sizeof(ip6_targ);
-
- tcp_translate(fd, tcp, header_size, payload, payload_size, io_targ, checksum);
-}
-
-/* function: tcp6_to_tcp
- * translate ipv6 tcp to ipv4 tcp
- * fd - tun interface fd
- * ip6 - source packet ipv6 header
- * tcp - source packet tcp header
- * header_size - size of tcp header including options
- * payload - tcp payload
- * payload_size - size of payload
- */
-void tcp6_to_tcp(int fd, const struct ip6_hdr *ip6, const struct tcphdr *tcp, size_t header_size,
- const char *payload, size_t payload_size) {
- struct iphdr ip_targ;
- struct iovec io_targ[5];
- struct tun_pi tun_header;
- uint32_t checksum;
-
- fill_tun_header(&tun_header,ETH_P_IP);
-
- fill_ip_header(&ip_targ, header_size + payload_size, IPPROTO_TCP, ip6);
-
- checksum = ipv4_pseudo_header_checksum(0, &ip_targ, header_size + payload_size);
+ tcp_targ->check = ip_checksum_finish(checksum);
- io_targ[0].iov_base = &tun_header;
- io_targ[0].iov_len = sizeof(tun_header);
- io_targ[1].iov_base = &ip_targ;
- io_targ[1].iov_len = sizeof(ip_targ);
+ out[POS_PAYLOAD].iov_base = (char *)payload;
+ out[POS_PAYLOAD].iov_len = payload_size;
- tcp_translate(fd, tcp, header_size, payload, payload_size, io_targ, checksum);
+ return POS_PAYLOAD + 1;
}
diff --git a/translate.h b/translate.h
index 641768e..07db023 100644
--- a/translate.h
+++ b/translate.h
@@ -18,15 +18,40 @@
#ifndef __TRANSLATE_H__
#define __TRANSLATE_H__
-void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size);
-void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size);
+#include <linux/if_tun.h>
-void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size);
-void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size);
+#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words.
-void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, size_t header_size,
- const char *payload, size_t payload_size);
-void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, size_t header_size,
- const char *payload, size_t payload_size);
+// A clat_packet is an array of iovec structures representing a packet that we are translating.
+// The POS_XXX constants represent the array indices within the clat_packet that contain specific
+// parts of the packet.
+enum clat_packet_index { POS_TUNHDR, POS_IPHDR, POS_TRANSPORTHDR, POS_ICMPIPHDR,
+ POS_PAYLOAD, POS_MAX };
+typedef struct iovec clat_packet[POS_MAX];
+
+// Returns the total length of the packet components after index.
+uint16_t payload_length(clat_packet packet, int index);
+
+// Functions to create tun, IPv4, and IPv6 headers.
+void fill_tun_header(struct tun_pi *tun_header, uint16_t proto);
+void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol,
+ const struct ip6_hdr *old_header);
+void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
+ const struct iphdr *old_header);
+
+// Translate ICMP packets.
+int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum,
+ const char *payload, size_t payload_size);
+int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+ const char *payload, size_t payload_size);
+
+// Translate TCP and UDP packets.
+int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len);
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len);
+
+int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
+ uint32_t checksum, const char *payload, size_t payload_size);
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
+ const char *payload, size_t payload_size);
#endif /* __TRANSLATE_H__ */