├── .gitignore ├── 4to6.c ├── 4to6.h ├── 6to4.c ├── 6to4.h ├── Makefile ├── README.md ├── common.c ├── common.h ├── config.mk.example ├── demo.png ├── main.c ├── test.py ├── test2.py ├── utils.c └── utils.h /.gitignore: -------------------------------------------------------------------------------- 1 | config.mk 2 | tun464 3 | *.o 4 | -------------------------------------------------------------------------------- /4to6.c: -------------------------------------------------------------------------------- 1 | #include "4to6.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | 11 | ssize_t v4_to_v6(ipv6_header_t *v6pkt, size_t v6len, const ipv4_header_t *v4pkt, size_t v4len, 12 | size_t copy_len, int reverse) 13 | { 14 | if (v4len < sizeof(ipv4_header_t)) return -1; 15 | if (v6len < sizeof(ipv6_header_t)) return -2; 16 | size_t v4header_len = 4 * (v4pkt->version_header_len & 0xf); 17 | uint16_t payload_len = ntohs(v4pkt->total_len) - v4header_len; 18 | if (payload_len < copy_len) copy_len = payload_len; 19 | if (v4len < v4header_len + copy_len) return -3; 20 | if (v6len < sizeof(ipv6_header_t) + copy_len) return -4; 21 | 22 | const void *v4payload = (const uint8_t *)v4pkt + v4header_len; 23 | size_t v4payload_len = v4len - v4header_len; 24 | void *v6payload = v6pkt + 1; 25 | size_t v6payload_len = v6len - sizeof(ipv6_header_t); 26 | 27 | // Check fragmentation. 28 | size_t fragment_offset = ntohs(v4pkt->flags) & 0x1fff; 29 | uint32_t fragment_mf = ntohs(v4pkt->flags) & IPV4_MF_BIT; 30 | size_t fragment_header_len = 0; 31 | int is_fragment = fragment_mf || (fragment_offset != 0); 32 | if (is_fragment) 33 | { 34 | if (v6len < sizeof(ipv6_header_t) + sizeof(ipv6_fragment_header_t) + copy_len) return -400; 35 | fragment_header_len = sizeof(ipv6_fragment_header_t); 36 | } 37 | 38 | // Translate the header. 39 | v6pkt->version_flow = htonl(0x60000000); 40 | v6pkt->payload_len = htons(fragment_header_len + payload_len); 41 | v6pkt->next_header = is_fragment ? IPV6_NEXT_HEADER_FRAGMENT : v4pkt->next_header; 42 | v6pkt->hop_limit = v4pkt->hop_limit; 43 | 44 | // Translate addresses. 45 | const uint8_t *src_prefix = local_prefix, *dst_prefix = remote_prefix; 46 | if (reverse) 47 | { 48 | src_prefix = remote_prefix; 49 | dst_prefix = local_prefix; 50 | } 51 | memcpy(v6pkt->src_bytes, src_prefix, PREFIX_BYTES); 52 | memcpy(v6pkt->src_bytes + PREFIX_BYTES, v4pkt->src_bytes, HOST_BYTES); 53 | memcpy(v6pkt->dst_bytes, dst_prefix, PREFIX_BYTES); 54 | memcpy(v6pkt->dst_bytes + PREFIX_BYTES, v4pkt->dst_bytes, HOST_BYTES); 55 | 56 | // Handle fragmentation. 57 | if (is_fragment) 58 | { 59 | ipv6_fragment_header_t *frag_header = (ipv6_fragment_header_t *)v6payload; 60 | v6payload = frag_header + 1; 61 | v6payload_len -= sizeof(ipv6_fragment_header_t); 62 | frag_header->next_header = v4pkt->next_header; 63 | frag_header->resevrved1 = 0; 64 | frag_header->offset_mf = htons((fragment_offset << 3) | (fragment_mf >> 13)); 65 | frag_header->id = htonl(ntohs(v4pkt->id)); 66 | } 67 | 68 | // Translate upper-layer protocol data. 69 | if (fragment_offset == 0 && 70 | copy_len >= sizeof(icmpv6_header_t) && v4pkt->next_header == IPV4_NEXT_HEADER_ICMP) 71 | { 72 | // We are going to copy all of the payload, but this is an ICMP packet, 73 | // so we should translate it. 74 | #ifdef VERBOSE 75 | printf(" This is an ICMP packet.\n"); 76 | #endif 77 | int only_header = copy_len != payload_len || copy_len == sizeof(icmpv6_header_t); 78 | int ret = v4_to_v6_icmp((icmpv6_header_t *)v6payload, v6payload_len, 79 | (const icmpv4_header_t *)v4payload, v4payload_len, 80 | v6pkt->src_bytes, v6pkt->dst_bytes, 81 | payload_len, only_header); 82 | if (ret <= 0) return ret; 83 | copy_len = ret; 84 | if (!only_header) 85 | { 86 | payload_len = ret; 87 | v6pkt->payload_len = htons(payload_len); 88 | } 89 | v6pkt->next_header = IPV6_NEXT_HEADER_ICMP; 90 | } 91 | #ifdef TRANSLATE_UDP 92 | else if (fragment_offset == 0 && 93 | copy_len >= sizeof(udp_header_t) && v4pkt->next_header == IP_NEXT_HEADER_UDP) 94 | { 95 | // Copy the payload. 96 | memcpy(v6payload, v4payload, copy_len); 97 | // Adjust the UDP header. 98 | v4_to_v6_udp_header((udp_header_t *)v6payload, (const udp_header_t *)v4payload, 99 | v4pkt->src_bytes, v4pkt->dst_bytes, 100 | v6pkt->src_bytes, v6pkt->dst_bytes); 101 | } 102 | #endif 103 | #ifdef TRANSLATE_TCP 104 | else if (fragment_offset == 0 && 105 | copy_len >= sizeof(tcp_header_t) && v4pkt->next_header == IP_NEXT_HEADER_TCP) 106 | { 107 | // Copy the payload. 108 | memcpy(v6payload, v4payload, copy_len); 109 | // Adjust the TCP header. 110 | v4_to_v6_tcp_header((tcp_header_t *)v6payload, (const tcp_header_t *)v4payload, 111 | v4pkt->src_bytes, v4pkt->dst_bytes, 112 | v6pkt->src_bytes, v6pkt->dst_bytes); 113 | } 114 | #endif 115 | else 116 | { 117 | // Just copy the payload. 118 | memcpy(v6payload, v4payload, copy_len); 119 | } 120 | 121 | return sizeof(ipv6_header_t) + fragment_header_len + copy_len; 122 | } 123 | 124 | static inline int v4_to_v6_icmp_type_code(uint16_t type_code) 125 | { 126 | #define DO_TYPE_CODE(name) \ 127 | case ICMPV4_TYPE_CODE_##name: \ 128 | return ICMPV6_TYPE_CODE_##name; 129 | switch (type_code) 130 | { 131 | DO_TYPE_CODE(TTL) 132 | DO_TYPE_CODE(DEFRAG) 133 | DO_TYPE_CODE(NETWORK_UNREACHABLE) 134 | DO_TYPE_CODE(ADDRESS_UNREACHABLE) 135 | DO_TYPE_CODE(PORT_UNREACHABLE) 136 | default: 137 | return -1; 138 | } 139 | #undef DO_TYPE_CODE 140 | } 141 | 142 | static inline ssize_t v4_to_v6_icmp_ip_payload(icmpv6_header_t *v6pkt, size_t v6len, 143 | const icmpv4_header_t *v4pkt, size_t v4len, 144 | const uint8_t *src_bytes, const uint8_t *dst_bytes) 145 | { 146 | int ret; 147 | if ((ret = v4_to_v6((ipv6_header_t *)(v6pkt + 1), v6len - sizeof(icmpv6_header_t), 148 | (ipv4_header_t *)(v4pkt + 1), v4len - sizeof(icmpv4_header_t), 8, 1)) <= 0) 149 | { 150 | return ret; 151 | } 152 | size_t len = sizeof(icmpv6_header_t) + ret; 153 | uint32_t checksum = ip_checksum_partial(src_bytes, 16) + ip_checksum_partial(dst_bytes, 16); 154 | checksum += (len >> 16) + (len & 0xffff); 155 | checksum += IPV6_NEXT_HEADER_ICMP; 156 | checksum += ip_checksum_partial(v6pkt, len); 157 | v6pkt->checksum = ip_checksum_final(checksum); 158 | return len; 159 | } 160 | 161 | static inline ssize_t v4_to_v6_icmp_data_payload(icmpv6_header_t *v6pkt, size_t v6len, 162 | const icmpv4_header_t *v4pkt, size_t v4len, 163 | const uint8_t *src_bytes, const uint8_t *dst_bytes, 164 | uint16_t payload_len, int only_header) 165 | { 166 | if (!only_header) 167 | { 168 | if (v6len < sizeof(icmpv6_header_t) + v4len - sizeof(icmpv4_header_t)) return -17; 169 | memcpy(v6pkt + 1, v4pkt + 1, v4len - sizeof(icmpv4_header_t)); 170 | } 171 | // Incrementally update the checksum. 172 | uint32_t checksum = ntohs(~v4pkt->checksum); 173 | // 0 -> src, dst 174 | checksum += ip_checksum_partial(src_bytes, 16); 175 | checksum += ip_checksum_partial(dst_bytes, 16); 176 | // type, code changed 177 | checksum += ~(((uint16_t)v4pkt->type << 8) | v4pkt->code) & 0xffff; 178 | checksum += (((uint16_t)v6pkt->type << 8) | v6pkt->code); 179 | // 0 -> payload length 180 | checksum += payload_len; 181 | // 0 -> next header 182 | checksum += IPV6_NEXT_HEADER_ICMP; 183 | v6pkt->checksum = ip_checksum_final(checksum); 184 | if (!only_header) 185 | { 186 | return sizeof(icmpv6_header_t) + v4len - sizeof(icmpv4_header_t); 187 | } 188 | else 189 | { 190 | return sizeof(icmpv6_header_t); 191 | } 192 | } 193 | 194 | ssize_t v4_to_v6_icmp(icmpv6_header_t *v6pkt, size_t v6len, 195 | const icmpv4_header_t *v4pkt, size_t v4len, 196 | const uint8_t *src_bytes, const uint8_t *dst_bytes, 197 | uint16_t payload_len, int only_header) 198 | { 199 | if (v4len < sizeof(icmpv4_header_t)) return -14; 200 | if (v6len < sizeof(icmpv6_header_t)) return -15; 201 | int new_type_code = v4_to_v6_icmp_type_code(v4pkt->type_code); 202 | if (new_type_code >= 0) 203 | { 204 | #ifdef VERBOSE 205 | printf(" This is an ICMP packet (generic error type %d code %d).\n", 206 | v4pkt->type, v4pkt->code); 207 | #endif 208 | v6pkt->type_code = new_type_code & 0xffff; 209 | v6pkt->checksum = 0; 210 | v6pkt->rest = 0; 211 | return v4_to_v6_icmp_ip_payload(v6pkt, v6len, v4pkt, v4len, src_bytes, dst_bytes); 212 | } 213 | if (v4pkt->type_code == ICMPV4_TYPE_CODE_TOO_BIG) 214 | { 215 | #ifdef VERBOSE 216 | printf(" This is an ICMP packet (too big).\n"); 217 | #endif 218 | v6pkt->type_code = ICMPV6_TYPE_CODE_TOO_BIG; 219 | v6pkt->checksum = 0; 220 | v6pkt->ununsed1 = 0; 221 | // Adjust MTU overhead. 222 | v6pkt->mtu = htons(ntohs(v4pkt->mtu) - sizeof(ipv4_header_t) + sizeof(ipv6_header_t)); 223 | return v4_to_v6_icmp_ip_payload(v6pkt, v6len, v4pkt, v4len, src_bytes, dst_bytes); 224 | } 225 | if (v4pkt->type_code == ICMPV4_TYPE_CODE_ECHO_REQUEST) 226 | { 227 | #ifdef VERBOSE 228 | printf(" This is an ICMP packet (echo request).\n"); 229 | #endif 230 | v6pkt->type_code = ICMPV6_TYPE_CODE_ECHO_REQUEST; 231 | v6pkt->checksum = 0; 232 | v6pkt->id = v4pkt->id; 233 | v6pkt->seq = v4pkt->seq; 234 | return v4_to_v6_icmp_data_payload(v6pkt, v6len, v4pkt, v4len, src_bytes, dst_bytes, 235 | payload_len, only_header); 236 | } 237 | if (v4pkt->type_code == ICMPV4_TYPE_CODE_ECHO_REPLY) 238 | { 239 | #ifdef VERBOSE 240 | printf(" This is an ICMP packet (echo reply).\n"); 241 | #endif 242 | v6pkt->type_code = ICMPV6_TYPE_CODE_ECHO_REPLY; 243 | v6pkt->checksum = 0; 244 | v6pkt->id = v4pkt->id; 245 | v6pkt->seq = v4pkt->seq; 246 | return v4_to_v6_icmp_data_payload(v6pkt, v6len, v4pkt, v4len, src_bytes, dst_bytes, 247 | payload_len, only_header); 248 | } 249 | #if defined(VERBOSE) || defined(LOG_ERROR) 250 | printf("Unknown ICMP type %d code %d.\n", v4pkt->type, v4pkt->code); 251 | #endif 252 | return -16; 253 | } 254 | 255 | void v4_to_v6_udp_header(udp_header_t *v6pkt, const udp_header_t *v4pkt, 256 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes, 257 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes) 258 | { 259 | // Incrementally update the checksum. 260 | uint32_t checksum = ntohs(~v4pkt->checksum); 261 | checksum += ip_checksum_neg_partial(v4src_bytes, 4) + ip_checksum_neg_partial(v4dst_bytes, 4); 262 | checksum += ip_checksum_partial(v6src_bytes, 16) + ip_checksum_partial(v6dst_bytes, 16); 263 | v6pkt->checksum = ip_checksum_final(checksum); 264 | } 265 | 266 | void v4_to_v6_tcp_header(tcp_header_t *v6pkt, const tcp_header_t *v4pkt, 267 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes, 268 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes) 269 | { 270 | // Incrementally update the checksum. 271 | uint32_t checksum = ntohs(~v4pkt->checksum); 272 | checksum += ip_checksum_neg_partial(v4src_bytes, 4) + ip_checksum_neg_partial(v4dst_bytes, 4); 273 | checksum += ip_checksum_partial(v6src_bytes, 16) + ip_checksum_partial(v6dst_bytes, 16); 274 | v6pkt->checksum = ip_checksum_final(checksum); 275 | } 276 | -------------------------------------------------------------------------------- /4to6.h: -------------------------------------------------------------------------------- 1 | #ifndef _TUN464_4TO6_H_ 2 | #define _TUN464_4TO6_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.h" 9 | 10 | ssize_t v4_to_v6(ipv6_header_t *v6pkt, size_t v6len, const ipv4_header_t *v4pkt, size_t v4len, 11 | size_t copy_len, int reverse); 12 | 13 | ssize_t v4_to_v6_icmp(icmpv6_header_t *v6pkt, size_t v6len, 14 | const icmpv4_header_t *v4pkt, size_t v4len, 15 | const uint8_t *src_bytes, const uint8_t *dst_bytes, 16 | uint16_t payload_len, int only_header); 17 | 18 | void v4_to_v6_udp_header(udp_header_t *v6pkt, const udp_header_t *v4pkt, 19 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes, 20 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes); 21 | 22 | void v4_to_v6_tcp_header(tcp_header_t *v6pkt, const tcp_header_t *v4pkt, 23 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes, 24 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes); 25 | 26 | #endif // _TUN464_4TO6_H_ 27 | -------------------------------------------------------------------------------- /6to4.c: -------------------------------------------------------------------------------- 1 | #include "6to4.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common.h" 10 | 11 | ssize_t v6_to_v4(ipv4_header_t *v4pkt, size_t v4len, const ipv6_header_t *v6pkt, size_t v6len, 12 | size_t copy_len, int reverse) 13 | { 14 | if (v6len < sizeof(ipv6_header_t)) return -5; 15 | if (v4len < sizeof(ipv4_header_t)) return -6; 16 | uint16_t payload_len = ntohs(v6pkt->payload_len); 17 | int is_fragment = 0; 18 | if (v6pkt->next_header == IPV6_NEXT_HEADER_FRAGMENT) 19 | { 20 | if (v6len < sizeof(ipv6_header_t) + sizeof(ipv6_fragment_header_t)) return -600; 21 | payload_len -= sizeof(ipv6_fragment_header_t); 22 | is_fragment = 1; 23 | } 24 | if (payload_len < copy_len) copy_len = payload_len; 25 | if (is_fragment) 26 | { 27 | if (v6len < sizeof(ipv6_header_t) + sizeof(ipv6_fragment_header_t) + copy_len) return -71; 28 | } 29 | else 30 | { 31 | if (v6len < sizeof(ipv6_header_t) + copy_len) return -72; 32 | } 33 | if (v4len < sizeof(ipv4_header_t) + copy_len) return -8; 34 | const uint8_t *dst_prefix = local_prefix, *src_prefix = remote_prefix; 35 | if (reverse) 36 | { 37 | dst_prefix = remote_prefix; 38 | src_prefix = local_prefix; 39 | } 40 | if (memcmp(v6pkt->dst_bytes, dst_prefix, PREFIX_BYTES) != 0) return -9; 41 | 42 | const void *v6payload = v6pkt + 1; 43 | size_t v6payload_len = v6len - sizeof(ipv6_header_t); 44 | void *v4payload = v4pkt + 1; 45 | size_t v4payload_len = v4len - sizeof(ipv4_header_t); 46 | 47 | // Check fragmentation. 48 | size_t fragment_offset = 0; 49 | uint32_t fragment_mf = 0; 50 | const ipv6_fragment_header_t *frag_header = (const ipv6_fragment_header_t *)v6payload; 51 | if (is_fragment) 52 | { 53 | is_fragment = 1; 54 | fragment_offset = ntohs(frag_header->offset_mf) >> 3; 55 | fragment_mf = ntohs(frag_header->offset_mf) & IPV6_MF_BIT; 56 | v6payload = frag_header + 1; 57 | v6payload_len -= sizeof(ipv6_fragment_header_t); 58 | } 59 | 60 | uint8_t next_header = is_fragment ? frag_header->next_header : v6pkt->next_header; 61 | 62 | // Translate the header and handle fragmentation. 63 | v4pkt->version_header_len = 0x45; 64 | v4pkt->dscp_ecn = 0; 65 | v4pkt->total_len = htons(sizeof(ipv4_header_t) + payload_len); 66 | v4pkt->id = is_fragment ? htons(ntohl(frag_header->id) & 0xffff) : 0; 67 | v4pkt->flags = is_fragment ? htons(fragment_offset | (fragment_mf << 13)) : htons(IPV4_DF_BIT); 68 | v4pkt->hop_limit = v6pkt->hop_limit; 69 | v4pkt->next_header = next_header; 70 | v4pkt->checksum = 0; 71 | 72 | // Translate addresses. 73 | if (memcmp(v6pkt->src_bytes, src_prefix, PREFIX_BYTES) == 0) 74 | { 75 | memcpy(v4pkt->src_bytes, v6pkt->src_bytes + PREFIX_BYTES, HOST_BYTES); 76 | } 77 | else 78 | { 79 | // Use CGN addresses. FIXME: new translation design needed 80 | // TODO: RFC 6791 81 | v4pkt->src_bytes[0] = 100; 82 | v4pkt->src_bytes[1] = 0x40 | (v6pkt->src_bytes[7] & 0x3f); 83 | v4pkt->src_bytes[2] = v6pkt->src_bytes[14]; 84 | v4pkt->src_bytes[3] = v6pkt->src_bytes[15]; 85 | } 86 | memcpy(v4pkt->dst_bytes, v6pkt->dst_bytes + PREFIX_BYTES, HOST_BYTES); 87 | 88 | // Translate upper-layer protocol data. 89 | if (fragment_offset == 0 && 90 | copy_len >= sizeof(icmpv4_header_t) && next_header == IPV6_NEXT_HEADER_ICMP) 91 | { 92 | // We are going to copy all of the payload, but this is an ICMP packet, 93 | // so we should translate it first. 94 | #ifdef VERBOSE 95 | printf(" This is an ICMP packet.\n"); 96 | #endif 97 | int only_header = copy_len != payload_len; 98 | int ret = v6_to_v4_icmp((icmpv4_header_t *)v4payload, v4payload_len, 99 | (const icmpv6_header_t *)v6payload, v6payload_len, 100 | v6pkt->src_bytes, v6pkt->dst_bytes, payload_len, only_header); 101 | if (ret <= 0) return ret; 102 | copy_len = ret; 103 | if (!only_header) 104 | { 105 | payload_len = ret; 106 | v4pkt->total_len = htons(sizeof(ipv4_header_t) + payload_len); 107 | } 108 | v4pkt->next_header = IPV4_NEXT_HEADER_ICMP; 109 | } 110 | #ifdef TRANSLATE_UDP 111 | else if (fragment_offset == 0 && 112 | copy_len >= sizeof(udp_header_t) && next_header == IP_NEXT_HEADER_UDP) 113 | { 114 | // Copy the payload. 115 | memcpy(v4payload, v6payload, copy_len); 116 | // Adjust the UDP header. 117 | v6_to_v4_udp_header((udp_header_t *)v4payload, (const udp_header_t *)v6payload, 118 | v6pkt->src_bytes, v6pkt->dst_bytes, 119 | v4pkt->src_bytes, v4pkt->dst_bytes); 120 | } 121 | #endif 122 | #ifdef TRANSLATE_TCP 123 | else if (fragment_offset == 0 && 124 | copy_len >= sizeof(tcp_header_t) && next_header == IP_NEXT_HEADER_TCP) 125 | { 126 | // Copy the payload. 127 | memcpy(v4payload, v6payload, copy_len); 128 | // Adjust the TCP header. 129 | v6_to_v4_tcp_header((tcp_header_t *)v4payload, (const tcp_header_t *)v6payload, 130 | v6pkt->src_bytes, v6pkt->dst_bytes, 131 | v4pkt->src_bytes, v4pkt->dst_bytes); 132 | } 133 | #endif 134 | else 135 | { 136 | // Just copy the payload. 137 | memcpy(v4payload, v6payload, copy_len); 138 | } 139 | 140 | // Calculate IPv4 header checksum. 141 | v4pkt->checksum = ip_checksum_final(ip_checksum_partial(v4pkt, sizeof(ipv4_header_t))); 142 | return sizeof(ipv4_header_t) + copy_len; 143 | } 144 | 145 | static inline int v6_to_v4_icmp_type_code(uint16_t type_code) 146 | { 147 | #define DO_TYPE_CODE(name) \ 148 | case ICMPV6_TYPE_CODE_##name: \ 149 | return ICMPV4_TYPE_CODE_##name; 150 | switch (type_code) 151 | { 152 | DO_TYPE_CODE(TTL) 153 | DO_TYPE_CODE(DEFRAG) 154 | DO_TYPE_CODE(NETWORK_UNREACHABLE) 155 | DO_TYPE_CODE(ADDRESS_UNREACHABLE) 156 | DO_TYPE_CODE(PORT_UNREACHABLE) 157 | default: 158 | return -1; 159 | } 160 | #undef DO_TYPE_CODE 161 | } 162 | 163 | static inline ssize_t v6_to_v4_icmp_ip_payload(icmpv4_header_t *v4pkt, size_t v4len, 164 | const icmpv6_header_t *v6pkt, size_t v6len) 165 | { 166 | int ret; 167 | if ((ret = v6_to_v4((ipv4_header_t *)(v4pkt + 1), v4len - sizeof(icmpv4_header_t), 168 | (ipv6_header_t *)(v6pkt + 1), v6len - sizeof(icmpv6_header_t), 8, 1)) <= 0) 169 | { 170 | return ret; 171 | } 172 | size_t len = sizeof(icmpv4_header_t) + ret; 173 | v4pkt->checksum = ip_checksum_final(ip_checksum_partial(v4pkt, len)); 174 | return len; 175 | } 176 | 177 | static inline ssize_t v6_to_v4_icmp_data_payload(icmpv4_header_t *v4pkt, size_t v4len, 178 | const icmpv6_header_t *v6pkt, size_t v6len, 179 | const uint8_t *src_bytes, const uint8_t *dst_bytes, 180 | uint16_t payload_len, int only_header) 181 | { 182 | if (!only_header) 183 | { 184 | if (v4len < sizeof(icmpv4_header_t) + v6len - sizeof(icmpv6_header_t)) return -13; 185 | memcpy(v4pkt + 1, v6pkt + 1, v6len - sizeof(icmpv6_header_t)); 186 | } 187 | // Incrementally update the checksum. 188 | uint32_t checksum = ntohs(~v6pkt->checksum); 189 | // src, dst -> 0 190 | checksum += ip_checksum_neg_partial(src_bytes, 16); 191 | checksum += ip_checksum_neg_partial(dst_bytes, 16); 192 | // type, code changed 193 | checksum += ~(((uint16_t)v6pkt->type << 8) | v6pkt->code) & 0xffff; 194 | checksum += (((uint16_t)v4pkt->type << 8) | v4pkt->code); 195 | // payload length -> 0 196 | checksum += ~payload_len & 0xffff; 197 | // next header -> 0 198 | checksum += ~IPV6_NEXT_HEADER_ICMP & 0xffff; 199 | v4pkt->checksum = ip_checksum_final(checksum); 200 | if (!only_header) 201 | { 202 | return sizeof(icmpv4_header_t) + v6len - sizeof(icmpv6_header_t); 203 | } 204 | else 205 | { 206 | return sizeof(icmpv4_header_t); 207 | } 208 | } 209 | 210 | ssize_t v6_to_v4_icmp(icmpv4_header_t *v4pkt, size_t v4len, 211 | const icmpv6_header_t *v6pkt, size_t v6len, 212 | const uint8_t *src_bytes, const uint8_t *dst_bytes, 213 | uint16_t payload_len, int only_header) 214 | { 215 | if (v6len < sizeof(icmpv6_header_t)) return -10; 216 | if (v4len < sizeof(icmpv4_header_t)) return -11; 217 | int new_type_code = v6_to_v4_icmp_type_code(v6pkt->type_code); 218 | if (new_type_code >= 0) 219 | { 220 | #ifdef VERBOSE 221 | printf(" This is an ICMP packet (generic error type %d code %d).\n", 222 | v6pkt->type, v6pkt->code); 223 | #endif 224 | v4pkt->type_code = new_type_code & 0xffff; 225 | v4pkt->checksum = 0; 226 | v4pkt->rest = 0; 227 | return v6_to_v4_icmp_ip_payload(v4pkt, v4len, v6pkt, v6len); 228 | } 229 | if (v6pkt->type_code == ICMPV6_TYPE_CODE_TOO_BIG) 230 | { 231 | #ifdef VERBOSE 232 | printf(" This is an ICMP packet (too big).\n"); 233 | #endif 234 | v4pkt->type_code = ICMPV4_TYPE_CODE_TOO_BIG; 235 | v4pkt->checksum = 0; 236 | v4pkt->ununsed1 = 0; 237 | // Adjust MTU overhead. 238 | v4pkt->mtu = htons(ntohs(v6pkt->mtu) - sizeof(ipv6_header_t) + sizeof(ipv4_header_t)); 239 | return v6_to_v4_icmp_ip_payload(v4pkt, v4len, v6pkt, v6len); 240 | } 241 | if (v6pkt->type_code == ICMPV6_TYPE_CODE_ECHO_REQUEST) 242 | { 243 | #ifdef VERBOSE 244 | printf(" This is an ICMP packet (echo request).\n"); 245 | #endif 246 | v4pkt->type_code = ICMPV4_TYPE_CODE_ECHO_REQUEST; 247 | v4pkt->checksum = 0; 248 | v4pkt->id = v6pkt->id; 249 | v4pkt->seq = v6pkt->seq; 250 | return v6_to_v4_icmp_data_payload(v4pkt, v4len, v6pkt, v6len, src_bytes, dst_bytes, 251 | payload_len, only_header); 252 | } 253 | if (v6pkt->type_code == ICMPV6_TYPE_CODE_ECHO_REPLY) 254 | { 255 | #ifdef VERBOSE 256 | printf(" This is an ICMP packet (echo reply).\n"); 257 | #endif 258 | v4pkt->type_code = ICMPV4_TYPE_CODE_ECHO_REPLY; 259 | v4pkt->checksum = 0; 260 | v4pkt->id = v6pkt->id; 261 | v4pkt->seq = v6pkt->seq; 262 | return v6_to_v4_icmp_data_payload(v4pkt, v4len, v6pkt, v6len, src_bytes, dst_bytes, 263 | payload_len, only_header); 264 | } 265 | #if defined(VERBOSE) || defined(LOG_ERROR) 266 | printf("Unknown ICMP type %d code %d.\n", v6pkt->type, v6pkt->code); 267 | #endif 268 | return -12; 269 | } 270 | 271 | void v6_to_v4_udp_header(udp_header_t *v4pkt, const udp_header_t *v6pkt, 272 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes, 273 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes) 274 | { 275 | // Incrementally update the checksum. 276 | uint32_t checksum = ntohs(~v6pkt->checksum); 277 | checksum += ip_checksum_neg_partial(v6src_bytes, 16) + ip_checksum_neg_partial(v6dst_bytes, 16); 278 | checksum += ip_checksum_partial(v4src_bytes, 4) + ip_checksum_partial(v4dst_bytes, 4); 279 | v4pkt->checksum = ip_checksum_final(checksum); 280 | } 281 | 282 | void v6_to_v4_tcp_header(tcp_header_t *v4pkt, const tcp_header_t *v6pkt, 283 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes, 284 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes) 285 | { 286 | // Incrementally update the checksum. 287 | uint32_t checksum = ntohs(~v6pkt->checksum); 288 | checksum += ip_checksum_neg_partial(v6src_bytes, 16) + ip_checksum_neg_partial(v6dst_bytes, 16); 289 | checksum += ip_checksum_partial(v4src_bytes, 4) + ip_checksum_partial(v4dst_bytes, 4); 290 | v4pkt->checksum = ip_checksum_final(checksum); 291 | } 292 | -------------------------------------------------------------------------------- /6to4.h: -------------------------------------------------------------------------------- 1 | #ifndef _TUN464_6TO4_H_ 2 | #define _TUN464_6TO4_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.h" 9 | 10 | ssize_t v6_to_v4(ipv4_header_t *v4pkt, size_t v4len, const ipv6_header_t *v6pkt, size_t v6len, 11 | size_t copy_len, int reverse); 12 | 13 | ssize_t v6_to_v4_icmp(icmpv4_header_t *v4pkt, size_t v4len, 14 | const icmpv6_header_t *v6pkt, size_t v6len, 15 | const uint8_t *src_bytes, const uint8_t *dst_bytes, 16 | uint16_t payload_len, int only_header); 17 | 18 | void v6_to_v4_udp_header(udp_header_t *v4pkt, const udp_header_t *v6pkt, 19 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes, 20 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes); 21 | 22 | void v6_to_v4_tcp_header(tcp_header_t *v4pkt, const tcp_header_t *v6pkt, 23 | const uint8_t *v6src_bytes, const uint8_t *v6dst_bytes, 24 | const uint8_t *v4src_bytes, const uint8_t *v4dst_bytes); 25 | 26 | #endif // _TUN464_6TO4_H_ 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include config.mk 2 | 3 | .PHONY: all 4 | all: tun464 5 | 6 | tun464: main.o common.o utils.o 4to6.o 6to4.o 7 | gcc -O2 -Wall $^ -o $@ -lpthread 8 | 9 | %.o: %.c common.h utils.h 4to6.h 6to4.h 10 | gcc -O2 -Wall -c $< -o $@ 11 | 12 | .PHONY: run 13 | run: tun464 14 | sudo ./tun464 tun464 $(PREFIX_A) $(PREFIX_B) 15 | 16 | .PHONY: run2 17 | run2: tun464 18 | sudo ./tun464 tun464 $(PREFIX_B) $(PREFIX_A) 19 | 20 | .PHONY: setup 21 | setup: 22 | sudo ip addr add 10.2.2.1/32 dev tun464-ipv4 23 | sudo ip link set tun464-ipv4 up 24 | sudo ip link set tun464-ipv4 mtu $(IPV4_MTU) 25 | sudo ip link set tun464-ipv6 up 26 | sudo ip route add $(PREFIX_A)/96 dev tun464-ipv6 27 | sudo ip route add 10.2.2.2/32 dev tun464-ipv4 28 | 29 | .PHONY: setup2 30 | setup2: 31 | sudo ip addr add 10.2.2.2/32 dev tun464-ipv4 32 | sudo ip link set tun464-ipv4 up 33 | sudo ip link set tun464-ipv4 mtu $(IPV4_MTU) 34 | sudo ip link set tun464-ipv6 up 35 | sudo ip route add $(PREFIX_B)/96 dev tun464-ipv6 36 | sudo ip route add 10.2.2.1/32 dev tun464-ipv4 37 | 38 | .PHONY: clean 39 | clean: 40 | -rm tun464 *.o 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tun464 2 | 3 | This is a lightweight tunnel (or so-called translator) to transit IPv4 packets over an IPv6 network or the IPv6 internet. 4 | 5 | Not like ipip6 or other tunnels, it minimizes the MTU overhead by embedding IPv4 addresses into IPv6 addresses. It can reach 1472 (1500 - 40 - 8 + 20) of MTU, while ipip6 would usually decrease MTU to 1452 (1500 - 40 - 8). 6 | 7 | A /96 IPv6 prefix is needed for each site since an IPv4 address will be embedded into the low 32 bits of an IPv6 address. For example, consider `192.0.2.1` and `192.0.2.2` are communicating through this tunnel, and the configured prefixes are `2001:db8:1:4646::/96` and `2001:db8:2:4646::/96`. For the IPv6 network, it looks like that `2001:db8:1:4646::c000:201` and `2001:db8:1:4646::c000:202` are communicating with each other. 8 | 9 | ## Prerequisites 10 | 11 | * Build essentials: GCC, make, ... 12 | 13 | ## Usage 14 | 15 | On one site: 16 | 17 | ```bash 18 | make run 19 | make setup 20 | ``` 21 | 22 | On the other site: 23 | 24 | ```bash 25 | make run2 26 | make setup2 27 | ``` 28 | 29 | ## Demo 30 | 31 | ![demo](demo.png) 32 | 33 | In the above figure, the hops reported to be in `100.64.0.0/10` are actually IPv6 routers, and their "IPv4 addresses" are not real. 34 | 35 | ## References 36 | 37 | * [RFC 6144 Framework for IPv4/IPv6 Translation](https://tools.ietf.org/html/rfc6144) 38 | * [RFC 6052 IPv6 Addressing of IPv4/IPv6 Translators](https://tools.ietf.org/html/rfc6052) 39 | * [RFC 6877 464XLAT: Combination of Stateful and Stateless Translation](https://tools.ietf.org/html/rfc6877) 40 | * [RFC 7915 IP/ICMP Translation Algorithm](https://tools.ietf.org/html/rfc7915) 41 | * [RFC 6791 Stateless Source Address Mapping for ICMPv6 Packets](https://tools.ietf.org/html/rfc6791) 42 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | 5 | uint8_t local_prefix[16]; 6 | uint8_t remote_prefix[16]; 7 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef _TUN464_COMMON_H_ 2 | #define _TUN464_COMMON_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // #define VERBOSE 9 | #define LOG_ERROR 10 | #define TRANSLATE_UDP 11 | #define TRANSLATE_TCP 12 | 13 | #define UNUSED(x) ((void)(x)) 14 | 15 | #define PAGE_SIZE 0x1000 16 | #define BUFFER_SIZE 0x10000 17 | 18 | #define PREFIX_BYTES 12 19 | #define HOST_BYTES 4 20 | 21 | #define LOCAL_PREFIX "2001:db8:1:4646::" // /96 22 | #define REMOTE_PREFIX "2001:db8:2:4646::" // /96 23 | 24 | #define IPV4_DF_BIT 0x4000 25 | #define IPV4_MF_BIT 0x2000 26 | #define IPV4_NEXT_HEADER_ICMP 1 27 | #define IPV6_MF_BIT 0x0001 28 | #define IPV6_NEXT_HEADER_ICMP 58 29 | #define IPV6_NEXT_HEADER_FRAGMENT 44 30 | #define IP_NEXT_HEADER_UDP 17 31 | #define IP_NEXT_HEADER_TCP 6 32 | 33 | #define ICMPV4_TYPE_TTL 0x0b 34 | #define ICMPV4_CODE_TTL 0x00 35 | #define ICMPV6_TYPE_TTL 0x03 36 | #define ICMPV6_CODE_TTL 0x00 37 | 38 | #define ICMPV4_TYPE_DEFRAG 0x0b 39 | #define ICMPV4_CODE_DEFRAG 0x01 40 | #define ICMPV6_TYPE_DEFRAG 0x03 41 | #define ICMPV6_CODE_DEFRAG 0x01 42 | 43 | #define ICMPV4_TYPE_TOO_BIG 0x03 44 | #define ICMPV4_CODE_TOO_BIG 0x04 45 | #define ICMPV6_TYPE_TOO_BIG 0x02 46 | #define ICMPV6_CODE_TOO_BIG 0x00 47 | 48 | #define ICMPV4_TYPE_ECHO_REQUEST 0x08 49 | #define ICMPV4_CODE_ECHO_REQUEST 0x00 50 | #define ICMPV6_TYPE_ECHO_REQUEST 0x80 51 | #define ICMPV6_CODE_ECHO_REQUEST 0x00 52 | 53 | #define ICMPV4_TYPE_ECHO_REPLY 0x00 54 | #define ICMPV4_CODE_ECHO_REPLY 0x00 55 | #define ICMPV6_TYPE_ECHO_REPLY 0x81 56 | #define ICMPV6_CODE_ECHO_REPLY 0x00 57 | 58 | #define ICMPV4_TYPE_NETWORK_UNREACHABLE 0x03 59 | #define ICMPV4_CODE_NETWORK_UNREACHABLE 0x00 60 | #define ICMPV6_TYPE_NETWORK_UNREACHABLE 0x01 61 | #define ICMPV6_CODE_NETWORK_UNREACHABLE 0x00 62 | 63 | #define ICMPV4_TYPE_ADDRESS_UNREACHABLE 0x03 64 | #define ICMPV4_CODE_ADDRESS_UNREACHABLE 0x01 65 | #define ICMPV6_TYPE_ADDRESS_UNREACHABLE 0x01 66 | #define ICMPV6_CODE_ADDRESS_UNREACHABLE 0x03 67 | 68 | #define ICMPV4_TYPE_PORT_UNREACHABLE 0x03 69 | #define ICMPV4_CODE_PORT_UNREACHABLE 0x03 70 | #define ICMPV6_TYPE_PORT_UNREACHABLE 0x01 71 | #define ICMPV6_CODE_PORT_UNREACHABLE 0x04 72 | 73 | // For little-endian systems. 74 | #define ICMPV4_TYPE_CODE_TTL 0x000b 75 | #define ICMPV6_TYPE_CODE_TTL 0x0003 76 | 77 | #define ICMPV4_TYPE_CODE_DEFRAG 0x010b 78 | #define ICMPV6_TYPE_CODE_DEFRAG 0x0103 79 | 80 | #define ICMPV4_TYPE_CODE_TOO_BIG 0x0403 81 | #define ICMPV6_TYPE_CODE_TOO_BIG 0x0002 82 | 83 | #define ICMPV4_TYPE_CODE_ECHO_REQUEST 0x0008 84 | #define ICMPV6_TYPE_CODE_ECHO_REQUEST 0x0080 85 | 86 | #define ICMPV4_TYPE_CODE_ECHO_REPLY 0x0000 87 | #define ICMPV6_TYPE_CODE_ECHO_REPLY 0x0081 88 | 89 | #define ICMPV4_TYPE_CODE_NETWORK_UNREACHABLE 0x0003 90 | #define ICMPV6_TYPE_CODE_NETWORK_UNREACHABLE 0x0001 91 | 92 | #define ICMPV4_TYPE_CODE_ADDRESS_UNREACHABLE 0x0103 93 | #define ICMPV6_TYPE_CODE_ADDRESS_UNREACHABLE 0x0301 94 | 95 | #define ICMPV4_TYPE_CODE_PORT_UNREACHABLE 0x0303 96 | #define ICMPV6_TYPE_CODE_PORT_UNREACHABLE 0x0401 97 | 98 | typedef struct 99 | { 100 | uint8_t version_header_len; 101 | uint8_t dscp_ecn; 102 | uint16_t total_len; 103 | uint16_t id; 104 | uint16_t flags; 105 | uint8_t hop_limit; 106 | uint8_t next_header; 107 | uint16_t checksum; 108 | union 109 | { 110 | uint32_t src; 111 | uint8_t src_bytes[4]; 112 | }; 113 | union 114 | { 115 | uint32_t dst; 116 | uint8_t dst_bytes[4]; 117 | }; 118 | } __attribute__((packed)) ipv4_header_t; 119 | 120 | typedef struct 121 | { 122 | uint32_t version_flow; 123 | uint16_t payload_len; 124 | uint8_t next_header; 125 | uint8_t hop_limit; 126 | union 127 | { 128 | struct 129 | { 130 | uint64_t src_hi; 131 | uint64_t src_lo; 132 | }; 133 | uint8_t src_bytes[16]; 134 | }; 135 | union 136 | { 137 | struct 138 | { 139 | uint64_t dst_hi; 140 | uint64_t dst_lo; 141 | }; 142 | uint8_t dst_bytes[16]; 143 | }; 144 | } __attribute__((packed)) ipv6_header_t; 145 | 146 | typedef struct 147 | { 148 | union 149 | { 150 | struct 151 | { 152 | uint8_t type; 153 | uint8_t code; 154 | }; 155 | uint16_t type_code; 156 | }; 157 | uint16_t checksum; 158 | union 159 | { 160 | uint8_t rest_bytes[4]; 161 | uint32_t rest; 162 | struct 163 | { 164 | uint16_t ununsed1; 165 | uint16_t mtu; 166 | }; 167 | struct 168 | { 169 | uint16_t id; 170 | uint16_t seq; 171 | }; 172 | }; 173 | } __attribute__((packed)) icmpv4_header_t; 174 | 175 | typedef struct 176 | { 177 | union 178 | { 179 | struct 180 | { 181 | uint8_t type; 182 | uint8_t code; 183 | }; 184 | uint16_t type_code; 185 | }; 186 | uint16_t checksum; 187 | union 188 | { 189 | uint8_t rest_bytes[4]; 190 | uint32_t rest; 191 | struct 192 | { 193 | uint16_t ununsed1; 194 | uint16_t mtu; 195 | }; 196 | struct 197 | { 198 | uint16_t id; 199 | uint16_t seq; 200 | }; 201 | }; 202 | } __attribute__((packed)) icmpv6_header_t; 203 | 204 | typedef struct 205 | { 206 | uint8_t next_header; 207 | uint8_t resevrved1; 208 | uint16_t offset_mf; 209 | uint32_t id; 210 | } __attribute__((packed)) ipv6_fragment_header_t; 211 | 212 | typedef struct 213 | { 214 | uint16_t src_port; 215 | uint16_t dst_port; 216 | uint16_t len; 217 | uint16_t checksum; 218 | } __attribute__((packed)) udp_header_t; 219 | 220 | typedef struct 221 | { 222 | uint32_t header0[4]; 223 | uint16_t checksum; 224 | uint16_t header1; 225 | } __attribute__((packed)) tcp_header_t; 226 | 227 | extern uint8_t local_prefix[16]; 228 | extern uint8_t remote_prefix[16]; 229 | 230 | static inline int get_version(void *pkt) 231 | { 232 | ipv4_header_t *v4pkt = (ipv4_header_t *)pkt; 233 | return v4pkt->version_header_len >> 4; 234 | } 235 | 236 | static inline uint32_t ip_checksum_partial(const void *buff, size_t len) 237 | { 238 | uint32_t checksum = 0; 239 | const uint16_t *buff16 = (const uint16_t *)buff; 240 | for (int i = 0; i < len / sizeof(uint16_t); ++i) 241 | { 242 | checksum += ntohs(buff16[i]); 243 | } 244 | if (len & 1) checksum += ((const uint8_t *)buff)[len - 1]; 245 | return checksum; 246 | } 247 | 248 | static inline uint32_t ip_checksum_neg_partial(const void *buff, size_t len) 249 | { 250 | uint32_t checksum = 0; 251 | const uint16_t *buff16 = (const uint16_t *)buff; 252 | for (int i = 0; i < len / sizeof(uint16_t); ++i) 253 | { 254 | checksum += ntohs(~buff16[i]); 255 | } 256 | if (len & 1) checksum += ~((const uint8_t *)buff)[len - 1] & 0xffff; 257 | return checksum; 258 | } 259 | 260 | static inline uint16_t ip_checksum_final(uint32_t checksum) 261 | { 262 | checksum = (checksum & 0xffff) + (checksum >> 16); 263 | checksum = (checksum & 0xffff) + (checksum >> 16); 264 | return ~htons(checksum & 0xffff); 265 | } 266 | 267 | #endif // _TUN464_COMMON_H_ 268 | -------------------------------------------------------------------------------- /config.mk.example: -------------------------------------------------------------------------------- 1 | PREFIX_A=2001:db8:1:4646:: 2 | PREFIX_B=2001:db8:2:4646:: 3 | IPV4_MTU=1472 # TODO: if it is a PPPoE network, change this to 1464 4 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twd2/tun464/939bd5469adfe43e8443dd0134757a58f377aed3/demo.png -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "common.h" 16 | #include "utils.h" 17 | #include "4to6.h" 18 | #include "6to4.h" 19 | 20 | int tun_alloc(const char *name, int flags) 21 | { 22 | const char *clonedev = "/dev/net/tun"; 23 | 24 | int fd; 25 | if ((fd = open(clonedev, O_RDWR)) < 0) 26 | { 27 | return fd; 28 | } 29 | 30 | struct ifreq ifr; 31 | bzero(&ifr, sizeof(ifr)); 32 | if (name) 33 | { 34 | strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); 35 | ifr.ifr_name[IFNAMSIZ - 1] = 0; 36 | } 37 | ifr.ifr_flags = flags; 38 | 39 | int err; 40 | if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) 41 | { 42 | close(fd); 43 | return err; 44 | } 45 | 46 | printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name); 47 | return fd; 48 | } 49 | 50 | void *guarded_malloc(size_t len) 51 | { 52 | len = (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 53 | uint8_t *ret = mmap(NULL, PAGE_SIZE + len + PAGE_SIZE, PROT_READ, 54 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 55 | if (ret == MAP_FAILED) 56 | { 57 | perror("mmap"); 58 | exit(1); 59 | } 60 | mprotect(ret + PAGE_SIZE, len, PROT_READ | PROT_WRITE); 61 | return ret + PAGE_SIZE; 62 | } 63 | 64 | static int v4tun_fd, v6tun_fd; 65 | 66 | static void *v4entry(void *_) 67 | { 68 | char *buffer = guarded_malloc(BUFFER_SIZE), *new_buffer = guarded_malloc(BUFFER_SIZE); 69 | while (1) 70 | { 71 | ssize_t len = read(v4tun_fd, buffer, BUFFER_SIZE); 72 | if (len <= 0) 73 | { 74 | perror("Reading from interface for IPv4"); 75 | exit(1); 76 | } 77 | 78 | int version = get_version(buffer); 79 | if (version != 4) continue; 80 | #ifdef VERBOSE 81 | print_ipv4_packet((ipv4_header_t *)buffer); 82 | #endif 83 | ssize_t new_len = v4_to_v6((ipv6_header_t *)new_buffer, BUFFER_SIZE, 84 | (ipv4_header_t *)buffer, len, -1, 0); 85 | if (new_len > 0) 86 | { 87 | #ifdef VERBOSE 88 | printf(" translated: "); 89 | print_ipv6_packet((ipv6_header_t *)new_buffer); 90 | #endif 91 | ssize_t ret = write(v6tun_fd, new_buffer, new_len); 92 | if (ret <= 0) 93 | { 94 | perror("write"); 95 | } 96 | } 97 | else 98 | { 99 | #if defined(VERBOSE) || defined(LOG_ERROR) 100 | #ifndef VERBOSE 101 | print_ipv4_packet((ipv4_header_t *)buffer); 102 | #endif 103 | printf(" translation failed: %ld\n", new_len); 104 | print_hex(buffer, len); 105 | printf("\n"); 106 | #endif 107 | } 108 | } 109 | return NULL; 110 | } 111 | 112 | static void *v6entry(void *_) 113 | { 114 | char *buffer = guarded_malloc(BUFFER_SIZE), *new_buffer = guarded_malloc(BUFFER_SIZE); 115 | while (1) 116 | { 117 | ssize_t len = read(v6tun_fd, buffer, BUFFER_SIZE); 118 | if (len <= 0) 119 | { 120 | perror("Reading from interface for IPv6"); 121 | exit(1); 122 | } 123 | 124 | int version = get_version(buffer); 125 | if (version != 6) continue; 126 | #ifdef VERBOSE 127 | print_ipv6_packet((ipv6_header_t *)buffer); 128 | #endif 129 | ssize_t new_len = v6_to_v4((ipv4_header_t *)new_buffer, BUFFER_SIZE, 130 | (ipv6_header_t *)buffer, len, -1, 0); 131 | if (new_len > 0) 132 | { 133 | #ifdef VERBOSE 134 | printf(" translated: "); 135 | print_ipv4_packet((ipv4_header_t *)new_buffer); 136 | #endif 137 | ssize_t ret = write(v4tun_fd, new_buffer, new_len); 138 | if (ret <= 0) 139 | { 140 | perror("write"); 141 | } 142 | } 143 | else 144 | { 145 | #if defined(VERBOSE) || defined(LOG_ERROR) 146 | #ifndef VERBOSE 147 | print_ipv6_packet((ipv6_header_t *)buffer); 148 | #endif 149 | printf(" translation failed: %ld\n", new_len); 150 | print_hex(buffer, len); 151 | printf("\n"); 152 | #endif 153 | } 154 | } 155 | return NULL; 156 | } 157 | 158 | int main(int argc, const char **argv) 159 | { 160 | if (argc < 2) 161 | { 162 | printf("Usage: %s dev_name [local_prefix remote_prefix]\n", argv[0]); 163 | return 1; 164 | } 165 | 166 | const char *name = argv[1]; 167 | if (strlen(name) + 5 + 1 > IFNAMSIZ) 168 | { 169 | printf("dev_name is too long.\n"); 170 | return 1; 171 | } 172 | char v4name[IFNAMSIZ], v6name[IFNAMSIZ]; 173 | strcpy(v4name, name); 174 | strcpy(v6name, name); 175 | strcat(v4name, "-ipv4"); 176 | strcat(v6name, "-ipv6"); 177 | printf("Using %s for IPv4 and %s for IPv6.\n", v4name, v6name); 178 | 179 | if (argc >= 4) 180 | { 181 | inet_pton(AF_INET6, argv[2], local_prefix); 182 | inet_pton(AF_INET6, argv[3], remote_prefix); 183 | printf("Local prefix: %s\n", argv[2]); 184 | printf("Remote prefix: %s\n", argv[3]); 185 | } 186 | else 187 | { 188 | inet_pton(AF_INET6, LOCAL_PREFIX, local_prefix); 189 | inet_pton(AF_INET6, REMOTE_PREFIX, remote_prefix); 190 | printf("Local prefix: %s\n", LOCAL_PREFIX); 191 | printf("Remote prefix: %s\n", REMOTE_PREFIX); 192 | } 193 | 194 | if ((v4tun_fd = tun_alloc(v4name, IFF_TUN | IFF_NO_PI)) < 0) 195 | { 196 | perror("Allocating interface for IPv4"); 197 | exit(1); 198 | } 199 | 200 | if ((v6tun_fd = tun_alloc(v6name, IFF_TUN | IFF_NO_PI)) < 0) 201 | { 202 | perror("Allocating interface for IPv6"); 203 | exit(1); 204 | } 205 | 206 | setuid(65534); 207 | setgid(65534); 208 | seteuid(65534); 209 | setegid(65534); 210 | 211 | pthread_t v4thread, v6thread; 212 | pthread_create(&v4thread, NULL, v4entry, NULL); 213 | pthread_create(&v6thread, NULL, v6entry, NULL); 214 | pthread_join(v4thread, NULL); 215 | pthread_join(v6thread, NULL); 216 | 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 4 | s.bind(('10.2.2.1', 6666)) 5 | s.sendto(b'g' * 4096, ('10.2.2.2', 6666)) 6 | data, end_point = s.recvfrom(65536) 7 | print('from', end_point, 'len', len(data)) 8 | -------------------------------------------------------------------------------- /test2.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 4 | s.bind(('10.2.2.2', 6666)) 5 | while True: 6 | data, end_point = s.recvfrom(65536) 7 | print('from', end_point, 'len', len(data)) 8 | s.sendto(data, end_point) 9 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.h" 9 | 10 | void print_hex(void *buff, size_t len) 11 | { 12 | char *ch = (char *)buff; 13 | for (size_t i = 0; i < len; ++i) 14 | { 15 | printf("%02hhx ", ch[i]); 16 | } 17 | } 18 | 19 | void print_ipv4_packet(ipv4_header_t *v4pkt) 20 | { 21 | char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 22 | inet_ntop(AF_INET, v4pkt->src_bytes, src, INET_ADDRSTRLEN); 23 | inet_ntop(AF_INET, v4pkt->dst_bytes, dst, INET_ADDRSTRLEN); 24 | printf("[IPv4] %s -> %s: header_len=%d, total_len=%d\n", 25 | src, dst, 26 | 4 * (v4pkt->version_header_len & 0xf), ntohs(v4pkt->total_len)); 27 | } 28 | 29 | void print_ipv6_packet(ipv6_header_t *v6pkt) 30 | { 31 | char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; 32 | inet_ntop(AF_INET6, v6pkt->src_bytes, src, INET6_ADDRSTRLEN); 33 | inet_ntop(AF_INET6, v6pkt->dst_bytes, dst, INET6_ADDRSTRLEN); 34 | printf("[IPv6] %s -> %s: payload_len=%d\n", src, dst, ntohs(v6pkt->payload_len)); 35 | } 36 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _TUN464_UTILS_H_ 2 | #define _TUN464_UTILS_H_ 3 | 4 | #include 5 | 6 | #include "common.h" 7 | 8 | void print_hex(void *buff, size_t len); 9 | void print_ipv4_packet(ipv4_header_t *v4pkt); 10 | void print_ipv6_packet(ipv6_header_t *v6pkt); 11 | 12 | #endif // _TUN464_UTILS_H_ 13 | --------------------------------------------------------------------------------