summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2013-03-22 00:42:21 +0900
committerLorenzo Colitti <lorenzo@google.com>2013-04-12 12:35:41 +0900
commitd90841824dc00f65a48a789396c7f428807432ca (patch)
treee91f44a1a698088f69523df7869ba08cedb10941
parent0aff5c273daa16e5af234a904ba4a9cf6dc414a6 (diff)
downloadandroid_external_android-clat-d90841824dc00f65a48a789396c7f428807432ca.tar.gz
android_external_android-clat-d90841824dc00f65a48a789396c7f428807432ca.tar.bz2
android_external_android-clat-d90841824dc00f65a48a789396c7f428807432ca.zip
Pass around packet data instead of fds
The current code calls all the translation functions one after another, accumulating the translated packet into local variables on the stack and calling writev() at the end. This does not allow calling the translation functions re-entrantly, which is needed, for example, to translate ICMP errors (which contain the packet that caused them). Define a clat_packet type to wrap the array of iovecs and an enum of packet positions. Also clean up the code a bit: get rid of a fair bit of duplicated code (though there is still some left), get rid of some redundant memcpy statements, fix style issues, etc. Bug: 8276725 Change-Id: Ib58d2348894e82275234fc67dbdb1f82753f204f
-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__ */