Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2011 Daniel Drown |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | * |
| 16 | * translate.c - CLAT functions / partial implementation of rfc6145 |
| 17 | */ |
| 18 | #include <string.h> |
| 19 | #include <sys/uio.h> |
| 20 | |
| 21 | #include <netinet/in.h> |
| 22 | #include <netinet/ip.h> |
| 23 | #include <netinet/ip_icmp.h> |
| 24 | #include <netinet/udp.h> |
| 25 | #include <netinet/tcp.h> |
| 26 | #include <netinet/ip6.h> |
| 27 | #include <netinet/icmp6.h> |
| 28 | #include <linux/icmp.h> |
| 29 | |
| 30 | #include "checksum.h" |
| 31 | #include "clatd.h" |
| 32 | #include "config.h" |
| 33 | #include "logging.h" |
| 34 | #include "debug.h" |
| 35 | |
| 36 | /* function: fill_tun_header |
| 37 | * fill in the header for the tun fd |
| 38 | * tun_header - tunnel header, already allocated |
| 39 | * proto - ethernet protocol id: ETH_P_IP(ipv4) or ETH_P_IPV6(ipv6) |
| 40 | */ |
| 41 | void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) { |
| 42 | tun_header->flags = 0; |
| 43 | tun_header->proto = htons(proto); |
| 44 | } |
| 45 | |
| 46 | /* function: ipv6_src_to_ipv4_src |
| 47 | * return the corresponding ipv4 address for the given ipv6 address |
| 48 | * sourceaddr - ipv6 source address |
| 49 | */ |
| 50 | uint32_t ipv6_src_to_ipv4_src(const struct in6_addr *sourceaddr) { |
| 51 | // assumes a /96 plat subnet |
| 52 | return sourceaddr->s6_addr32[3]; |
| 53 | } |
| 54 | |
| 55 | /* function: fill_ip_header |
| 56 | * generating an ipv4 header from an ipv6 header (called by the layer 4 protocol-specific functions) |
| 57 | * ip_targ - (ipv4) target packet header, source addr: original ipv4 addr, dest addr: local subnet addr |
| 58 | * payload_len - length of other data inside packet |
| 59 | * protocol - protocol number (tcp, udp, etc) |
| 60 | * old_header - (ipv6) source packet header, source addr: nat64 prefix, dest addr: local subnet prefix |
| 61 | */ |
| 62 | void fill_ip_header(struct iphdr *ip_targ, uint16_t payload_len, uint8_t protocol, const struct ip6_hdr *old_header) { |
| 63 | memset(ip_targ, 0, sizeof(ip_targ)); |
| 64 | |
| 65 | ip_targ->ihl = 5; |
| 66 | ip_targ->version = 4; |
| 67 | ip_targ->tos = 0; |
| 68 | ip_targ->tot_len = htons(sizeof(struct iphdr) + payload_len); |
| 69 | ip_targ->id = 0; |
| 70 | ip_targ->frag_off = htons(IP_DF); |
| 71 | ip_targ->ttl = old_header->ip6_hlim; |
| 72 | ip_targ->protocol = protocol; |
| 73 | ip_targ->check = 0; |
| 74 | |
| 75 | ip_targ->saddr = ipv6_src_to_ipv4_src(&old_header->ip6_src); |
| 76 | ip_targ->daddr = Global_Clatd_Config.ipv4_local_subnet.s_addr; |
| 77 | |
| 78 | ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr)); |
| 79 | } |
| 80 | |
| 81 | /* function: ipv4_dst_to_ipv6_dst |
| 82 | * return the corresponding ipv6 address for the given ipv4 address |
| 83 | * destination - ipv4 destination address (network byte order) |
| 84 | */ |
| 85 | struct in6_addr ipv4_dst_to_ipv6_dst(uint32_t destination) { |
| 86 | struct in6_addr v6_destination; |
| 87 | |
| 88 | // assumes a /96 plat subnet |
| 89 | v6_destination = Global_Clatd_Config.plat_subnet; |
| 90 | v6_destination.s6_addr32[3] = destination; |
| 91 | |
| 92 | return v6_destination; |
| 93 | } |
| 94 | |
| 95 | /* function: fill_ip6_header |
| 96 | * generating an ipv6 header from an ipv4 header (called by the layer 4 protocol-specific functions) |
| 97 | * ip6 - (ipv6) target packet header, source addr: local subnet prefix, dest addr: nat64 prefix |
| 98 | * payload_len - length of other data inside packet |
| 99 | * protocol - protocol number (tcp, udp, etc) |
| 100 | * old_header - (ipv4) source packet header, source addr: local subnet addr, dest addr: internet's ipv4 addr |
| 101 | */ |
| 102 | void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol, const struct iphdr *old_header) { |
| 103 | memset(ip6, 0, sizeof(struct ip6_hdr)); |
| 104 | |
| 105 | ip6->ip6_vfc = 6 << 4; |
| 106 | ip6->ip6_plen = htons(payload_len); |
| 107 | ip6->ip6_nxt = protocol; |
| 108 | ip6->ip6_hlim = old_header->ttl; |
| 109 | |
| 110 | ip6->ip6_src = Global_Clatd_Config.ipv6_local_subnet; |
| 111 | ip6->ip6_dst = ipv4_dst_to_ipv6_dst(old_header->daddr); |
| 112 | } |
| 113 | |
| 114 | /* function: icmp_to_icmp6 |
| 115 | * translate ipv4 icmp to ipv6 icmp (only currently supports echo/echo reply) |
| 116 | * fd - tun interface fd |
| 117 | * ip - source packet ipv4 header |
| 118 | * icmp - source packet icmp header |
| 119 | * payload - icmp payload |
| 120 | * payload_size - size of payload |
| 121 | */ |
| 122 | void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size) { |
| 123 | struct ip6_hdr ip6_targ; |
| 124 | struct icmp6_hdr icmp6_targ; |
| 125 | struct iovec io_targ[4]; |
| 126 | struct tun_pi tun_header; |
| 127 | uint32_t checksum_temp; |
| 128 | |
| 129 | if((icmp->type != ICMP_ECHO) && (icmp->type != ICMP_ECHOREPLY)) { |
| 130 | logmsg_dbg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x",icmp->type); |
| 131 | return; |
| 132 | } |
| 133 | |
| 134 | fill_tun_header(&tun_header,ETH_P_IPV6); |
| 135 | |
| 136 | fill_ip6_header(&ip6_targ,payload_size + sizeof(icmp6_targ),IPPROTO_ICMPV6,ip); |
| 137 | |
| 138 | memset(&icmp6_targ, 0, sizeof(icmp6_targ)); |
| 139 | icmp6_targ.icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY; |
| 140 | icmp6_targ.icmp6_code = 0; |
| 141 | icmp6_targ.icmp6_cksum = 0; |
| 142 | icmp6_targ.icmp6_id = icmp->un.echo.id; |
| 143 | icmp6_targ.icmp6_seq = icmp->un.echo.sequence; |
| 144 | |
| 145 | checksum_temp = ipv6_pseudo_header_checksum(0,&ip6_targ); |
| 146 | checksum_temp = ip_checksum_add(checksum_temp, &icmp6_targ, sizeof(icmp6_targ)); |
| 147 | checksum_temp = ip_checksum_add(checksum_temp, payload, payload_size); |
| 148 | icmp6_targ.icmp6_cksum = ip_checksum_finish(checksum_temp); |
| 149 | |
| 150 | io_targ[0].iov_base = &tun_header; |
| 151 | io_targ[0].iov_len = sizeof(tun_header); |
| 152 | io_targ[1].iov_base = &ip6_targ; |
| 153 | io_targ[1].iov_len = sizeof(ip6_targ); |
| 154 | io_targ[2].iov_base = &icmp6_targ; |
| 155 | io_targ[2].iov_len = sizeof(icmp6_targ); |
| 156 | io_targ[3].iov_base = (char *)payload; |
| 157 | io_targ[3].iov_len = payload_size; |
| 158 | |
| 159 | writev(fd, io_targ, 4); |
| 160 | } |
| 161 | |
| 162 | /* function: icmp6_to_icmp |
| 163 | * translate ipv6 icmp to ipv4 icmp (only currently supports echo/echo reply) |
| 164 | * fd - tun interface fd |
| 165 | * ip6 - source packet ipv6 header |
| 166 | * icmp6 - source packet icmp6 header |
| 167 | * payload - icmp6 payload |
| 168 | * payload_size - size of payload |
| 169 | */ |
| 170 | void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { |
| 171 | struct iphdr ip_targ; |
| 172 | struct icmphdr icmp_targ; |
| 173 | struct iovec io_targ[4]; |
| 174 | struct tun_pi tun_header; |
| 175 | uint32_t temp_icmp_checksum; |
| 176 | |
| 177 | if((icmp6->icmp6_type != ICMP6_ECHO_REQUEST) && (icmp6->icmp6_type != ICMP6_ECHO_REPLY)) { |
| 178 | logmsg_dbg(ANDROID_LOG_WARN,"icmp6_to_icmp/unhandled icmp6 type: 0x%x",icmp6->icmp6_type); |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | memset(&icmp_targ, 0, sizeof(icmp_targ)); |
| 183 | |
| 184 | fill_tun_header(&tun_header,ETH_P_IP); |
| 185 | fill_ip_header(&ip_targ,sizeof(icmp_targ) + payload_size, IPPROTO_ICMP, ip6); |
| 186 | |
| 187 | icmp_targ.type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY; |
| 188 | icmp_targ.code = 0x0; |
| 189 | icmp_targ.checksum = 0; |
| 190 | icmp_targ.un.echo.id = icmp6->icmp6_id; |
| 191 | icmp_targ.un.echo.sequence = icmp6->icmp6_seq; |
| 192 | |
| 193 | temp_icmp_checksum = ip_checksum_add(0, &icmp_targ, sizeof(icmp_targ)); |
| 194 | temp_icmp_checksum = ip_checksum_add(temp_icmp_checksum, (void *)payload, payload_size); |
| 195 | icmp_targ.checksum = ip_checksum_finish(temp_icmp_checksum); |
| 196 | |
| 197 | io_targ[0].iov_base = &tun_header; |
| 198 | io_targ[0].iov_len = sizeof(tun_header); |
| 199 | io_targ[1].iov_base = &ip_targ; |
| 200 | io_targ[1].iov_len = sizeof(ip_targ); |
| 201 | io_targ[2].iov_base = &icmp_targ; |
| 202 | io_targ[2].iov_len = sizeof(icmp_targ); |
| 203 | io_targ[3].iov_base = (char *)payload; |
| 204 | io_targ[3].iov_len = payload_size; |
| 205 | |
| 206 | writev(fd, io_targ, 4); |
| 207 | } |
| 208 | |
| 209 | /* function: udp_translate |
| 210 | * common between ipv4/ipv6 - setup checksum and send udp packet |
| 211 | * fd - tun interface fd |
| 212 | * udp - source packet udp header |
| 213 | * payload - udp payload |
| 214 | * payload_size - size of payload |
| 215 | * io_targ - iovec with tun and ipv4/ipv6 header (see below) |
| 216 | * array position 0 - tun header |
| 217 | * array position 1 - ipv4/ipv6 header |
| 218 | * array position 2 - empty (will be udp header) |
| 219 | * array position 3 - empty (will be payload) |
| 220 | * checksum - partial checksum covering ipv4/ipv6 header |
| 221 | */ |
| 222 | void udp_translate(int fd, const struct udphdr *udp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum) { |
| 223 | struct udphdr udp_targ; |
| 224 | |
| 225 | memcpy(&udp_targ, udp, sizeof(udp_targ)); |
| 226 | udp_targ.check = 0; // reset checksum, to be calculated |
| 227 | |
| 228 | checksum = ip_checksum_add(checksum, &udp_targ, sizeof(struct udphdr)); |
| 229 | checksum = ip_checksum_add(checksum, payload, payload_size); |
| 230 | udp_targ.check = ip_checksum_finish(checksum); |
| 231 | |
| 232 | io_targ[2].iov_base = &udp_targ; |
| 233 | io_targ[2].iov_len = sizeof(udp_targ); |
| 234 | io_targ[3].iov_base = (char *)payload; |
| 235 | io_targ[3].iov_len = payload_size; |
| 236 | |
| 237 | writev(fd, io_targ, 4); |
| 238 | } |
| 239 | |
| 240 | /* function: udp_to_udp6 |
| 241 | * translate ipv4 udp to ipv6 udp |
| 242 | * fd - tun interface fd |
| 243 | * ip - source packet ipv4 header |
| 244 | * udp - source packet udp header |
| 245 | * payload - udp payload |
| 246 | * payload_size - size of payload |
| 247 | */ |
| 248 | void udp_to_udp6(int fd, const struct iphdr *ip, const struct udphdr *udp, const char *payload, size_t payload_size) { |
| 249 | struct ip6_hdr ip6_targ; |
| 250 | struct iovec io_targ[4]; |
| 251 | struct tun_pi tun_header; |
| 252 | uint32_t checksum; |
| 253 | |
| 254 | fill_tun_header(&tun_header,ETH_P_IPV6); |
| 255 | |
| 256 | fill_ip6_header(&ip6_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip); |
| 257 | |
| 258 | checksum = ipv6_pseudo_header_checksum(0, &ip6_targ); |
| 259 | |
| 260 | io_targ[0].iov_base = &tun_header; |
| 261 | io_targ[0].iov_len = sizeof(tun_header); |
| 262 | io_targ[1].iov_base = &ip6_targ; |
| 263 | io_targ[1].iov_len = sizeof(ip6_targ); |
| 264 | |
| 265 | udp_translate(fd,udp,payload,payload_size,io_targ,checksum); |
| 266 | } |
| 267 | |
| 268 | /* function: udp6_to_udp |
| 269 | * translate ipv6 udp to ipv4 udp |
| 270 | * fd - tun interface fd |
| 271 | * ip6 - source packet ipv6 header |
| 272 | * udp - source packet udp header |
| 273 | * payload - udp payload |
| 274 | * payload_size - size of payload |
| 275 | */ |
| 276 | void udp6_to_udp(int fd, const struct ip6_hdr *ip6, const struct udphdr *udp, const char *payload, size_t payload_size) { |
| 277 | struct iphdr ip_targ; |
| 278 | struct iovec io_targ[4]; |
| 279 | struct tun_pi tun_header; |
| 280 | uint32_t checksum; |
| 281 | |
| 282 | fill_tun_header(&tun_header,ETH_P_IP); |
| 283 | |
| 284 | fill_ip_header(&ip_targ,payload_size + sizeof(struct udphdr),IPPROTO_UDP,ip6); |
| 285 | |
| 286 | checksum = ipv4_pseudo_header_checksum(0, &ip_targ); |
| 287 | |
| 288 | io_targ[0].iov_base = &tun_header; |
| 289 | io_targ[0].iov_len = sizeof(tun_header); |
| 290 | io_targ[1].iov_base = &ip_targ; |
| 291 | io_targ[1].iov_len = sizeof(ip_targ); |
| 292 | |
| 293 | udp_translate(fd,udp,payload,payload_size,io_targ,checksum); |
| 294 | } |
| 295 | |
| 296 | /* function: tcp_translate |
| 297 | * common between ipv4/ipv6 - setup checksum and send tcp packet |
| 298 | * fd - tun interface fd |
| 299 | * tcp - source packet tcp header |
| 300 | * payload - tcp payload |
| 301 | * payload_size - size of payload |
| 302 | * io_targ - iovec with tun and ipv4/ipv6 header (see below) |
| 303 | * array position 0 - tun header |
| 304 | * array position 1 - ipv4/ipv6 header |
| 305 | * array position 2 - empty (will be tcp header) |
| 306 | * array position 3 - empty (will be tcp options or payload) |
| 307 | * array position 4 - empty (can be payload) |
| 308 | * checksum - partial checksum covering ipv4/ipv6 header |
| 309 | * options - pointer to tcp option buffer |
| 310 | * options_size - size of tcp option buffer |
| 311 | * |
| 312 | * TODO: mss rewrite |
| 313 | * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) |
| 314 | */ |
| 315 | void tcp_translate(int fd, const struct tcphdr *tcp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum, const char *options, size_t options_size) { |
| 316 | struct tcphdr tcp_targ; |
| 317 | int targ_index = 2; |
| 318 | |
| 319 | memcpy(&tcp_targ, tcp, sizeof(tcp_targ)); |
| 320 | tcp_targ.check = 0; |
| 321 | |
| 322 | checksum = ip_checksum_add(checksum, &tcp_targ, sizeof(tcp_targ)); |
| 323 | if(options) { |
| 324 | checksum = ip_checksum_add(checksum, options, options_size); |
| 325 | } |
| 326 | checksum = ip_checksum_add(checksum, payload, payload_size); |
| 327 | tcp_targ.check = ip_checksum_finish(checksum); |
| 328 | |
| 329 | io_targ[targ_index].iov_base = &tcp_targ; |
| 330 | io_targ[targ_index].iov_len = sizeof(tcp_targ); |
| 331 | targ_index++; |
| 332 | |
| 333 | if(options) { |
| 334 | io_targ[targ_index].iov_base = (char *)options; |
| 335 | io_targ[targ_index].iov_len = options_size; |
| 336 | targ_index++; |
| 337 | } |
| 338 | |
| 339 | io_targ[targ_index].iov_base = (char *)payload; |
| 340 | io_targ[targ_index].iov_len = payload_size; |
| 341 | targ_index++; |
| 342 | |
| 343 | writev(fd, io_targ, targ_index); |
| 344 | } |
| 345 | |
| 346 | /* function: tcp_to_tcp6 |
| 347 | * translate ipv4 tcp to ipv6 tcp |
| 348 | * fd - tun interface fd |
| 349 | * ip - source packet ipv4 header |
| 350 | * tcp - source packet tcp header |
| 351 | * payload - tcp payload |
| 352 | * payload_size - size of payload |
| 353 | * options - tcp options |
| 354 | * options_size - size of options |
| 355 | */ |
| 356 | void tcp_to_tcp6(int fd,const struct iphdr *ip, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size) { |
| 357 | struct ip6_hdr ip6_targ; |
| 358 | struct iovec io_targ[5]; |
| 359 | struct tun_pi tun_header; |
| 360 | uint32_t checksum; |
| 361 | |
| 362 | fill_tun_header(&tun_header,ETH_P_IPV6); |
| 363 | |
| 364 | fill_ip6_header(&ip6_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip); |
| 365 | |
| 366 | checksum = ipv6_pseudo_header_checksum(0, &ip6_targ); |
| 367 | |
| 368 | io_targ[0].iov_base = &tun_header; |
| 369 | io_targ[0].iov_len = sizeof(tun_header); |
| 370 | io_targ[1].iov_base = &ip6_targ; |
| 371 | io_targ[1].iov_len = sizeof(ip6_targ); |
| 372 | |
| 373 | tcp_translate(fd,tcp,payload,payload_size,io_targ,checksum,options,options_size); |
| 374 | } |
| 375 | |
| 376 | /* function: tcp6_to_tcp |
| 377 | * translate ipv6 tcp to ipv4 tcp |
| 378 | * fd - tun interface fd |
| 379 | * ip6 - source packet ipv6 header |
| 380 | * tcp - source packet tcp header |
| 381 | * payload - tcp payload |
| 382 | * payload_size - size of payload |
| 383 | * options - tcp options |
| 384 | * options_size - size of options |
| 385 | */ |
| 386 | void tcp6_to_tcp(int fd,const struct ip6_hdr *ip6, const struct tcphdr *tcp, const char *payload, size_t payload_size, const char *options, size_t options_size) { |
| 387 | struct iphdr ip_targ; |
| 388 | struct iovec io_targ[5]; |
| 389 | struct tun_pi tun_header; |
| 390 | uint32_t checksum; |
| 391 | |
| 392 | fill_tun_header(&tun_header,ETH_P_IP); |
| 393 | |
| 394 | fill_ip_header(&ip_targ,payload_size+options_size+sizeof(struct tcphdr),IPPROTO_TCP,ip6); |
| 395 | |
| 396 | checksum = ipv4_pseudo_header_checksum(0, &ip_targ); |
| 397 | |
| 398 | io_targ[0].iov_base = &tun_header; |
| 399 | io_targ[0].iov_len = sizeof(tun_header); |
| 400 | io_targ[1].iov_base = &ip_targ; |
| 401 | io_targ[1].iov_len = sizeof(ip_targ); |
| 402 | |
| 403 | tcp_translate(fd,tcp,payload,payload_size,io_targ,checksum,options,options_size); |
| 404 | } |