summaryrefslogtreecommitdiffstats
path: root/ipv4.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipv4.c')
-rw-r--r--ipv4.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/ipv4.c b/ipv4.c
new file mode 100644
index 0000000..784f10c
--- /dev/null
+++ b/ipv4.c
@@ -0,0 +1,187 @@
+/*
+ * 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.c - takes ipv4 packets, finds their headers, and then calls translation functions on them
+ */
+#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"
+
+/* 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
+ */
+void icmp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) {
+ struct icmphdr icmp;
+ const char *payload;
+ size_t payload_size;
+
+ if(len < sizeof(icmp)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"icmp_packet/(too small)");
+ return;
+ }
+
+ memcpy(&icmp, packet, sizeof(icmp));
+ payload = packet + sizeof(icmp);
+ payload_size = len - sizeof(icmp);
+
+ icmp_to_icmp6(fd,ip,&icmp,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) {
+ struct tcphdr tcp;
+ const char *payload;
+ const char *options;
+ size_t payload_size, options_size;
+
+ if(len < sizeof(tcp)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)");
+ return;
+ }
+
+ memcpy(&tcp, packet, sizeof(tcp));
+
+ 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;
+ }
+
+ if(tcp.doff > 5) {
+ options = packet + sizeof(tcp);
+ options_size = tcp.doff*4 - sizeof(tcp);
+ } else {
+ options = NULL;
+ options_size = 0;
+ }
+
+ payload = packet + tcp.doff*4;
+ payload_size = len - tcp.doff*4;
+
+ tcp_to_tcp6(fd,ip,&tcp,payload,payload_size,options,options_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
+ * packet - packet data
+ * len - size of packet
+ */
+void ip_packet(int fd, const char *packet, size_t len) {
+ struct iphdr header;
+ uint16_t frag_flags;
+ const char *next_header;
+ size_t len_left;
+
+ if(len < sizeof(header)) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/too short for an ip header");
+ return;
+ }
+
+ memcpy(&header, packet, sizeof(header));
+
+ 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;
+ }
+
+ if(header.ihl < 5) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header length set to less than 5: %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;
+ }
+
+ if(header.version != 4) {
+ logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header version not 4: %x",header.version);
+ return;
+ }
+
+ /* rfc6145 - If any IPv4 options are present in the IPv4 packet, they MUST be
+ * ignored and the packet translated normally; there is no attempt to
+ * translate the options.
+ */
+
+ next_header = packet + header.ihl*4;
+ len_left = len - header.ihl*4;
+
+ 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);
+ } else {
+#if CLAT_DEBUG
+ logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/unknown protocol: %x",header.protocol);
+ logcat_hexdump("ipv4/protocol", packet, len);
+#endif
+ }
+}