summaryrefslogtreecommitdiffstats
path: root/ipv6.c
blob: 8fc36a49c017711be2348df1430c0ebc509acdf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * 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) {
  struct tcphdr tcp;
  const char *payload;
  const char *options;
  size_t payload_size, options_size;

  if(len < sizeof(tcp)) {
    logmsg_dbg(ANDROID_LOG_ERROR,"tcp6_packet/(too small)");
    return;
  }

  memcpy(&tcp, packet, sizeof(tcp));

  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;
  }

  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;

  tcp6_to_tcp(fd,ip6,&tcp,payload,payload_size,options,options_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
  }
}