/* function: tcp_translate * common between ipv4/ipv6 - setup checksum and send tcp packet * fd - tun interface fd * tcp - source packet tcp header * payload - tcp payload * payload_size - size of payload * io_targ - iovec with tun and ipv4/ipv6 header (see below) * array position 0 - tun header * array position 1 - ipv4/ipv6 header * array position 2 - empty (will be tcp header) * array position 3 - empty (will be tcp options or payload) * array position 4 - empty (can be payload) * checksum - partial checksum covering ipv4/ipv6 header * options - pointer to tcp option buffer * options_size - size of tcp option buffer * * TODO: mss rewrite * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented) */ 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) { struct tcphdr tcp_targ; int targ_index = 2; memcpy(&tcp_targ, tcp, sizeof(tcp_targ)); tcp_targ.check = 0; checksum = ip_checksum_add(checksum, &tcp_targ, sizeof(tcp_targ)); checksum = ip_checksum_add(checksum, payload, payload_size); if(options) { checksum = ip_checksum_add(checksum, options, options_size); } tcp_targ.check = ip_checksum_finish(checksum); io_targ[targ_index].iov_base = &tcp_targ; io_targ[targ_index].iov_len = sizeof(tcp_targ); targ_index++; if(options) { io_targ[targ_index].iov_base = (char *)options; io_targ[targ_index].iov_len = options_size; targ_index++; } io_targ[targ_index].iov_base = (char *)payload; io_targ[targ_index].iov_len = payload_size; targ_index++; writev(fd, io_targ, targ_index); }
/* print udp header */ void dump_udp_generic(const struct udphdr *udp, uint32_t temp_checksum, const char *payload, size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, udp, sizeof(struct udphdr)); temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size); my_checksum = ip_checksum_finish(temp_checksum); printf("UDP\n"); printf("source = %x\n",ntohs(udp->source)); printf("dest = %x\n",ntohs(udp->dest)); printf("len = %x\n",ntohs(udp->len)); printf("check = %x (mine %x)\n",udp->check,my_checksum); }
/* function: udp_translate * common between ipv4/ipv6 - setup checksum and send udp packet * fd - tun interface fd * udp - source packet udp header * payload - udp payload * payload_size - size of payload * io_targ - iovec with tun and ipv4/ipv6 header (see below) * array position 0 - tun header * array position 1 - ipv4/ipv6 header * array position 2 - empty (will be udp header) * array position 3 - empty (will be payload) * checksum - partial checksum covering ipv4/ipv6 header */ void udp_translate(int fd, const struct udphdr *udp, const char *payload, size_t payload_size, struct iovec *io_targ, uint32_t checksum) { struct udphdr udp_targ; memcpy(&udp_targ, udp, sizeof(udp_targ)); udp_targ.check = 0; // reset checksum, to be calculated checksum = ip_checksum_add(checksum, &udp_targ, sizeof(struct udphdr)); checksum = ip_checksum_add(checksum, payload, payload_size); udp_targ.check = ip_checksum_finish(checksum); io_targ[2].iov_base = &udp_targ; io_targ[2].iov_len = sizeof(udp_targ); io_targ[3].iov_base = (char *)payload; io_targ[3].iov_len = payload_size; writev(fd, io_targ, 4); }
/* function: packet_checksum * calculates the checksum over all the packet components starting from pos * checksum - checksum of packet components before pos * packet - packet to calculate the checksum of * pos - position to start counting from * returns - the completed 16-bit checksum, ready to write into a checksum header field */ uint16_t packet_checksum(uint32_t checksum, clat_packet packet, clat_packet_index pos) { int i; for (i = pos; i < CLAT_POS_MAX; i++) { if (packet[i].iov_len > 0) { checksum = ip_checksum_add(checksum, packet[i].iov_base, packet[i].iov_len); } } return ip_checksum_finish(checksum); }
/* function: icmp_to_icmp6 * translate ipv4 icmp to ipv6 icmp * fd - tun interface fd * ip - source packet ipv4 header * icmp - source packet icmp header * payload - icmp payload * payload_size - size of payload */ void icmp_to_icmp6(int fd, const struct iphdr *ip, const struct icmphdr *icmp, const char *payload, size_t payload_size) { struct ip6_hdr ip6_targ; struct icmp6_hdr icmp6_targ; struct iovec io_targ[4]; struct tun_pi tun_header; uint32_t checksum_temp; if(icmp->type != ICMP_ECHO && icmp->type != ICMP_ECHOREPLY) { #if CLAT_DEBUG logmsg(ANDROID_LOG_WARN,"icmp_to_icmp6/unhandled icmp type: 0x%x",icmp->type); #endif return; } fill_tun_header(&tun_header,ETH_P_IPV6); fill_ip6_header(&ip6_targ,payload_size + sizeof(icmp6_targ),IPPROTO_ICMPV6,ip); memset(&icmp6_targ, 0, sizeof(icmp6_targ)); icmp6_targ.icmp6_type = (icmp->type == ICMP_ECHO) ? ICMP6_ECHO_REQUEST : ICMP6_ECHO_REPLY; icmp6_targ.icmp6_code = 0; icmp6_targ.icmp6_cksum = 0; icmp6_targ.icmp6_id = icmp->un.echo.id; icmp6_targ.icmp6_seq = icmp->un.echo.sequence; checksum_temp = ipv6_pseudo_header_checksum(0,&ip6_targ); checksum_temp = ip_checksum_add(checksum_temp,&icmp6_targ,sizeof(icmp6_targ)); checksum_temp = ip_checksum_add(checksum_temp,payload,payload_size); icmp6_targ.icmp6_cksum = ip_checksum_finish(checksum_temp); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); io_targ[1].iov_base = &ip6_targ; io_targ[1].iov_len = sizeof(ip6_targ); io_targ[2].iov_base = &icmp6_targ; io_targ[2].iov_len = sizeof(icmp6_targ); io_targ[3].iov_base = (char *)payload; io_targ[3].iov_len = payload_size; writev(fd, io_targ, 4); }
/* function: icmp6_to_icmp * translate ipv6 icmp to ipv4 icmp * fd - tun interface fd * ip6 - source packet ipv6 header * icmp6 - source packet icmp6 header * payload - icmp6 payload * payload_size - size of payload */ void icmp6_to_icmp(int fd, const struct ip6_hdr *ip6, const struct icmp6_hdr *icmp6, const char *payload, size_t payload_size) { struct iphdr ip_targ; struct icmphdr icmp_targ; struct iovec io_targ[4]; struct tun_pi tun_header; uint32_t temp_icmp_checksum; if((icmp6->icmp6_type != ICMP6_ECHO_REQUEST) && (icmp6->icmp6_type != ICMP6_ECHO_REPLY)) { #if CLAT_DEBUG logmsg(ANDROID_LOG_WARN,"icmp6_to_icmp/unhandled icmp6 type: 0x%x",icmp6->icmp6_type); #endif return; } memset(&icmp_targ, 0, sizeof(icmp_targ)); fill_tun_header(&tun_header,ETH_P_IP); fill_ip_header(&ip_targ,sizeof(icmp_targ) + payload_size, IPPROTO_ICMP, ip6); icmp_targ.type = (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) ? ICMP_ECHO : ICMP_ECHOREPLY; icmp_targ.code = 0x0; icmp_targ.checksum = 0; icmp_targ.un.echo.id = icmp6->icmp6_id; icmp_targ.un.echo.sequence = icmp6->icmp6_seq; temp_icmp_checksum = ip_checksum_add(0,(void *)&icmp_targ,sizeof(icmp_targ)); temp_icmp_checksum = ip_checksum_add(temp_icmp_checksum, (void *)payload, payload_size); icmp_targ.checksum = ip_checksum_finish(temp_icmp_checksum); io_targ[0].iov_base = &tun_header; io_targ[0].iov_len = sizeof(tun_header); io_targ[1].iov_base = &ip_targ; io_targ[1].iov_len = sizeof(ip_targ); io_targ[2].iov_base = &icmp_targ; io_targ[2].iov_len = sizeof(icmp_targ); io_targ[3].iov_base = (char *)payload; io_targ[3].iov_len = payload_size; writev(fd, io_targ, 4); }
/* print tcp header */ void dump_tcp_generic(const struct tcphdr *tcp, const uint8_t *options, size_t options_size, uint32_t temp_checksum, const uint8_t *payload, size_t payload_size) { uint16_t my_checksum; temp_checksum = ip_checksum_add(temp_checksum, tcp, sizeof(struct tcphdr)); if(options) { temp_checksum = ip_checksum_add(temp_checksum, options, options_size); } temp_checksum = ip_checksum_add(temp_checksum, payload, payload_size); my_checksum = ip_checksum_finish(temp_checksum); printf("TCP\n"); printf("source = %x\n",ntohs(tcp->source)); printf("dest = %x\n",ntohs(tcp->dest)); printf("seq = %x\n",ntohl(tcp->seq)); printf("ack = %x\n",ntohl(tcp->ack_seq)); printf("d_off = %x\n",tcp->doff); printf("res1 = %x\n",tcp->res1); #ifdef __BIONIC__ printf("CWR = %x\n",tcp->cwr); printf("ECE = %x\n",tcp->ece); #else printf("CWR/ECE = %x\n",tcp->res2); #endif printf("urg = %x ack = %x psh = %x rst = %x syn = %x fin = %x\n", tcp->urg, tcp->ack, tcp->psh, tcp->rst, tcp->syn, tcp->fin); printf("window = %x\n",ntohs(tcp->window)); printf("check = %x [mine %x]\n",tcp->check,my_checksum); printf("urgent = %x\n",tcp->urg_ptr); if(options) { size_t i; printf("options: "); for(i=0; i<options_size; i++) { printf("%x ",*(options+i)); } printf("\n"); } }