blob: 61013f2aec4f96338e3a82be8534b380d5a68b8a [file] [log] [blame]
/*
* 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.c - takes ipv6 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 "ipv6.h"
#include "logging.h"
#include "dump.h"
#include "config.h"
#include "debug.h"
/* 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
*/
void icmp6_packet(int fd, const char *packet, size_t len, struct ip6_hdr *ip6) {
struct icmp6_hdr icmp6;
const char *payload;
size_t payload_size;
if(len < sizeof(icmp6)) {
logmsg_dbg(ANDROID_LOG_ERROR,"icmp6_packet/(too small)");
return;
}
memcpy(&icmp6, packet, sizeof(icmp6));
payload = packet + sizeof(icmp6);
payload_size = len - sizeof(icmp6);
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);
}
/* function: log_bad_address
* logs a bad address to android's log buffer if debugging is turned on
* 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 *badaddr) {
#if CLAT_DEBUG
char badaddr_str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, badaddr, badaddr_str, sizeof(badaddr_str));
logmsg_dbg(ANDROID_LOG_ERROR,fmt,badaddr_str);
#endif
}
/* function: ipv6_packet
* takes an ipv6 packet and hands it off to the layer 4 protocol function
* fd - tun interface fd
* packet - packet data
* len - size of packet
*/
void ipv6_packet(int fd, const char *packet, size_t len) {
struct ip6_hdr header;
const char *next_header;
size_t len_left;
int i;
if(len < sizeof(header)) {
logmsg_dbg(ANDROID_LOG_ERROR,"ipv6_packet/too short for an ip6 header");
return;
}
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
}
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(!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;
}
// 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);
} else {
#if CLAT_DEBUG
logmsg(ANDROID_LOG_ERROR,"ipv6_packet/unknown next header type: %x",header.ip6_nxt);
logcat_hexdump("ipv6/nxthdr", packet, len);
#endif
}
}