summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRicardo Cerqueira <cyanogenmod@cerqueira.org>2014-06-03 01:25:00 +0100
committerRicardo Cerqueira <cyanogenmod@cerqueira.org>2014-06-03 01:25:00 +0100
commit5a3678586c4c0d88da6cea9262af0a4f46744d23 (patch)
treeef8678294829041cf1ed9c0ac3c52c36e2fa31d7
parent856da217c532b9df1246586151ad8c4a56fc66f6 (diff)
parent2b4cc7393268622ac3de071435a6e4ab857342df (diff)
downloadandroid_external_android-clat-stable/cm-11.0-XNF8Y.tar.gz
android_external_android-clat-stable/cm-11.0-XNF8Y.tar.bz2
android_external_android-clat-stable/cm-11.0-XNF8Y.zip
Android 4.4.3 release 1
-rw-r--r--Android.mk1
-rw-r--r--checksum.c54
-rw-r--r--checksum.h6
-rw-r--r--clatd.c89
-rw-r--r--clatd.conf3
-rw-r--r--clatd.h6
-rw-r--r--config.c6
-rw-r--r--config.h3
-rw-r--r--dump.c9
-rw-r--r--icmp.c2
-rw-r--r--ipv4.c56
-rw-r--r--ipv4.h25
-rw-r--r--ipv6.c77
-rw-r--r--ipv6.h25
-rw-r--r--translate.c186
-rw-r--r--translate.h48
16 files changed, 353 insertions, 243 deletions
diff --git a/Android.mk b/Android.mk
index b0d19d9..ec1eeb7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -3,6 +3,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:=clatd.c dump.c checksum.c translate.c icmp.c ipv4.c ipv6.c config.c dns64.c logging.c getaddr.c getroute.c netlink_callbacks.c netlink_msg.c setif.c setroute.c mtu.c
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
LOCAL_C_INCLUDES := external/libnl-headers
LOCAL_STATIC_LIBRARIES := libnl_2
LOCAL_SHARED_LIBRARIES := libcutils liblog
diff --git a/checksum.c b/checksum.c
index a4dc9b8..3dd1e00 100644
--- a/checksum.c
+++ b/checksum.c
@@ -49,19 +49,27 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len) {
return checksum;
}
-/* function: ip_checksum_finish
- * close the checksum
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
* temp_sum - sum from ip_checksum_add
+ * returns: the folded checksum in network byte order
*/
-uint16_t ip_checksum_finish(uint32_t temp_sum) {
+uint16_t ip_checksum_fold(uint32_t temp_sum) {
while(temp_sum > 0xffff)
temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
- temp_sum = (~temp_sum) & 0xffff;
-
return temp_sum;
}
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ * temp_sum - sum from ip_checksum_add
+ * returns: a header checksum value in network byte order
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+ return ~ip_checksum_fold(temp_sum);
+}
+
/* function: ip_checksum
* combined ip_checksum_add and ip_checksum_finish
* data - data to checksum
@@ -76,16 +84,16 @@ uint16_t ip_checksum(const void *data, int len) {
/* function: ipv6_pseudo_header_checksum
* calculate the pseudo header checksum for use in tcp/udp/icmp headers
- * current - the current checksum or 0 to start a new checksum
- * ip6 - the ipv6 header
- * len - the transport length (transport header + payload)
+ * ip6 - the ipv6 header
+ * len - the transport length (transport header + payload)
+ * protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
*/
-uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len) {
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol) {
uint32_t checksum_len, checksum_next;
-
checksum_len = htonl((uint32_t) len);
- checksum_next = htonl(ip6->ip6_nxt);
+ checksum_next = htonl(protocol);
+ uint32_t current = 0;
current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
@@ -96,16 +104,16 @@ uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6
/* function: ipv4_pseudo_header_checksum
* calculate the pseudo header checksum for use in tcp/udp headers
- * current - the current checksum or 0 to start a new checksum
* ip - the ipv4 header
* len - the transport length (transport header + payload)
*/
-uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len) {
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len) {
uint16_t temp_protocol, temp_length;
temp_protocol = htons(ip->protocol);
temp_length = htons(len);
+ uint32_t current = 0;
current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
@@ -113,3 +121,23 @@ uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, u
return current;
}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ * checksum - the header checksum in the original packet in network byte order
+ * old_hdr_sum - the pseudo-header checksum of the original packet
+ * new_hdr_sum - the pseudo-header checksum of the translated packet
+ * returns: the new header checksum in network byte order
+ */
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
+ // Algorithm suggested in RFC 1624.
+ // http://tools.ietf.org/html/rfc1624#section-3
+ checksum = ~checksum;
+ uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+ uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+ if (folded_sum > folded_old) {
+ return ~(folded_sum - folded_old);
+ } else {
+ return ~(folded_sum - folded_old - 1); // end-around borrow
+ }
+}
diff --git a/checksum.h b/checksum.h
index 473f5f5..6195810 100644
--- a/checksum.h
+++ b/checksum.h
@@ -22,7 +22,9 @@ uint32_t ip_checksum_add(uint32_t current, const void *data, int len);
uint16_t ip_checksum_finish(uint32_t temp_sum);
uint16_t ip_checksum(const void *data, int len);
-uint32_t ipv6_pseudo_header_checksum(uint32_t current, const struct ip6_hdr *ip6, uint16_t len);
-uint32_t ipv4_pseudo_header_checksum(uint32_t current, const struct iphdr *ip, uint16_t len);
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr *ip6, uint16_t len, uint8_t protocol);
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr *ip, uint16_t len);
+
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
#endif /* __CHECKSUM_H__ */
diff --git a/clatd.c b/clatd.c
index 063026d..a89c588 100644
--- a/clatd.c
+++ b/clatd.c
@@ -29,15 +29,6 @@
#include <arpa/inet.h>
#include <fcntl.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
-
#include <sys/capability.h>
#include <sys/uio.h>
#include <linux/prctl.h>
@@ -47,8 +38,6 @@
#include <private/android_filesystem_config.h>
-#include "ipv4.h"
-#include "ipv6.h"
#include "translate.h"
#include "clatd.h"
#include "config.h"
@@ -62,14 +51,12 @@
#define DEVICENAME6 "clat"
#define DEVICENAME4 "clat4"
+/* 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header */
+#define MTU_DELTA 28
+
int forwarding_fd = -1;
volatile sig_atomic_t running = 1;
-struct tun_data {
- char device6[IFNAMSIZ], device4[IFNAMSIZ];
- int fd6, fd4;
-};
-
/* function: set_forwarding
* enables/disables ipv6 forwarding
*/
@@ -84,7 +71,7 @@ void set_forwarding(int fd, const char *setting) {
/* function: stop_loop
* signal handler: stop the event loop
*/
-void stop_loop(int signal) {
+void stop_loop() {
running = 0;
}
@@ -146,7 +133,6 @@ void deconfigure_tun_ipv6(const struct tun_data *tunnel) {
* tunnel - tun device data
*/
void configure_tun_ipv6(const struct tun_data *tunnel) {
- struct in6_addr local_nat64_prefix_6;
int status;
status = if_route(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_subnet,
@@ -205,6 +191,13 @@ void configure_tun_ip(const struct tun_data *tunnel) {
exit(1);
}
+ status = add_address(tunnel->device6, AF_INET6, &Global_Clatd_Config.ipv6_local_address,
+ 64, NULL);
+ if(status < 0) {
+ logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_address(6) failed: %s",strerror(-status));
+ exit(1);
+ }
+
if((status = if_up(tunnel->device6, Global_Clatd_Config.mtu)) < 0) {
logmsg(ANDROID_LOG_FATAL,"configure_tun_ip/if_up(6) failed: %s",strerror(-status));
exit(1);
@@ -281,8 +274,9 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix,
Global_Clatd_Config.mtu = 1280;
}
- if(Global_Clatd_Config.ipv4mtu <= 0 || (Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - 20)) {
- Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu-20;
+ if(Global_Clatd_Config.ipv4mtu <= 0 ||
+ Global_Clatd_Config.ipv4mtu > Global_Clatd_Config.mtu - MTU_DELTA) {
+ Global_Clatd_Config.ipv4mtu = Global_Clatd_Config.mtu - MTU_DELTA;
logmsg(ANDROID_LOG_WARN,"ipv4mtu now set to = %d",Global_Clatd_Config.ipv4mtu);
}
@@ -301,56 +295,6 @@ void configure_interface(const char *uplink_interface, const char *plat_prefix,
configure_tun_ip(tunnel);
}
-/* function: packet_handler
- * takes a tun header and a packet and sends it down the stack
- * tunnel - tun device data
- * tun_header - tun header
- * 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) {
- 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];
- char icmp_iphdr[sizeof(struct ip6_hdr)];
- char icmp_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.
- { icmp_iphdr, 0 }, // ICMP error inner IP header.
- { icmp_transporthdr, 0 }, // ICMP error 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(ntohs(tun_header->proto) == ETH_P_IP) {
- fd = tunnel->fd6;
- fill_tun_header(&tun_targ, ETH_P_IPV6);
- iov_len = ipv4_packet(out, CLAT_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, CLAT_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
* reads a packet from the tunnel fd and passes it down the stack
* active_fd - tun file descriptor marked ready for reading
@@ -372,7 +316,6 @@ void read_packet(int active_fd, const struct tun_data *tunnel) {
logmsg(ANDROID_LOG_WARN,"read_packet/tun interface removed");
running = 0;
} else {
- struct tun_pi tun_header;
ssize_t header_size = sizeof(struct tun_pi);
if(readlen < header_size) {
@@ -380,7 +323,7 @@ void read_packet(int active_fd, const struct tun_data *tunnel) {
return;
}
- packet_handler(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size);
+ translate_packet(tunnel, (struct tun_pi *) packet, packet + header_size, readlen - header_size);
}
}
@@ -470,7 +413,7 @@ int main(int argc, char **argv) {
// 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_open6 failed: %s", strerror(errno));
exit(1);
}
diff --git a/clatd.conf b/clatd.conf
index 086d39a..578da59 100644
--- a/clatd.conf
+++ b/clatd.conf
@@ -5,6 +5,9 @@ ipv6_host_id ::464
# ipv4 subnet for the local traffic to use. This is a /32 host address
ipv4_local_subnet 192.0.0.4
+# ipv6 extra link local address for the ip6 iface.
+ipv6_local_address fe80::c000:0004
+
# get the plat_subnet from dns lookups (requires DNS64)
plat_from_dns64 yes
# hostname to use to lookup plat subnet. must contain only A records
diff --git a/clatd.h b/clatd.h
index 3459b09..44a655e 100644
--- a/clatd.h
+++ b/clatd.h
@@ -18,6 +18,7 @@
#ifndef __CLATD_H__
#define __CLATD_H__
+#include <linux/if.h>
#include <linux/if_tun.h>
#define MAXMTU 1500
@@ -30,4 +31,9 @@
// how frequently (in seconds) to poll for an address change while there is no traffic
#define NO_TRAFFIC_INTERFACE_POLL_FREQUENCY 90
+struct tun_data {
+ char device6[IFNAMSIZ], device4[IFNAMSIZ];
+ int fd6, fd4;
+};
+
#endif /* __CLATD_H__ */
diff --git a/config.c b/config.c
index e7ec80e..b82ac6c 100644
--- a/config.c
+++ b/config.c
@@ -154,7 +154,7 @@ void free_config() {
* does dns lookups to set the plat subnet or exits on failure, waits forever for a dns response with a query backoff timer
*/
void dns64_detection() {
- int i, backoff_sleep, status;
+ int backoff_sleep, status;
struct in6_addr tmp_ptr;
backoff_sleep = 1;
@@ -255,6 +255,9 @@ int read_config(const char *file, const char *uplink_interface, const char *plat
if(!config_item_ip(root, "ipv4_local_subnet", DEFAULT_IPV4_LOCAL_SUBNET, &Global_Clatd_Config.ipv4_local_subnet))
goto failed;
+ if(!config_item_ip6(root, "ipv6_local_address", DEFAULT_IPV6_LOCAL_ADDRESS, &Global_Clatd_Config.ipv6_local_address))
+ goto failed;
+
if(plat_prefix) { // plat subnet is coming from the command line
if(inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) {
logmsg(ANDROID_LOG_FATAL,"invalid IPv6 address specified for plat prefix: %s", plat_prefix);
@@ -295,6 +298,7 @@ void dump_config() {
logmsg(ANDROID_LOG_DEBUG,"mtu = %d",Global_Clatd_Config.mtu);
logmsg(ANDROID_LOG_DEBUG,"ipv4mtu = %d",Global_Clatd_Config.ipv4mtu);
+ logmsg(ANDROID_LOG_DEBUG,"ipv6_local_address = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_address, charbuffer, sizeof(charbuffer)));
logmsg(ANDROID_LOG_DEBUG,"ipv6_local_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.ipv6_local_subnet, charbuffer, sizeof(charbuffer)));
logmsg(ANDROID_LOG_DEBUG,"ipv4_local_subnet = %s",inet_ntop(AF_INET, &Global_Clatd_Config.ipv4_local_subnet, charbuffer, sizeof(charbuffer)));
logmsg(ANDROID_LOG_DEBUG,"plat_subnet = %s",inet_ntop(AF_INET6, &Global_Clatd_Config.plat_subnet, charbuffer, sizeof(charbuffer)));
diff --git a/config.h b/config.h
index a83cbec..18760c3 100644
--- a/config.h
+++ b/config.h
@@ -22,10 +22,13 @@
#include <sys/system_properties.h>
#define DEFAULT_IPV4_LOCAL_SUBNET "192.168.255.1"
+#define DEFAULT_IPV6_LOCAL_ADDRESS "fe80::c000:0004"
+
#define DEFAULT_DNS64_DETECTION_HOSTNAME "ipv4.google.com"
struct clat_config {
int16_t mtu, ipv4mtu;
+ struct in6_addr ipv6_local_address;
struct in6_addr ipv6_local_subnet;
struct in6_addr ipv6_host_id;
struct in_addr ipv4_local_subnet;
diff --git a/dump.c b/dump.c
index 8567655..ba5fa3e 100644
--- a/dump.c
+++ b/dump.c
@@ -147,14 +147,14 @@ void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const ch
/* print ipv4/udp header */
void dump_udp(const struct udphdr *udp, const struct iphdr *ip, const char *payload, size_t payload_size) {
uint32_t temp_checksum;
- temp_checksum = ipv4_pseudo_header_checksum(0, ip, sizeof(*udp) + payload_size);
+ temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*udp) + payload_size);
dump_udp_generic(udp, temp_checksum, payload, payload_size);
}
/* print ipv6/udp header */
void dump_udp6(const struct udphdr *udp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size) {
uint32_t temp_checksum;
- temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*udp) + payload_size);
+ temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*udp) + payload_size, IPPROTO_UDP);
dump_udp_generic(udp, temp_checksum, payload, payload_size);
}
@@ -203,7 +203,7 @@ void dump_tcp_generic(const struct tcphdr *tcp, const char *options, size_t opti
void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *payload, size_t payload_size, const char *options, size_t options_size) {
uint32_t temp_checksum;
- temp_checksum = ipv4_pseudo_header_checksum(0, ip, sizeof(*tcp) + options_size + payload_size);
+ temp_checksum = ipv4_pseudo_header_checksum(ip, sizeof(*tcp) + options_size + payload_size);
dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
}
@@ -211,7 +211,7 @@ void dump_tcp(const struct tcphdr *tcp, const struct iphdr *ip, const char *payl
void dump_tcp6(const struct tcphdr *tcp, const struct ip6_hdr *ip6, const char *payload, size_t payload_size, const char *options, size_t options_size) {
uint32_t temp_checksum;
- temp_checksum = ipv6_pseudo_header_checksum(0, ip6, sizeof(*tcp) + options_size + payload_size);
+ temp_checksum = ipv6_pseudo_header_checksum(ip6, sizeof(*tcp) + options_size + payload_size, IPPROTO_TCP);
dump_tcp_generic(tcp, options, options_size, temp_checksum, payload, payload_size);
}
@@ -220,6 +220,7 @@ void logcat_hexdump(const char *info, const char *data, size_t len) {
char output[PACKETLEN*3+2];
size_t i;
+ output[0] = '\0';
for(i = 0; i < len && i < PACKETLEN; i++) {
snprintf(output + i*3, 4, " %02x", (uint8_t)data[i]);
}
diff --git a/icmp.c b/icmp.c
index af96b83..75a4a4d 100644
--- a/icmp.c
+++ b/icmp.c
@@ -139,7 +139,7 @@ uint8_t icmp6_to_icmp_type(uint8_t type, uint8_t code) {
}
// We don't understand this ICMP type. Return parameter problem so the caller will bail out.
- logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type %d", type);
+ logmsg_dbg(ANDROID_LOG_DEBUG, "icmp6_to_icmp_type: unhandled ICMP type/code %d/%d", type, code);
return ICMP_PARAMETERPROB;
}
diff --git a/ipv4.c b/ipv4.c
index 89e47e4..84dccd5 100644
--- a/ipv4.c
+++ b/ipv4.c
@@ -17,18 +17,8 @@
*/
#include <string.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
-
#include "translate.h"
#include "checksum.h"
-#include "ipv4.h"
#include "logging.h"
#include "debug.h"
#include "dump.h"
@@ -67,11 +57,12 @@ int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t c
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;
+ struct ip6_frag *frag_hdr;
+ size_t frag_hdr_len;
uint8_t nxthdr;
const char *next_header;
size_t len_left;
- uint32_t checksum;
+ uint32_t old_sum, new_sum;
int iov_len;
if(len < sizeof(struct iphdr)) {
@@ -79,12 +70,6 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) {
return 0;
}
- 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 0;
- }
-
if(header->ihl < 5) {
logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl);
return 0;
@@ -121,15 +106,32 @@ int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) {
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);
+ /* Calculate the pseudo-header checksum.
+ * Technically, the length that is used in the pseudo-header checksum is the transport layer
+ * length, which is not the same as len_left in the case of fragmented packets. But since
+ * translation does not change the transport layer length, the checksum is unaffected.
+ */
+ old_sum = ipv4_pseudo_header_checksum(header, len_left);
+ new_sum = ipv6_pseudo_header_checksum(ip6_targ, len_left, nxthdr);
+
+ // If the IPv4 packet is fragmented, add a Fragment header.
+ frag_hdr = (struct ip6_frag *) out[pos + 1].iov_base;
+ frag_hdr_len = maybe_fill_frag_header(frag_hdr, ip6_targ, header);
+ out[pos + 1].iov_len = frag_hdr_len;
+
+ if (frag_hdr_len && frag_hdr->ip6f_offlg & IP6F_OFF_MASK) {
+ // Non-first fragment. Copy the rest of the packet as is.
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else if (nxthdr == IPPROTO_ICMPV6) {
+ iov_len = icmp_packet(out, pos + 2, (const struct icmphdr *) next_header, new_sum, len_left);
+ } else if (nxthdr == IPPROTO_TCP) {
+ iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum,
+ len_left);
+ } else if (nxthdr == IPPROTO_UDP) {
+ iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum,
+ len_left);
+ } else if (nxthdr == IPPROTO_GRE) {
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
} else {
#if CLAT_DEBUG
logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol);
diff --git a/ipv4.h b/ipv4.h
deleted file mode 100644
index 00befd2..0000000
--- a/ipv4.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2011 Daniel Drown
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * ipv4.h - takes an ipv4 packet and hands it off to the proper translate function
- */
-#ifndef __IPV4_H__
-#define __IPV4_H__
-
-#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 ef1e62f..2e83be1 100644
--- a/ipv6.c
+++ b/ipv6.c
@@ -17,19 +17,10 @@
*/
#include <string.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
#include <arpa/inet.h>
#include "translate.h"
#include "checksum.h"
-#include "ipv6.h"
#include "logging.h"
#include "dump.h"
#include "config.h"
@@ -43,8 +34,7 @@
* len - size of ip payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
- size_t len) {
+int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) {
const char *payload;
size_t payload_size;
@@ -56,7 +46,7 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32
payload = (const char *) (icmp6 + 1);
payload_size = len - sizeof(struct icmp6_hdr);
- return icmp6_to_icmp(out, pos, icmp6, checksum, payload, payload_size);
+ return icmp6_to_icmp(out, pos, icmp6, payload, payload_size);
}
/* function: log_bad_address
@@ -64,16 +54,18 @@ int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32
* fmt - printf-style format, use %s to place the address
* badaddr - the bad address in question
*/
-void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
#if CLAT_DEBUG
+void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
char srcstr[INET6_ADDRSTRLEN];
char dststr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr));
inet_ntop(AF_INET6, dst, dststr, sizeof(dststr));
logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr);
-#endif
}
+#else
+#define log_bad_address(fmt, src, dst)
+#endif
/* function: ipv6_packet
* takes an ipv6 packet and hands it off to the layer 4 protocol function
@@ -85,12 +77,12 @@ void log_bad_address(const char *fmt, const struct in6_addr *src, const struct i
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;
+ struct ip6_frag *frag_hdr = NULL;
uint8_t protocol;
const char *next_header;
size_t len_left;
- uint32_t checksum;
+ uint32_t old_sum, new_sum;
int iov_len;
- int i;
if(len < sizeof(struct ip6_hdr)) {
logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len);
@@ -121,10 +113,6 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) {
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
@@ -133,19 +121,48 @@ int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) {
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);
+ // If there's a Fragment header, parse it and decide what the next header is.
+ // Do this before calculating the pseudo-header checksum because it updates the next header value.
+ if (protocol == IPPROTO_FRAGMENT) {
+ frag_hdr = (struct ip6_frag *) next_header;
+ if (len_left < sizeof(*frag_hdr)) {
+ logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len);
+ return 0;
+ }
+
+ next_header += sizeof(*frag_hdr);
+ len_left -= sizeof(*frag_hdr);
- // does not support IPv6 extension headers, this will drop any packet with them
- 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,
+ protocol = parse_frag_header(frag_hdr, ip_targ);
+ }
+
+ // ICMP and ICMPv6 have different protocol numbers.
+ if (protocol == IPPROTO_ICMPV6) {
+ protocol = IPPROTO_ICMP;
+ ip_targ->protocol = IPPROTO_ICMP;
+ }
+
+ /* Calculate the pseudo-header checksum.
+ * Technically, the length that is used in the pseudo-header checksum is the transport layer
+ * length, which is not the same as len_left in the case of fragmented packets. But since
+ * translation does not change the transport layer length, the checksum is unaffected.
+ */
+ old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol);
+ new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left);
+
+ // Does not support IPv6 extension headers except Fragment.
+ if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) {
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
+ } else if (protocol == IPPROTO_ICMP) {
+ iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left);
+ } else if (protocol == IPPROTO_TCP) {
+ iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum,
len_left);
- } else if(ip6->ip6_nxt == IPPROTO_UDP) {
- iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum,
+ } else if (protocol == IPPROTO_UDP) {
+ iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum,
len_left);
+ } else if (protocol == IPPROTO_GRE) {
+ iov_len = generic_packet(out, pos + 2, next_header, len_left);
} else {
#if CLAT_DEBUG
logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt);
diff --git a/ipv6.h b/ipv6.h
deleted file mode 100644
index 019de17..0000000
--- a/ipv6.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2011 Daniel Drown
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * ipv6.h - takes an ipv6 packet and hands it off to the proper translate function
- */
-#ifndef __IPV6_H__
-#define __IPV6_H__
-
-#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 fc70f3d..7585092 100644
--- a/translate.c
+++ b/translate.c
@@ -16,15 +16,7 @@
* translate.c - CLAT functions / partial implementation of rfc6145
*/
#include <string.h>
-
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <netinet/ip6.h>
-#include <netinet/icmp6.h>
-#include <linux/icmp.h>
+#include <sys/uio.h>
#include "icmp.h"
#include "translate.h"
@@ -149,7 +141,7 @@ void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
// Third-party ICMPv6 message. This may have been originated by an native IPv6 address.
// In that case, the source IPv6 address can't be translated and we need to make up an IPv4
// source address. For now, use 255.0.0.<ttl>, which at least looks useful in traceroute.
- if (ip->saddr == (uint32_t) INADDR_NONE) {
+ if ((uint32_t) ip->saddr == INADDR_NONE) {
ttl_guess = icmp_guess_ttl(old_header->ip6_hlim);
ip->saddr = htonl((0xff << 24) + ttl_guess);
}
@@ -175,6 +167,54 @@ void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol
ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr);
}
+/* function: maybe_fill_frag_header
+ * fills a fragmentation header
+ * generate an ipv6 fragment header from an ipv4 header
+ * frag_hdr - target (ipv6) fragmentation header
+ * ip6_targ - target (ipv6) header
+ * old_header - (ipv4) source packet header
+ * returns: the length of the fragmentation header if present, or zero if not present
+ */
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header) {
+ uint16_t frag_flags = ntohs(old_header->frag_off);
+ uint16_t frag_off = frag_flags & IP_OFFMASK;
+ if (frag_off == 0 && (frag_flags & IP_MF) == 0) {
+ // Not a fragment.
+ return 0;
+ }
+
+ frag_hdr->ip6f_nxt = ip6_targ->ip6_nxt;
+ frag_hdr->ip6f_reserved = 0;
+ // In IPv4, the offset is the bottom 13 bits; in IPv6 it's the top 13 bits.
+ frag_hdr->ip6f_offlg = htons(frag_off << 3);
+ if (frag_flags & IP_MF) {
+ frag_hdr->ip6f_offlg |= IP6F_MORE_FRAG;
+ }
+ frag_hdr->ip6f_ident = htonl(ntohs(old_header->id));
+ ip6_targ->ip6_nxt = IPPROTO_FRAGMENT;
+
+ return sizeof(*frag_hdr);
+}
+
+/* function: parse_frag_header
+ * return the length of the fragmentation header if present, or zero if not present
+ * generate an ipv6 fragment header from an ipv4 header
+ * frag_hdr - (ipv6) fragmentation header
+ * ip_targ - target (ipv4) header
+ * returns: the next header value
+ */
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ) {
+ uint16_t frag_off = (ntohs(frag_hdr->ip6f_offlg & IP6F_OFF_MASK) >> 3);
+ if (frag_hdr->ip6f_offlg & IP6F_MORE_FRAG) {
+ frag_off |= IP_MF;
+ }
+ ip_targ->frag_off = htons(frag_off);
+ ip_targ->id = htons(ntohl(frag_hdr->ip6f_ident) & 0xffff);
+ ip_targ->protocol = frag_hdr->ip6f_nxt;
+ return frag_hdr->ip6f_nxt;
+}
+
/* function: icmp_to_icmp6
* translate ipv4 icmp to ipv6 icmp
* out - output packet
@@ -208,12 +248,10 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t
// The pseudo-header checksum was calculated on the transport length of the original IPv4
// packet that we were asked to translate. This transport length is 20 bytes smaller than it
// needs to be, because the ICMP error contains an IPv4 header, which we will be translating to
- // an IPv6 header, which is 20 bytes longer. Fix it up here. This is simpler than the
- // alternative, which is to always update the pseudo-header checksum in all UDP/TCP/ICMP
- // translation functions (rather than pre-calculating it when translating the IPv4 header).
+ // an IPv6 header, which is 20 bytes longer. Fix it up here.
// We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the
// pseudo-header when calculating its checksum (as the IPv4 header has its own checksum).
- checksum = htonl(ntohl(checksum) + 20);
+ checksum = checksum + htons(20);
} else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
// Ping packet.
icmp6_targ->icmp6_id = icmp->un.echo.id;
@@ -236,16 +274,14 @@ int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t
* translate ipv6 icmp to ipv4 icmp
* 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
*/
-int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
+int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6,
const char *payload, size_t payload_size) {
struct icmphdr *icmp_targ = out[pos].iov_base;
uint8_t icmp_type;
- int ttl;
int clat_packet_len;
memset(icmp_targ, 0, sizeof(struct icmphdr));
@@ -280,14 +316,32 @@ int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint3
return clat_packet_len;
}
+/* function: generic_packet
+ * takes a generic IP packet and sets it up for translation
+ * out - output packet
+ * pos - position in the output packet of the transport header
+ * payload - pointer to IP payload
+ * len - size of ip payload
+ * returns: the highest position in the output clat_packet that's filled in
+ */
+int generic_packet(clat_packet out, int pos, const char *payload, size_t len) {
+ out[pos].iov_len = 0;
+ out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
+ out[CLAT_POS_PAYLOAD].iov_len = len;
+
+ return CLAT_POS_PAYLOAD + 1;
+}
+
/* 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
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
* len - size of ip payload
*/
-int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) {
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp,
+ uint32_t old_sum, uint32_t new_sum, size_t len) {
const char *payload;
size_t payload_size;
@@ -299,7 +353,7 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec
payload = (const char *) (udp + 1);
payload_size = len - sizeof(struct udphdr);
- return udp_translate(out, pos, udp, checksum, payload, payload_size);
+ return udp_translate(out, pos, udp, old_sum, new_sum, payload, payload_size);
}
/* function: tcp_packet
@@ -310,7 +364,8 @@ int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t chec
* len - size of ip payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) {
+int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp,
+ uint32_t old_sum, uint32_t new_sum, size_t len) {
const char *payload;
size_t payload_size, header_size;
@@ -333,20 +388,21 @@ int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t chec
payload = ((const char *) tcp) + header_size;
payload_size = len - header_size;
- return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size);
+ return tcp_translate(out, pos, tcp, header_size, old_sum, new_sum, payload, payload_size);
}
/* function: udp_translate
* common between ipv4/ipv6 - setup checksum and send udp packet
* out - output packet
* udp - udp header
- * checksum - pseudo-header checksum
+ * old_sum - pseudo-header checksum of old header
+ * new_sum - pseudo-header checksum of new header
* payload - tcp payload
* payload_size - size of payload
* returns: the highest position in the output clat_packet that's filled in
*/
-int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
- const char *payload, size_t payload_size) {
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t old_sum,
+ uint32_t new_sum, const char *payload, size_t payload_size) {
struct udphdr *udp_targ = out[pos].iov_base;
memcpy(udp_targ, udp, sizeof(struct udphdr));
@@ -355,8 +411,22 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c
out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
out[CLAT_POS_PAYLOAD].iov_len = payload_size;
- udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
- udp_targ->check = packet_checksum(checksum, out, pos);
+ if (udp_targ->check) {
+ udp_targ->check = ip_checksum_adjust(udp->check, old_sum, new_sum);
+ } else {
+ // Zero checksums are special. RFC 768 says, "An all zero transmitted checksum value means that
+ // the transmitter generated no checksum (for debugging or for higher level protocols that
+ // don't care)." However, in IPv6 zero UDP checksums were only permitted by RFC 6935 (2013). So
+ // for safety we recompute it.
+ udp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
+ udp_targ->check = packet_checksum(new_sum, out, pos);
+ }
+
+ // RFC 768: "If the computed checksum is zero, it is transmitted as all ones (the equivalent
+ // in one's complement arithmetic)."
+ if (!udp_targ->check) {
+ udp_targ->check = 0xffff;
+ }
return CLAT_POS_PAYLOAD + 1;
}
@@ -370,12 +440,9 @@ int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t c
* payload - tcp payload
* payload_size - size of payload
* 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)
*/
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) {
+ uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size) {
struct tcphdr *tcp_targ = out[pos].iov_base;
out[pos].iov_len = header_size;
@@ -392,8 +459,61 @@ int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t hea
out[CLAT_POS_PAYLOAD].iov_base = (char *)payload;
out[CLAT_POS_PAYLOAD].iov_len = payload_size;
- tcp_targ->check = 0; // Checksum field must be 0 when calculating checksum.
- tcp_targ->check = packet_checksum(checksum, out, pos);
+ tcp_targ->check = ip_checksum_adjust(tcp->check, old_sum, new_sum);
return CLAT_POS_PAYLOAD + 1;
}
+
+/* function: translate_packet
+ * takes a tun header and a packet and sends it down the stack
+ * tunnel - tun device data
+ * tun_header - tun header
+ * packet - packet
+ * packetsize - size of packet
+ */
+void translate_packet(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 fraghdr[sizeof(struct ip6_frag)];
+ char transporthdr[MAX_TCP_HDR];
+ char icmp_iphdr[sizeof(struct ip6_hdr)];
+ char icmp_fraghdr[sizeof(struct ip6_frag)];
+ char icmp_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.
+ { fraghdr, 0 }, // Fragment header.
+ { transporthdr, 0 }, // Transport layer header.
+ { icmp_iphdr, 0 }, // ICMP error inner IP header.
+ { icmp_fraghdr, 0 }, // ICMP error fragmentation header.
+ { icmp_transporthdr, 0 }, // ICMP error 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, "translate_packet: unexpected flags = %d", tun_header->flags);
+ }
+
+ if(ntohs(tun_header->proto) == ETH_P_IP) {
+ fd = tunnel->fd6;
+ fill_tun_header(&tun_targ, ETH_P_IPV6);
+ iov_len = ipv4_packet(out, CLAT_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, CLAT_POS_IPHDR, packet, packetsize);
+ } else {
+ logmsg(ANDROID_LOG_WARN, "translate_packet: unknown packet type = %x",tun_header->proto);
+ }
+
+ if (iov_len > 0) {
+ writev(fd, out, iov_len);
+ }
+}
diff --git a/translate.h b/translate.h
index fded251..5efa817 100644
--- a/translate.h
+++ b/translate.h
@@ -18,17 +18,29 @@
#ifndef __TRANSLATE_H__
#define __TRANSLATE_H__
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <linux/icmp.h>
#include <linux/if_tun.h>
+#include "clatd.h"
+
#define MAX_TCP_HDR (15 * 4) // Data offset field is 4 bits and counts in 32-bit words.
// A clat_packet is an array of iovec structures representing a packet that we are translating.
// The CLAT_POS_XXX constants represent the array indices within the clat_packet that contain
// specific parts of the packet. The packet_* functions operate on all the packet segments past a
// given position.
-enum clat_packet_index { CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_TRANSPORTHDR,
- CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_TRANSPORTHDR,
- CLAT_POS_PAYLOAD, CLAT_POS_MAX };
+enum clat_packet_index {
+ CLAT_POS_TUNHDR, CLAT_POS_IPHDR, CLAT_POS_FRAGHDR, CLAT_POS_TRANSPORTHDR,
+ CLAT_POS_ICMPERR_IPHDR, CLAT_POS_ICMPERR_FRAGHDR, CLAT_POS_ICMPERR_TRANSPORTHDR,
+ CLAT_POS_PAYLOAD, CLAT_POS_MAX
+};
typedef struct iovec clat_packet[CLAT_POS_MAX];
// Calculates the checksum over all the packet components starting from pos.
@@ -47,19 +59,37 @@ void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protoco
void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
const struct iphdr *old_header);
+// Translate and send packets.
+void translate_packet(const struct tun_data *tunnel, struct tun_pi *tun_header, const char *packet,
+ size_t packetsize);
+
+// Translate IPv4 and IPv6 packets.
+int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len);
+int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len);
+
+// Deal with fragmented packets.
+size_t maybe_fill_frag_header(struct ip6_frag *frag_hdr, struct ip6_hdr *ip6_targ,
+ const struct iphdr *old_header);
+uint8_t parse_frag_header(const struct ip6_frag *frag_hdr, struct iphdr *ip_targ);
+
// 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,
+int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6,
const char *payload, size_t payload_size);
+// Translate generic IP packets.
+int generic_packet(clat_packet out, int pos, const char *payload, size_t len);
+
// 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_packet(clat_packet out, int pos, const struct tcphdr *tcp,
+ uint32_t old_sum, uint32_t new_sum, size_t len);
+int udp_packet(clat_packet out, int pos, const struct udphdr *udp,
+ uint32_t old_sum, uint32_t new_sum, 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);
+ uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size);
+int udp_translate(clat_packet out, int pos, const struct udphdr *udp,
+ uint32_t old_sum, uint32_t new_sum, const char *payload, size_t payload_size);
#endif /* __TRANSLATE_H__ */