Contiki-NG
Loading...
Searching...
No Matches
nat64.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026, RISE Research Institutes of Sweden AB.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * \addtogroup nat64
33 * @{
34 *
35 * \file
36 * NAT64 core — dispatches IPv6 packets to protocol-specific
37 * handlers (UDP via kernel sockets, TCP via splice proxy,
38 * ICMPv6 Echo via unprivileged ICMP sockets) and synthesizes
39 * ICMPv6 Destination Unreachable errors back to the IoT node
40 * when forwarding fails.
41 * \author
42 * Nicolas Tsiftes <nicolas.tsiftes@ri.se>
43 */
44
45#include "nat64.h"
46#include "nat64-platform.h"
47#include "nat64-dns64.h"
48#include "nat64-tcp.h"
49#include "ipv6/ip64-addr.h"
50#include "net/ipv6/tcpip.h"
51
52#include <string.h>
53
54/* Log configuration */
55#include "sys/log.h"
56#define LOG_MODULE "NAT64"
57#define LOG_LEVEL LOG_LEVEL_INFO
58
59#define IPV6_HDRLEN 40
60#define IP_PROTO_TCP 6
61#define IP_PROTO_UDP 17
62#define IP_PROTO_ICMPV6 58
63#define DNS_PORT 53
64#define DEFAULT_HOPLIM 64
65
66#define ICMP6_DST_UNREACH 1
67#define ICMP6_ECHO_REQUEST 128
68#define ICMP6_ECHO_REPLY 129
69
70#define ICMP4_ECHO_REQUEST 8
71#define ICMP4_ECHO_REPLY 0
72
73#define ICMP6_HDRLEN 8
74
75#ifndef NAT64_ICMP6_QUEUE_SIZE
76#define NAT64_ICMP6_QUEUE_SIZE 8
77#endif
78
79/* Maximum DNS query payload that can be rewritten in place (AAAA->A
80 * substitution). Sized to cover EDNS0 messages up to typical 6LoWPAN
81 * MTUs; larger queries are forwarded without translation, which means
82 * the upstream resolver answers an AAAA query as AAAA and the IoT
83 * node sees no A->AAAA synthesis -- harmless for the data path but a
84 * miss for DNS64. Override at compile time if a deployment uses a
85 * larger EDNS payload size. */
86#ifndef NAT64_DNS_BUF_SIZE
87#define NAT64_DNS_BUF_SIZE 1500
88#endif
89
90/* Test-only override: when set, 127.0.0.0/8 is treated as a permitted
91 * NAT64 destination so the gateway can be exercised against an echo
92 * server bound to the BR's loopback interface. Never enable this in
93 * production; doing so lets the IoT mesh reach the BR host's local
94 * services. */
95#ifndef NAT64_CONF_ALLOW_LOOPBACK
96#define NAT64_CONF_ALLOW_LOOPBACK 0
97#endif
98
99/* Each queued ICMPv6 message holds an IPv6 header (40) + ICMPv6 header
100 * (8) + as much of the invoking packet as fits. 256 bytes is plenty
101 * for IPv6+TCP (60) or IPv6+UDP (48) headers and a small slack. */
102#define NAT64_ICMP6_BUF_SIZE 256
103
104struct v6hdr {
105 uint8_t vtc, tcflow;
106 uint16_t flow;
107 uint8_t plen[2];
108 uint8_t nexthdr, hoplim;
109 uip_ip6addr_t src, dst;
110};
111
112struct udphdr {
113 uint16_t sport, dport, ulen, uchksum;
114};
115
116struct icmp6_dst_unreach {
117 uint8_t type;
118 uint8_t code;
119 uint16_t checksum;
120 uint32_t unused;
121};
122
123struct icmp6_pending {
124 uint16_t len;
125 uint8_t buf[NAT64_ICMP6_BUF_SIZE];
126};
127
128static struct icmp6_pending icmp6_queue[NAT64_ICMP6_QUEUE_SIZE];
129static unsigned icmp6_queue_count;
130
131static bool active;
132/*---------------------------------------------------------------------------*/
133/**
134 * \brief Reject IPv4 destinations that must not be reached via NAT64.
135 * \param addr Candidate IPv4 destination address.
136 * \return true if the destination falls in a special-use range and
137 * must be dropped, false if it is a globally routable address.
138 *
139 * Covers loopback, private (RFC 1918), CGNAT, link-local, multicast,
140 * documentation, benchmarking and other special-use ranges per
141 * RFC 5735 / RFC 6890. Forwarding such addresses across the NAT64
142 * gateway would either leak link-local traffic onto the upstream
143 * IPv4 network or expose the gateway operator's private IPv4 LAN to
144 * the IoT mesh, so the gateway responds with an ICMPv6 Destination
145 * Unreachable (Communication Administratively Prohibited) instead.
146 */
147static bool
149{
150 uint8_t a = addr->u8[0];
151 uint8_t b = addr->u8[1];
152
153 /* 0.0.0.0/8 — "this" network */
154 if(a == 0) {
155 return true;
156 }
157 /* 10.0.0.0/8 — private */
158 if(a == 10) {
159 return true;
160 }
161 /* 100.64.0.0/10 — shared address space (RFC 6598) */
162 if(a == 100 && (b & 0xc0) == 64) {
163 return true;
164 }
165 /* 127.0.0.0/8 — loopback */
166 if(a == 127) {
167 return !NAT64_CONF_ALLOW_LOOPBACK;
168 }
169 /* 169.254.0.0/16 — link-local */
170 if(a == 169 && b == 254) {
171 return true;
172 }
173 /* 172.16.0.0/12 — private */
174 if(a == 172 && (b & 0xf0) == 16) {
175 return true;
176 }
177 /* 192.0.0.0/24 — IETF protocol assignments */
178 if(a == 192 && b == 0 && addr->u8[2] == 0) {
179 return true;
180 }
181 /* 192.0.2.0/24 — TEST-NET-1 (documentation) */
182 if(a == 192 && b == 0 && addr->u8[2] == 2) {
183 return true;
184 }
185 /* 192.168.0.0/16 — private */
186 if(a == 192 && b == 168) {
187 return true;
188 }
189 /* 198.18.0.0/15 — benchmarking */
190 if(a == 198 && (b & 0xfe) == 18) {
191 return true;
192 }
193 /* 198.51.100.0/24 — TEST-NET-2 */
194 if(a == 198 && b == 51 && addr->u8[2] == 100) {
195 return true;
196 }
197 /* 203.0.113.0/24 — TEST-NET-3 */
198 if(a == 203 && b == 0 && addr->u8[2] == 113) {
199 return true;
200 }
201 /* 224.0.0.0/4 — multicast */
202 if((a & 0xf0) == 224) {
203 return true;
204 }
205 /* 240.0.0.0/4 — reserved (includes 255.255.255.255) */
206 if((a & 0xf0) == 240) {
207 return true;
208 }
209 return false;
210}
211/*---------------------------------------------------------------------------*/
212static inline uint16_t
213get16(const uint8_t *p)
214{
215 return ((uint16_t)p[0] << 8) | p[1];
216}
217/*---------------------------------------------------------------------------*/
218static inline void
219put16(uint8_t *p, uint16_t v)
220{
221 p[0] = (uint8_t)(v >> 8);
222 p[1] = (uint8_t)v;
223}
224/*---------------------------------------------------------------------------*/
225static uint32_t
226cksum_acc(uint32_t acc, const void *buf, uint16_t nbytes)
227{
228 const uint8_t *p = buf;
229 while(nbytes > 1) {
230 acc += ((uint16_t)p[0] << 8) | p[1];
231 p += 2;
232 nbytes -= 2;
233 }
234 if(nbytes == 1) {
235 acc += (uint16_t)p[0] << 8;
236 }
237 return acc;
238}
239/*---------------------------------------------------------------------------*/
240static uint16_t
241cksum_fold(uint32_t acc)
242{
243 while(acc >> 16) {
244 acc = (acc & 0xffff) + (acc >> 16);
245 }
246 return ~((uint16_t)acc);
247}
248/*---------------------------------------------------------------------------*/
249static uint16_t
250udp6_checksum(const struct v6hdr *ip6, const struct udphdr *udp,
251 const uint8_t *payload, uint16_t payload_len)
252{
253 uint16_t udp_total = sizeof(struct udphdr) + payload_len;
254 uint32_t acc = 0;
255
256 acc = cksum_acc(acc, &ip6->src, sizeof(uip_ip6addr_t));
257 acc = cksum_acc(acc, &ip6->dst, sizeof(uip_ip6addr_t));
258 acc += udp_total;
259 acc += IP_PROTO_UDP;
260 acc = cksum_acc(acc, udp, sizeof(struct udphdr));
261 acc = cksum_acc(acc, payload, payload_len);
262
263 uint16_t result = cksum_fold(acc);
264 return (result == 0) ? 0xffff : result;
265}
266/*---------------------------------------------------------------------------*/
267static uint16_t
268icmp6_checksum(const struct v6hdr *ip6, const void *icmp, uint16_t icmp_len)
269{
270 uint32_t acc = 0;
271
272 acc = cksum_acc(acc, &ip6->src, sizeof(uip_ip6addr_t));
273 acc = cksum_acc(acc, &ip6->dst, sizeof(uip_ip6addr_t));
274 acc += icmp_len;
275 acc += IP_PROTO_ICMPV6;
276 acc = cksum_acc(acc, icmp, icmp_len);
277
278 uint16_t result = cksum_fold(acc);
279 return (result == 0) ? 0xffff : result;
280}
281/*---------------------------------------------------------------------------*/
282void
283nat64_queue_icmp6_unreach(const uint8_t *invoking_pkt, uint16_t invoking_len,
284 uint8_t code)
285{
286 const struct v6hdr *orig;
287 struct icmp6_pending *slot;
288 struct v6hdr *ip6;
289 struct icmp6_dst_unreach *icmp;
290 uint16_t embed_len, icmp_total, total;
291
292 if(invoking_len < IPV6_HDRLEN) {
293 return;
294 }
295
296 orig = (const struct v6hdr *)invoking_pkt;
297
298 /* RFC 4443 §2.4 (e): do not generate ICMPv6 errors in response to
299 * ICMPv6 errors. */
300 if(orig->nexthdr == IP_PROTO_ICMPV6) {
301 return;
302 }
303
304 if(icmp6_queue_count >= NAT64_ICMP6_QUEUE_SIZE) {
305 LOG_WARN("icmp6: queue full, dropping Dest Unreach code=%u\n", code);
306 return;
307 }
308
309 /* Embed as much of the invoking packet as fits, capped by the per-slot
310 * buffer. RFC 4443 §3.1 caps the entire ICMPv6 message at the IPv6
311 * minimum MTU (1280); our 256-byte slot is well below that. */
312 embed_len = invoking_len;
313 if(embed_len > NAT64_ICMP6_BUF_SIZE - IPV6_HDRLEN - sizeof(*icmp)) {
314 embed_len = NAT64_ICMP6_BUF_SIZE - IPV6_HDRLEN - sizeof(*icmp);
315 }
316
317 icmp_total = sizeof(*icmp) + embed_len;
318 total = IPV6_HDRLEN + icmp_total;
319
320 slot = &icmp6_queue[icmp6_queue_count];
321
322 ip6 = (struct v6hdr *)slot->buf;
323 ip6->vtc = 0x60;
324 ip6->tcflow = 0;
325 ip6->flow = 0;
326 put16(ip6->plen, icmp_total);
327 ip6->nexthdr = IP_PROTO_ICMPV6;
328 ip6->hoplim = DEFAULT_HOPLIM;
329 /* The error appears to come from the destination the IoT node tried
330 * to reach, so its socket layer can match the embedded headers. */
331 uip_ip6addr_copy(&ip6->src, &orig->dst);
332 uip_ip6addr_copy(&ip6->dst, &orig->src);
333
334 icmp = (struct icmp6_dst_unreach *)(slot->buf + IPV6_HDRLEN);
335 icmp->type = ICMP6_DST_UNREACH;
336 icmp->code = code;
337 icmp->checksum = 0;
338 icmp->unused = 0;
339
340 memcpy(slot->buf + IPV6_HDRLEN + sizeof(*icmp), invoking_pkt, embed_len);
341
342 icmp->checksum = uip_htons(icmp6_checksum(ip6, icmp, icmp_total));
343
344 slot->len = total;
345 icmp6_queue_count++;
346
347 LOG_DBG("icmp6: queued Dest Unreach code=%u (%u bytes)\n", code, total);
348}
349/*---------------------------------------------------------------------------*/
350void
351nat64_queue_icmp6_unreach_tuple(const uip_ip6addr_t *ip6_src, uint16_t src_port,
352 const uip_ip4addr_t *ip4_dst, uint16_t dst_port,
353 uint8_t ipproto, uint8_t code)
354{
355 uint8_t fake[IPV6_HDRLEN + 8];
356 struct v6hdr *ip6 = (struct v6hdr *)fake;
357 uint8_t *th = fake + IPV6_HDRLEN;
358
359 ip6->vtc = 0x60;
360 ip6->tcflow = 0;
361 ip6->flow = 0;
362 put16(ip6->plen, 8);
363 ip6->nexthdr = ipproto;
364 ip6->hoplim = DEFAULT_HOPLIM;
365 uip_ip6addr_copy(&ip6->src, ip6_src);
366 ip64_addr_4to6(ip4_dst, &ip6->dst);
367
368 /* First 8 bytes of the transport header: source port, destination
369 * port, and four zeroed bytes (TCP sequence number or UDP length +
370 * checksum). Enough for the IoT node's socket layer to match. */
371 put16(th, src_port);
372 put16(th + 2, dst_port);
373 th[4] = th[5] = th[6] = th[7] = 0;
374
375 nat64_queue_icmp6_unreach(fake, sizeof(fake), code);
376}
377/*---------------------------------------------------------------------------*/
378void
380{
381 unsigned i;
382
383 for(i = 0; i < icmp6_queue_count; i++) {
384 struct icmp6_pending *slot = &icmp6_queue[i];
385 if(slot->len == 0 || slot->len > UIP_BUFSIZE) {
386 continue;
387 }
388 memcpy(uip_buf, slot->buf, slot->len);
389 uip_len = slot->len;
390 LOG_DBG("icmp6: injecting Dest Unreach (%u bytes)\n", uip_len);
391 tcpip_input();
392 }
393 icmp6_queue_count = 0;
394}
395/*---------------------------------------------------------------------------*/
396bool
397nat64_is_ip64_addr(const uip_ip6addr_t *addr)
398{
399 return active && ip64_addr_is_ip64(addr);
400}
401/*---------------------------------------------------------------------------*/
402/**
403 * \brief Forward an outbound IPv6/UDP datagram to its IPv4 destination.
404 * \param pkt Original IPv6 packet (header + payload).
405 * \param ip6 Parsed IPv6 header (alias for pkt).
406 * \param dst4 Pre-extracted IPv4 destination address.
407 * \param payload_len Length of the IPv6 payload (UDP header + data).
408 * \return 1 if the packet was forwarded, 0 on a parse or send error.
409 *
410 * If the destination port is 53, a private copy of the payload is
411 * passed through ::nat64_dns64_6to4 so the upstream resolver receives
412 * an A query rather than the original AAAA query. All other UDP
413 * traffic is forwarded verbatim through the platform layer.
414 */
415static int
416handle_udp_output(const uint8_t *pkt, const struct v6hdr *ip6,
417 const uip_ip4addr_t *dst4, uint16_t payload_len)
418{
419 const struct udphdr *udp;
420 const uint8_t *data;
421 uint16_t data_len;
422 static uint8_t dns_buf[NAT64_DNS_BUF_SIZE];
423
424 if(payload_len < sizeof(struct udphdr)) {
425 LOG_WARN("udp_output: payload too short (%u bytes)\n", payload_len);
426 return 0;
427 }
428
429 udp = (const struct udphdr *)(pkt + IPV6_HDRLEN);
430 data = pkt + IPV6_HDRLEN + sizeof(struct udphdr);
431 data_len = payload_len - sizeof(struct udphdr);
432
433 if(udp->dport == UIP_HTONS(DNS_PORT)) {
434 if(data_len <= sizeof(dns_buf)) {
435 memcpy(dns_buf, data, data_len);
436 nat64_dns64_6to4(dns_buf, data_len);
437 data = dns_buf;
438 } else {
439 LOG_WARN("DNS64: query of %u bytes exceeds buffer (%u), forwarding "
440 "without AAAA->A rewrite\n",
441 data_len, (unsigned)sizeof(dns_buf));
442 }
443 }
444
445 return nat64_platform_udp_send(dst4, uip_ntohs(udp->dport),
446 &ip6->src, uip_ntohs(udp->sport),
447 data, data_len) >= 0 ? 1 : 0;
448}
449/*---------------------------------------------------------------------------*/
450/* ICMPv4 checksum: 16-bit one's complement over the entire ICMP message
451 * (no pseudo-header). */
452static uint16_t
453icmp4_checksum(const void *icmp, uint16_t icmp_len)
454{
455 return cksum_fold(cksum_acc(0, icmp, icmp_len));
456}
457/*---------------------------------------------------------------------------*/
458/**
459 * \brief Translate an outbound ICMPv6 Echo Request to ICMPv4 and send it.
460 * \param pkt Original IPv6 packet (header + payload).
461 * \param ip6 Parsed IPv6 header (alias for pkt).
462 * \param dst4 Pre-extracted IPv4 destination address.
463 * \param payload_len Length of the IPv6 payload (ICMPv6 message).
464 * \return 1 on success, 0 on parse or send error.
465 *
466 * Only Echo Request (type 128) is forwarded — all other ICMPv6 types
467 * are link-local control traffic (Neighbor Discovery, MLD, ...) that
468 * never traverses NAT64. The translation rewrites the type byte and
469 * recomputes the checksum (ICMPv4 has no pseudo-header) before
470 * handing the message to the platform layer for delivery on a Linux
471 * unprivileged ICMP socket.
472 */
473static int
474handle_icmp6_output(const uint8_t *pkt, const struct v6hdr *ip6,
475 const uip_ip4addr_t *dst4, uint16_t payload_len)
476{
477 static uint8_t icmp_buf[256];
478 const uint8_t *icmp;
479 uint16_t identifier;
480
481 if(payload_len < ICMP6_HDRLEN) {
482 LOG_WARN("icmp6_output: payload too short (%u bytes)\n", payload_len);
483 return 0;
484 }
485
486 icmp = pkt + IPV6_HDRLEN;
487
488 /* Only Echo Request is forwarded. Other ICMPv6 types (Neighbor
489 * Discovery, MLD, ...) are link-local and never traverse NAT64. */
490 if(icmp[0] != ICMP6_ECHO_REQUEST) {
491 LOG_DBG("icmp6_output: ignoring ICMPv6 type %u\n", icmp[0]);
492 return 0;
493 }
494
495 if(payload_len > sizeof(icmp_buf)) {
496 LOG_WARN("icmp6_output: echo request too large (%u bytes)\n",
497 payload_len);
498 return 0;
499 }
500
501 /* Translate to ICMPv4 Echo Request: rewrite type and recompute the
502 * checksum (which has no IPv4 pseudo-header). */
503 memcpy(icmp_buf, icmp, payload_len);
504 icmp_buf[0] = ICMP4_ECHO_REQUEST;
505 icmp_buf[2] = 0;
506 icmp_buf[3] = 0;
507 uint16_t cksum = icmp4_checksum(icmp_buf, payload_len);
508 icmp_buf[2] = (uint8_t)(cksum >> 8);
509 icmp_buf[3] = (uint8_t)cksum;
510
511 identifier = ((uint16_t)icmp[4] << 8) | icmp[5];
512
513 return nat64_platform_icmp_send(dst4, &ip6->src, identifier,
514 icmp_buf, payload_len) >= 0 ? 1 : 0;
515}
516/*---------------------------------------------------------------------------*/
517int
518nat64_output(const uint8_t *pkt, uint16_t len)
519{
520 const struct v6hdr *ip6 = (const struct v6hdr *)pkt;
521 uint16_t payload_len;
522 uip_ip4addr_t dst4;
523
524 if(len < IPV6_HDRLEN) {
525 LOG_WARN("output: packet too short (%u bytes)\n", len);
526 return 0;
527 }
528
529 payload_len = get16(ip6->plen);
530 if(payload_len + IPV6_HDRLEN > len) {
531 LOG_WARN("output: payload %u exceeds packet %u\n", payload_len, len);
532 return 0;
533 }
534
535 if(ip64_addr_is_ip64(&ip6->src)) {
536 LOG_WARN("output: dropping packet with NAT64 source address\n");
537 return 0;
538 }
539
540 if(!ip64_addr_6to4(&ip6->dst, &dst4)) {
541 LOG_WARN("output: destination is not a NAT64 address\n");
542 return 0;
543 }
544
545 if(ipv4_dst_is_forbidden(&dst4)) {
546 LOG_WARN("output: dropping packet to forbidden IPv4 destination "
547 "%u.%u.%u.%u\n",
548 dst4.u8[0], dst4.u8[1], dst4.u8[2], dst4.u8[3]);
550 return 0;
551 }
552
553 switch(ip6->nexthdr) {
554 case IP_PROTO_UDP:
555 return handle_udp_output(pkt, ip6, &dst4, payload_len);
556 case IP_PROTO_TCP:
557 return nat64_tcp_output(pkt, len);
558 case IP_PROTO_ICMPV6:
559 return handle_icmp6_output(pkt, ip6, &dst4, payload_len);
560 default:
561 LOG_WARN("output: unsupported next-header %u\n", ip6->nexthdr);
562 return 0;
563 }
564}
565/*---------------------------------------------------------------------------*/
566void
568 const uint8_t *payload, uint16_t payload_len)
569{
570 struct v6hdr *ip6;
571 struct udphdr *udp;
572 uint16_t udp_total;
573
574 udp_total = sizeof(struct udphdr) + payload_len;
575
576 if(IPV6_HDRLEN + udp_total > UIP_BUFSIZE) {
577 LOG_WARN("udp_input: response too large (%u bytes)\n",
578 IPV6_HDRLEN + udp_total);
579 return;
580 }
581
582 ip6 = (struct v6hdr *)uip_buf;
583 ip6->vtc = 0x60;
584 ip6->tcflow = 0;
585 ip6->flow = 0;
586 ip6->nexthdr = IP_PROTO_UDP;
587 ip6->hoplim = DEFAULT_HOPLIM;
588
589 ip64_addr_4to6(&s->ip4_remote, &ip6->src);
590 uip_ip6addr_copy(&ip6->dst, &s->ip6_peer);
591
592 udp = (struct udphdr *)(uip_buf + IPV6_HDRLEN);
593 udp->sport = uip_htons(s->ip4_remote_port);
594 udp->dport = uip_htons(s->ip6_peer_port);
595
596 memcpy(uip_buf + IPV6_HDRLEN + sizeof(struct udphdr),
597 payload, payload_len);
598
599 if(s->ip4_remote_port == DNS_PORT) {
600 uint16_t max_payload = UIP_BUFSIZE - IPV6_HDRLEN - sizeof(struct udphdr);
601 uint16_t new_len;
602 new_len = nat64_dns64_4to6(
603 payload, payload_len,
604 uip_buf + IPV6_HDRLEN + sizeof(struct udphdr), payload_len,
605 max_payload);
606 if(new_len > max_payload) {
607 LOG_WARN("udp_input: DNS64 response too large (%u bytes)\n", new_len);
608 return;
609 }
610 payload_len = new_len;
611 udp_total = sizeof(struct udphdr) + payload_len;
612 }
613
614 put16(ip6->plen, udp_total);
615 udp->ulen = uip_htons(udp_total);
616
617 udp->uchksum = 0;
618 udp->uchksum = uip_htons(udp6_checksum(
619 ip6, udp,
620 uip_buf + IPV6_HDRLEN + sizeof(struct udphdr),
621 payload_len));
622
623 uip_len = IPV6_HDRLEN + udp_total;
624
625 LOG_DBG("udp_input: injecting %u-byte packet\n", uip_len);
626 tcpip_input();
627}
628/*---------------------------------------------------------------------------*/
629void
630nat64_icmp_input(struct nat64_session *s, const uint8_t *icmp_pkt, uint16_t len)
631{
632 struct v6hdr *ip6;
633 uint8_t *icmp_out;
634
635 if(len < ICMP6_HDRLEN) {
636 LOG_WARN("icmp_input: reply too short (%u bytes)\n", len);
637 return;
638 }
639 if(IPV6_HDRLEN + len > UIP_BUFSIZE) {
640 LOG_WARN("icmp_input: reply too large (%u bytes)\n", IPV6_HDRLEN + len);
641 return;
642 }
643
644 /* Only forward Echo Replies; the kernel may surface other ICMP
645 * types on the ping socket (e.g., Destination Unreachable from a
646 * router on the IPv4 path). We do not relay those — the kernel
647 * already returns them via socket-level errors that the platform
648 * layer maps to ICMPv6 errors via nat64_queue_icmp6_unreach_*. */
649 if(icmp_pkt[0] != ICMP4_ECHO_REPLY) {
650 LOG_DBG("icmp_input: ignoring ICMPv4 type %u\n", icmp_pkt[0]);
651 return;
652 }
653
654 ip6 = (struct v6hdr *)uip_buf;
655 ip6->vtc = 0x60;
656 ip6->tcflow = 0;
657 ip6->flow = 0;
658 put16(ip6->plen, len);
659 ip6->nexthdr = IP_PROTO_ICMPV6;
660 ip6->hoplim = DEFAULT_HOPLIM;
661 ip64_addr_4to6(&s->ip4_remote, &ip6->src);
662 uip_ip6addr_copy(&ip6->dst, &s->ip6_peer);
663
664 icmp_out = uip_buf + IPV6_HDRLEN;
665 memcpy(icmp_out, icmp_pkt, len);
666
667 /* Translate to ICMPv6 Echo Reply (type 129) and restore the
668 * original identifier from the session — the kernel may have
669 * rewritten it on the wire. */
670 icmp_out[0] = ICMP6_ECHO_REPLY;
671 icmp_out[4] = (uint8_t)(s->ip6_peer_port >> 8);
672 icmp_out[5] = (uint8_t)s->ip6_peer_port;
673
674 icmp_out[2] = 0;
675 icmp_out[3] = 0;
676 uint16_t cksum = icmp6_checksum(ip6, icmp_out, len);
677 icmp_out[2] = (uint8_t)(cksum >> 8);
678 icmp_out[3] = (uint8_t)cksum;
679
680 uip_len = IPV6_HDRLEN + len;
681
682 LOG_DBG("icmp_input: injecting Echo Reply (%u bytes)\n", uip_len);
683 tcpip_input();
684}
685/*---------------------------------------------------------------------------*/
686void
688{
690 active = true;
691}
692/*---------------------------------------------------------------------------*/
693/** @} */
int nat64_platform_udp_send(const uip_ip4addr_t *dst, uint16_t dstport, const uip_ip6addr_t *ip6_src, uint16_t srcport, const uint8_t *payload, uint16_t len)
Forward a UDP payload to an IPv4 server.
Definition nat64-sock.c:411
static int handle_udp_output(const uint8_t *pkt, const struct v6hdr *ip6, const uip_ip4addr_t *dst4, uint16_t payload_len)
Forward an outbound IPv6/UDP datagram to its IPv4 destination.
Definition nat64.c:416
int nat64_platform_icmp_send(const uip_ip4addr_t *dst, const uip_ip6addr_t *ip6_src, uint16_t identifier, const uint8_t *icmp_pkt, uint16_t icmp_len)
Forward an ICMPv4 Echo Request to an IPv4 destination.
Definition nat64-sock.c:596
void nat64_queue_icmp6_unreach_tuple(const uip_ip6addr_t *ip6_src, uint16_t src_port, const uip_ip4addr_t *ip4_dst, uint16_t dst_port, uint8_t ipproto, uint8_t code)
Queue an ICMPv6 Destination Unreachable for a 5-tuple whose connection failed.
Definition nat64.c:351
bool nat64_is_ip64_addr(const uip_ip6addr_t *addr)
Check whether an IPv6 address embeds an IPv4 address via the NAT64 prefix.
Definition nat64.c:397
void nat64_udp_input(struct nat64_session *s, const uint8_t *payload, uint16_t payload_len)
Inject a UDP response from an IPv4 server.
Definition nat64.c:567
void nat64_activate(void)
Initialize the NAT64 gateway.
Definition nat64.c:687
static bool ipv4_dst_is_forbidden(const uip_ip4addr_t *addr)
Reject IPv4 destinations that must not be reached via NAT64.
Definition nat64.c:148
int nat64_output(const uint8_t *pkt, uint16_t len)
Process an outgoing IPv6 packet destined for an IPv4 host.
Definition nat64.c:518
void nat64_queue_icmp6_unreach(const uint8_t *invoking_pkt, uint16_t invoking_len, uint8_t code)
Queue an ICMPv6 Destination Unreachable for delivery to the IoT node.
Definition nat64.c:283
static int handle_icmp6_output(const uint8_t *pkt, const struct v6hdr *ip6, const uip_ip4addr_t *dst4, uint16_t payload_len)
Translate an outbound ICMPv6 Echo Request to ICMPv4 and send it.
Definition nat64.c:474
uint16_t nat64_dns64_4to6(const uint8_t *ipv4data, uint16_t ipv4len, uint8_t *ipv6data, uint16_t ipv6len, uint16_t ipv6bufsiz)
Rewrite an incoming DNS response from A to AAAA.
int nat64_tcp_output(const uint8_t *pkt, uint16_t len)
Process an outgoing IPv6+TCP packet from an IoT node.
Definition nat64-tcp.c:393
void nat64_flush_icmp6(void)
Drain the queue of pending ICMPv6 errors into the uIP stack.
Definition nat64.c:379
#define NAT64_ICMP6_ADMIN
Communication administratively prohibited.
Definition nat64.h:170
void nat64_dns64_6to4(uint8_t *data, uint16_t len)
Rewrite an outgoing DNS query from AAAA to A.
void nat64_tcp_init(void)
Initialize the TCP splice proxy.
Definition nat64-tcp.c:740
void nat64_icmp_input(struct nat64_session *s, const uint8_t *icmp_pkt, uint16_t len)
Inject an ICMPv4 Echo Reply received from an IPv4 host.
Definition nat64.c:630
void tcpip_input(void)
Deliver an incoming packet to the TCP/IP stack.
Definition tcpip.c:433
#define ICMP6_ECHO_REPLY
Echo reply.
Definition uip-icmp6.h:58
#define ICMP6_DST_UNREACH
dest unreachable
Definition uip-icmp6.h:53
#define ICMP6_ECHO_REQUEST
Echo request.
Definition uip-icmp6.h:57
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition uip.h:1157
uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition uip6.c:2345
#define uip_buf
Macro to access uip_aligned_buf as an array of bytes.
Definition uip.h:465
uint16_t uip_len
The length of the packet in the uip_buf buffer.
Definition uip6.c:159
#define UIP_BUFSIZE
The size of the uIP packet buffer.
Definition uipopt.h:92
Header file for the logging system.
NAT64 DNS64 translation (RFC 6147).
NAT64 platform interface — socket-based.
NAT64 TCP splice proxy.
NAT64 gateway core API.
A NAT64 session binding an IoT node's IPv6 flow to an IPv4 socket.
uint16_t ip4_remote_port
IPv4 server port.
uip_ip6addr_t ip6_peer
IoT node's IPv6 address.
uip_ip4addr_t ip4_remote
IPv4 server address.
uint16_t ip6_peer_port
IoT node's transport port.
Header for the Contiki/uIP interface.
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition uip-nd6.c:107
Representation of an IP address.
Definition uip.h:95