Contiki-NG
Loading...
Searching...
No Matches
nat64-sock.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 socket-based IPv4 forwarding for the native platform.
37 *
38 * Implements the platform layer (nat64-platform.h) using BSD
39 * sockets on Linux/macOS. Each NAT64 session owns one
40 * non-blocking socket (UDP, TCP, or unprivileged ICMP) that
41 * is registered with the native main-loop select callback.
42 * Inbound data is delivered to the protocol-agnostic core via
43 * ::nat64_udp_input / ::nat64_tcp_data_in / ::nat64_icmp_input,
44 * and socket-level errors are translated into ICMPv6
45 * Destination Unreachable codes returned to the IoT node.
46 * \author
47 * Nicolas Tsiftes <nicolas.tsiftes@ri.se>
48 */
49
50#include "contiki.h"
51#include "nat64.h"
52#include "nat64-platform.h"
53#include "nat64-tcp.h"
54#include "sys/platform.h"
55
56#include <arpa/inet.h>
57#include <errno.h>
58#include <fcntl.h>
59#include <netinet/in.h>
60#include <string.h>
61#include <sys/socket.h>
62#include <unistd.h>
63
64/* Log configuration */
65#include "sys/log.h"
66#define LOG_MODULE "NAT64"
67#define LOG_LEVEL LOG_LEVEL_INFO
68
69#ifndef NAT64_MAX_SESSIONS
70#define NAT64_MAX_SESSIONS 128
71#endif
72
73#ifndef NAT64_SESSION_TIMEOUT
74#define NAT64_SESSION_TIMEOUT (5 * 60 * CLOCK_SECOND)
75#endif
76
77#define NAT64_PRIO CONTIKI_VERBOSE_PRIO + 40
78
79#ifndef NAT64_MAX_SESSIONS_PER_NODE
80#define NAT64_MAX_SESSIONS_PER_NODE 8
81#endif
82
83static struct nat64_session sessions[NAT64_MAX_SESSIONS];
84
85/* Whether NAT64 is active. Off by default and enabled with --nat64, unless a
86 * build turns it on at compile time (the standalone translator module does). */
87#ifndef NAT64_DEFAULT_ENABLED
88#define NAT64_DEFAULT_ENABLED 0
89#endif
90static bool nat64_enabled = NAT64_DEFAULT_ENABLED;
91
92/*---------------------------------------------------------------------------*/
93/**
94 * \brief Map a Linux errno to an ICMPv6 Destination Unreachable code.
95 * \param err A `errno` value reported by a connect()/send()/recv()
96 * failure on an IPv4 socket.
97 * \return One of the NAT64_ICMP6_* codes (RFC 4443 ยง3.1).
98 *
99 * Used by the platform layer to translate socket-level failures into
100 * the ICMPv6 errors returned to the IoT node via
101 * ::nat64_queue_icmp6_unreach_tuple. Unrecognized errors fall back
102 * to "no route to destination".
103 */
104static uint8_t
106{
107 switch(err) {
108 case ECONNREFUSED:
109 return NAT64_ICMP6_PORT;
110 case EHOSTUNREACH:
111 case ETIMEDOUT:
112 return NAT64_ICMP6_ADDR;
113 case ENETUNREACH:
114 return NAT64_ICMP6_NOROUTE;
115 case EACCES:
116 case EPERM:
117 return NAT64_ICMP6_ADMIN;
118 default:
119 return NAT64_ICMP6_NOROUTE;
120 }
121}
122
123/*---------------------------------------------------------------------------*/
124/* Session helpers. */
125/*---------------------------------------------------------------------------*/
126
127static void
128close_session(struct nat64_session *s)
129{
130 if(s->proto == NAT64_PROTO_TCP) {
132 }
133 if(s->fd >= 0) {
134 select_set_callback(s->fd, NULL);
135 close(s->fd);
136 s->fd = -1;
137 }
138 s->active = false;
139 s->proto = NAT64_PROTO_NONE;
140}
141/*---------------------------------------------------------------------------*/
142/**
143 * \brief Reap an expired session, notifying its peer if applicable.
144 * \param s The session whose expiry timer has fired.
145 *
146 * For ESTABLISHED TCP sessions this synthesizes a FIN toward the IoT
147 * node before tearing down, so the IoT-side TCP layer doesn't keep a
148 * zombie connection until its own keepalive fires.
149 */
150static void
152{
153 if(s->proto == NAT64_PROTO_TCP &&
154 s->tcp_state == NAT64_TCP_ESTABLISHED) {
156 }
157 close_session(s);
158}
159/*---------------------------------------------------------------------------*/
160static struct nat64_session *
161find_session(enum nat64_session_proto proto,
162 const uip_ip6addr_t *ip6_src, uint16_t srcport,
163 const uip_ip4addr_t *dst, uint16_t dstport)
164{
165 unsigned i;
166 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
167 struct nat64_session *s = &sessions[i];
168 if(s->active &&
169 s->proto == proto &&
170 s->ip6_peer_port == srcport &&
171 s->ip4_remote_port == dstport &&
172 uip_ip6addr_cmp(&s->ip6_peer, ip6_src) &&
173 uip_ip4addr_cmp(&s->ip4_remote, dst)) {
174 if(timer_expired(&s->expiry)) {
176 return NULL;
177 }
178 return s;
179 }
180 }
181 return NULL;
182}
183/*---------------------------------------------------------------------------*/
184static unsigned
185count_node_sessions(const uip_ip6addr_t *ip6_src)
186{
187 unsigned i, count = 0;
188 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
189 if(sessions[i].active &&
190 !timer_expired(&sessions[i].expiry) &&
191 uip_ip6addr_cmp(&sessions[i].ip6_peer, ip6_src)) {
192 count++;
193 }
194 }
195 return count;
196}
197/*---------------------------------------------------------------------------*/
198static struct nat64_session *
199alloc_session(const uip_ip6addr_t *ip6_src)
200{
201 unsigned i;
202
203 if(count_node_sessions(ip6_src) >= NAT64_MAX_SESSIONS_PER_NODE) {
204 LOG_WARN("Per-node session limit reached (%u)\n",
205 NAT64_MAX_SESSIONS_PER_NODE);
206 return NULL;
207 }
208
209 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
210 if(!sessions[i].active) {
211 return &sessions[i];
212 }
213 if(timer_expired(&sessions[i].expiry)) {
214 expire_session(&sessions[i]);
215 return &sessions[i];
216 }
217 }
218 LOG_WARN("Session table full\n");
219 return NULL;
220}
221/*---------------------------------------------------------------------------*/
222static void
223fill_session(struct nat64_session *s, enum nat64_session_proto proto,
224 const uip_ip6addr_t *ip6_src, uint16_t srcport,
225 const uip_ip4addr_t *dst, uint16_t dstport)
226{
227 s->proto = proto;
228 uip_ip6addr_copy(&s->ip6_peer, ip6_src);
229 s->ip6_peer_port = srcport;
230 memcpy(&s->ip4_remote, dst, sizeof(uip_ip4addr_t));
231 s->ip4_remote_port = dstport;
232 s->active = true;
233 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
234}
235/*---------------------------------------------------------------------------*/
236static bool
237register_fd(struct nat64_session *s);
238
239static struct sockaddr_in
240make_addr(const uip_ip4addr_t *ip, uint16_t port)
241{
242 struct sockaddr_in sa;
243 memset(&sa, 0, sizeof(sa));
244 sa.sin_family = AF_INET;
245 sa.sin_port = htons(port);
246 memcpy(&sa.sin_addr, ip, sizeof(uip_ip4addr_t));
247 return sa;
248}
249
250/*---------------------------------------------------------------------------*/
251/* Select callbacks. */
252/*---------------------------------------------------------------------------*/
253
254static void
255handle_tcp_connect_complete(struct nat64_session *s)
256{
257 int err = 0;
258 socklen_t errlen = sizeof(err);
259
260 if(getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0 || err != 0) {
261 int e = err ? err : errno;
262 LOG_WARN("TCP connect failed: %s\n", strerror(e));
263 nat64_queue_icmp6_unreach_tuple(&s->ip6_peer, s->ip6_peer_port,
264 &s->ip4_remote, s->ip4_remote_port,
265 IPPROTO_TCP, errno_to_icmp6_code(e));
266 close_session(s);
267 return;
268 }
269
270 LOG_INFO("TCP connected to %u.%u.%u.%u:%u (fd %d)\n",
271 s->ip4_remote.u8[0], s->ip4_remote.u8[1],
272 s->ip4_remote.u8[2], s->ip4_remote.u8[3],
273 s->ip4_remote_port, s->fd);
274
275 s->tcp_state = NAT64_TCP_ESTABLISHED;
277}
278/*---------------------------------------------------------------------------*/
279static int
280generic_set_fd(fd_set *rset, fd_set *wset)
281{
282 unsigned i;
283
284 /* Flush deferred TCP ACKs and any pending ICMPv6 errors. set_fd is
285 * called on every main-loop iteration, so this ensures ACKs and
286 * errors are delivered promptly even when select() times out with no
287 * ready fds. */
290
291 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
292 struct nat64_session *s = &sessions[i];
293 if(!s->active || s->fd < 0) {
294 continue;
295 }
296 if(s->proto == NAT64_PROTO_TCP &&
297 s->tcp_state == NAT64_TCP_CONNECTING) {
298 FD_SET(s->fd, wset);
299 } else if(s->proto == NAT64_PROTO_TCP &&
301 /* The TCP proxy owns only one server-to-IoT buffer. Suppressing
302 * reads here keeps unread bytes in the kernel until the IoT node
303 * ACKs the current chunk. */
304 } else {
305 FD_SET(s->fd, rset);
306 }
307 }
308 return 1;
309}
310/*---------------------------------------------------------------------------*/
311static void
312generic_handle_fd(fd_set *rset, fd_set *wset)
313{
314 unsigned i;
315 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
316 struct nat64_session *s = &sessions[i];
317 if(!s->active || s->fd < 0) {
318 continue;
319 }
320
321 if(timer_expired(&s->expiry)) {
323 continue;
324 }
325
326 if(s->proto == NAT64_PROTO_TCP &&
327 s->tcp_state == NAT64_TCP_CONNECTING &&
328 FD_ISSET(s->fd, wset)) {
329 handle_tcp_connect_complete(s);
330 continue;
331 }
332
333 if(!FD_ISSET(s->fd, rset)) {
334 continue;
335 }
336
337 if(s->proto == NAT64_PROTO_TCP &&
338 s->tcp_state == NAT64_TCP_ESTABLISHED) {
339 uint8_t buf[1500];
340 ssize_t n = recv(s->fd, buf, sizeof(buf), 0);
341 if(n > 0) {
342 LOG_INFO("TCP recv %zd bytes from server (fd %d)\n", n, s->fd);
343 nat64_tcp_data_in(s, buf, (uint16_t)n);
344 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
345 } else if(n == 0) {
346 LOG_INFO("TCP server closed connection (fd %d)\n", s->fd);
348 s->tcp_state = NAT64_TCP_CLOSING;
350 /* IoT side already FIN'd โ€” both halves done, reap now. */
351 LOG_INFO("TCP both sides FIN'd, destroying session\n");
352 close_session(s);
353 }
354 } else if(errno != EAGAIN && errno != EWOULDBLOCK) {
355 LOG_ERR("TCP recv error (fd %d): %s\n", s->fd, strerror(errno));
357 s->tcp_state = NAT64_TCP_CLOSING;
359 LOG_INFO("TCP both sides done, destroying session\n");
360 close_session(s);
361 }
362 }
363 } else if(s->proto == NAT64_PROTO_UDP) {
364 uint8_t buf[1500];
365 ssize_t n = recv(s->fd, buf, sizeof(buf), 0);
366 if(n > 0) {
367 nat64_udp_input(s, buf, (uint16_t)n);
368 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
369 } else if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
370 int e = errno;
371
372 LOG_ERR("UDP recvfrom error (fd %d): %s\n", s->fd, strerror(e));
373 nat64_queue_icmp6_unreach_tuple(&s->ip6_peer, s->ip6_peer_port,
374 &s->ip4_remote, s->ip4_remote_port,
375 IPPROTO_UDP, errno_to_icmp6_code(e));
376 close_session(s);
377 }
378 } else if(s->proto == NAT64_PROTO_ICMP) {
379 uint8_t buf[256];
380 ssize_t n = recv(s->fd, buf, sizeof(buf), 0);
381 if(n > 0) {
382 nat64_icmp_input(s, buf, (uint16_t)n);
383 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
384 } else if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
385 LOG_ERR("ICMP recv error (fd %d): %s\n", s->fd, strerror(errno));
386 }
387 }
388 }
389}
390/*---------------------------------------------------------------------------*/
391static const struct select_callback nat64_select_cb = {
392 generic_set_fd,
393 generic_handle_fd,
394};
395/*---------------------------------------------------------------------------*/
396static bool
397register_fd(struct nat64_session *s)
398{
399 if(fcntl(s->fd, F_SETFL, O_NONBLOCK) < 0) {
400 /* If the socket stays blocking, a single slow IPv4 server can
401 * stall the entire main loop on the next send/recv. Refuse the
402 * session rather than risk that. */
403 LOG_ERR("fcntl(F_SETFL, O_NONBLOCK) failed for fd %d: %s\n",
404 s->fd, strerror(errno));
405 close(s->fd);
406 s->fd = -1;
407 s->active = false;
408 return false;
409 }
410 if(!select_set_callback(s->fd, &nat64_select_cb)) {
411 LOG_ERR("select_set_callback failed for fd %d\n", s->fd);
412 close(s->fd);
413 s->fd = -1;
414 s->active = false;
415 return false;
416 }
417 return true;
418}
419
420/*---------------------------------------------------------------------------*/
421/* Platform API. */
422/*---------------------------------------------------------------------------*/
423
424int
425nat64_platform_udp_send(const uip_ip4addr_t *dst, uint16_t dstport,
426 const uip_ip6addr_t *ip6_src, uint16_t srcport,
427 const uint8_t *payload, uint16_t len)
428{
429 struct nat64_session *s;
430 ssize_t sent;
431
432 s = find_session(NAT64_PROTO_UDP, ip6_src, srcport, dst, dstport);
433 if(s == NULL) {
434 s = alloc_session(ip6_src);
435 if(s == NULL) {
436 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
437 IPPROTO_UDP, NAT64_ICMP6_ADMIN);
438 return -1;
439 }
440 s->fd = socket(AF_INET, SOCK_DGRAM, 0);
441 if(s->fd < 0) {
442 LOG_ERR("socket(DGRAM): %s\n", strerror(errno));
443 return -1;
444 }
445 fill_session(s, NAT64_PROTO_UDP, ip6_src, srcport, dst, dstport);
446 if(!register_fd(s)) {
447 return -1;
448 }
449 /* Connect the UDP socket so the kernel filters incoming packets
450 * by source address, preventing spoofed responses. */
451 struct sockaddr_in peer = make_addr(dst, dstport);
452 if(connect(s->fd, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
453 int e = errno;
454 LOG_ERR("UDP connect: %s\n", strerror(e));
455 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
456 IPPROTO_UDP, errno_to_icmp6_code(e));
457 close_session(s);
458 return -1;
459 }
460 LOG_DBG("New UDP session fd %d\n", s->fd);
461 }
462
463 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
464
465 sent = send(s->fd, payload, len, 0);
466 if(sent < 0) {
467 int e = errno;
468 LOG_ERR("sendto: %s\n", strerror(e));
469 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
470 IPPROTO_UDP, errno_to_icmp6_code(e));
471 return -1;
472 }
473 return (int)sent;
474}
475/*---------------------------------------------------------------------------*/
476struct nat64_session *
477nat64_platform_tcp_connect(const uip_ip4addr_t *dst, uint16_t dstport,
478 const uip_ip6addr_t *ip6_src, uint16_t srcport,
479 uint32_t peer_isn)
480{
481 struct nat64_session *s;
482 int ret;
483
484 s = find_session(NAT64_PROTO_TCP, ip6_src, srcport, dst, dstport);
485 if(s != NULL) {
486 return s;
487 }
488
489 s = alloc_session(ip6_src);
490 if(s == NULL) {
491 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
492 IPPROTO_TCP, NAT64_ICMP6_ADMIN);
493 return NULL;
494 }
495
496 s->fd = socket(AF_INET, SOCK_STREAM, 0);
497 if(s->fd < 0) {
498 LOG_ERR("socket(STREAM): %s\n", strerror(errno));
499 return NULL;
500 }
501
502 fill_session(s, NAT64_PROTO_TCP, ip6_src, srcport, dst, dstport);
503 s->peer_isn = peer_isn;
504 s->tcp_state = NAT64_TCP_CONNECTING;
505
506 if(!register_fd(s)) {
507 return NULL;
508 }
509
510 struct sockaddr_in dest = make_addr(dst, dstport);
511 ret = connect(s->fd, (struct sockaddr *)&dest, sizeof(dest));
512 if(ret < 0 && errno != EINPROGRESS) {
513 int e = errno;
514 LOG_ERR("connect: %s\n", strerror(e));
515 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
516 IPPROTO_TCP, errno_to_icmp6_code(e));
517 close_session(s);
518 return NULL;
519 }
520
521 if(ret == 0) {
522 handle_tcp_connect_complete(s);
523 }
524
525 LOG_DBG("TCP connecting fd %d to %u.%u.%u.%u:%u\n",
526 s->fd,
527 dst->u8[0], dst->u8[1], dst->u8[2], dst->u8[3], dstport);
528 return s;
529}
530/*---------------------------------------------------------------------------*/
531int
533 const uint8_t *data, uint16_t len)
534{
535 ssize_t sent;
536
537 if(s == NULL || s->tcp_state != NAT64_TCP_ESTABLISHED) {
538 return -1;
539 }
540
541 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
542
543 sent = send(s->fd, data, len, 0);
544 if(sent < 0) {
545 if(errno == EAGAIN || errno == EWOULDBLOCK) {
546 /* NAT64 does not own a copy of IoT-to-server bytes. Returning 0
547 * leaves peer_next unchanged, so the IoT TCP stack remains the
548 * source of truth and retransmits the unsent data on its RTO. */
549 LOG_WARN("TCP send would block (fd %d), IoT will retransmit\n",
550 s->fd);
551 return 0;
552 }
553 LOG_ERR("TCP send error (fd %d): %s\n", s->fd, strerror(errno));
554 return -1;
555 }
556 LOG_INFO("TCP sent %zd bytes to server (fd %d)\n", sent, s->fd);
557 return (int)sent;
558}
559/*---------------------------------------------------------------------------*/
560void
562{
563 if(s == NULL) {
564 return;
565 }
566 LOG_DBG("TCP shutdown(WR) fd %d\n", s->fd);
567 /* Half-close: signal EOF to the IPv4 server but keep the read side
568 * open so any remaining server->IoT data can still be delivered.
569 * The session transitions to CLOSING only when the server itself
570 * closes (recv() returns 0 in generic_handle_fd) or when an explicit
571 * teardown occurs. */
572 shutdown(s->fd, SHUT_WR);
573}
574/*---------------------------------------------------------------------------*/
575void
577{
578 if(s == NULL) {
579 return;
580 }
581 LOG_DBG("TCP destroy fd %d\n", s->fd);
582 close_session(s);
583}
584/*---------------------------------------------------------------------------*/
585void
587{
588 if(s == NULL) {
589 return;
590 }
591 if(s->fd >= 0) {
592 /* SO_LINGER with l_linger=0 makes the subsequent close() emit a
593 * TCP RST instead of a graceful FIN, so the upstream server sees
594 * the connection abort directly. */
595 struct linger lin = { .l_onoff = 1, .l_linger = 0 };
596 setsockopt(s->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(lin));
597 }
598 LOG_DBG("TCP abort fd %d\n", s->fd);
599 close_session(s);
600}
601/*---------------------------------------------------------------------------*/
602int
604 const uip_ip6addr_t *ip6_src, uint16_t identifier,
605 const uint8_t *icmp_pkt, uint16_t icmp_len)
606{
607 struct nat64_session *s;
608 ssize_t sent;
609
610 /* Sessions are keyed on (ip6_src, identifier, dst, 0). ip4_remote_port
611 * is unused for ICMP and stored as 0; ip6_peer_port stores the
612 * ICMPv6 Echo identifier. */
613 s = find_session(NAT64_PROTO_ICMP, ip6_src, identifier, dst, 0);
614 if(s == NULL) {
615 s = alloc_session(ip6_src);
616 if(s == NULL) {
617 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
618 IPPROTO_ICMPV6, NAT64_ICMP6_ADMIN);
619 return -1;
620 }
621
622 /* Open an unprivileged ICMP socket. Requires either CAP_NET_RAW
623 * or the running GID to be in net.ipv4.ping_group_range. */
624 s->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
625 if(s->fd < 0) {
626 int e = errno;
627 LOG_ERR("socket(ICMP): %s\n", strerror(e));
628 if(e == EACCES || e == EPERM) {
629 LOG_ERR("Hint: add the running user's GID to "
630 "net.ipv4.ping_group_range\n");
631 }
632 s->active = false;
633 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
634 IPPROTO_ICMPV6,
636 return -1;
637 }
638
639 fill_session(s, NAT64_PROTO_ICMP, ip6_src, identifier, dst, 0);
640
641 if(!register_fd(s)) {
642 return -1;
643 }
644
645 /* Connect so the kernel filters ICMP replies by source address. */
646 struct sockaddr_in peer = make_addr(dst, 0);
647 if(connect(s->fd, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
648 int e = errno;
649 LOG_ERR("ICMP connect: %s\n", strerror(e));
650 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
651 IPPROTO_ICMPV6,
653 close_session(s);
654 return -1;
655 }
656 LOG_DBG("New ICMP session fd %d id=%u\n", s->fd, identifier);
657 }
658
659 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
660
661 sent = send(s->fd, icmp_pkt, icmp_len, 0);
662 if(sent < 0) {
663 int e = errno;
664 LOG_ERR("ICMP send: %s\n", strerror(e));
665 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
666 IPPROTO_ICMPV6, errno_to_icmp6_code(e));
667 return -1;
668 }
669 return (int)sent;
670}
671/*---------------------------------------------------------------------------*/
672static bool
673read_urandom(void *buf, size_t len)
674{
675 int fd = open("/dev/urandom", O_RDONLY);
676 if(fd < 0) {
677 LOG_ERR("Failed to open /dev/urandom: %s\n", strerror(errno));
678 return false;
679 }
680 ssize_t n = read(fd, buf, len);
681 close(fd);
682 if(n != (ssize_t)len) {
683 LOG_ERR("Short read from /dev/urandom\n");
684 return false;
685 }
686 return true;
687}
688/*---------------------------------------------------------------------------*/
689bool
691{
692 unsigned i;
693 uint8_t isn_key[16];
694
695 memset(sessions, 0, sizeof(sessions));
696 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
697 sessions[i].fd = -1;
698 }
699
700 if(!read_urandom(isn_key, sizeof(isn_key))) {
701 LOG_ERR("Cannot seed ISN secret โ€” /dev/urandom unavailable\n");
702 return false;
703 }
705 memset(isn_key, 0, sizeof(isn_key));
706
708 LOG_INFO("Socket-based NAT64 initialized (%u max sessions)\n",
709 NAT64_MAX_SESSIONS);
710 return true;
711}
712/*---------------------------------------------------------------------------*/
713static int
714nat64_option_callback(const char *optarg)
715{
716 nat64_enabled = true;
717 return 0;
718}
719CONTIKI_OPTION(NAT64_PRIO, { "nat64", no_argument, NULL, 0 },
720 nat64_option_callback,
721 "Enable NAT64 gateway (socket-based, no TUN device needed)\n");
722/*---------------------------------------------------------------------------*/
723bool
725{
726 return nat64_enabled;
727}
728/*---------------------------------------------------------------------------*/
729/** @} */
static int read(void *buf, unsigned short bufsize)
Definition cc2538-rf.c:779
static volatile uint64_t count
Num.
Definition clock.c:50
#define CONTIKI_OPTION(prio,...)
Add a command line option when the compilation unit is present.
Definition platform.h:153
void nat64_tcp_flush_acks(void)
Flush deferred TCP ACKs.
Definition nat64-tcp.c:660
void nat64_platform_tcp_destroy(struct nat64_session *s)
Fully tear down a TCP session.
Definition nat64-sock.c:576
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:425
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:603
nat64_session_proto
Transport protocol tracked by a NAT64 session.
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
struct nat64_session * nat64_platform_tcp_connect(const uip_ip4addr_t *dst, uint16_t dstport, const uip_ip6addr_t *ip6_src, uint16_t srcport, uint32_t peer_isn)
Initiate a TCP connection to an IPv4 server.
Definition nat64-sock.c:477
bool nat64_tcp_has_pending_data(const struct nat64_session *s)
Check whether a session has buffered data awaiting delivery.
Definition nat64-tcp.c:783
void nat64_tcp_free_seqstate(const struct nat64_session *s)
Free any TCP sequence state associated with a session.
Definition nat64-tcp.c:797
bool nat64_tcp_peer_fin_received(const struct nat64_session *s)
Check whether the IoT node has already half-closed the session.
Definition nat64-tcp.c:790
static void expire_session(struct nat64_session *s)
Reap an expired session, notifying its peer if applicable.
Definition nat64-sock.c:151
void nat64_platform_tcp_close(struct nat64_session *s)
Half-close a TCP session (send FIN).
Definition nat64-sock.c:561
bool nat64_platform_init(void)
Initialize the platform layer.
Definition nat64-sock.c:690
void nat64_platform_tcp_abort(struct nat64_session *s)
Abort a TCP session by sending RST upstream.
Definition nat64-sock.c:586
void nat64_tcp_set_isn_secret(const uint8_t key[16])
Set the 128-bit secret key for TCP ISN generation.
Definition nat64-tcp.c:777
#define NAT64_ICMP6_ADDR
Address unreachable.
Definition nat64.h:171
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:563
void nat64_tcp_closed(struct nat64_session *s)
Notify that an IPv4 server closed a TCP connection.
Definition nat64-tcp.c:746
void nat64_tcp_data_in(struct nat64_session *s, const uint8_t *data, uint16_t len)
Forward TCP data from an IPv4 server to the IoT node.
Definition nat64-tcp.c:718
void nat64_tcp_established(struct nat64_session *s)
Notify that a TCP connection to an IPv4 server completed.
Definition nat64-tcp.c:703
void nat64_activate(void)
Initialize the NAT64 gateway.
Definition nat64.c:683
static uint8_t errno_to_icmp6_code(int err)
Map a Linux errno to an ICMPv6 Destination Unreachable code.
Definition nat64-sock.c:105
#define NAT64_ICMP6_PORT
Port unreachable.
Definition nat64.h:172
int nat64_platform_tcp_send(struct nat64_session *s, const uint8_t *data, uint16_t len)
Send data on an established TCP session.
Definition nat64-sock.c:532
bool nat64_is_enabled(void)
Check whether the NAT64 gateway has been enabled at runtime.
Definition nat64-sock.c:724
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_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:626
#define NAT64_ICMP6_NOROUTE
No route to destination.
Definition nat64.h:169
@ NAT64_TCP_ESTABLISHED
Connection open, data can flow.
@ NAT64_TCP_CONNECTING
Non-blocking connect() in progress.
@ NAT64_TCP_CLOSING
Half-closed (SHUT_WR sent).
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition timer.c:64
bool timer_expired(struct timer *t)
Check if a timer has expired.
Definition timer.c:123
#define uip_ip4addr_cmp(addr1, addr2)
Compare two IP addresses.
Definition uip.h:998
Header file for the logging system.
NAT64 platform interface โ€” socket-based.
NAT64 TCP splice proxy.
NAT64 gateway core API.
Header file for the Contiki-NG main routine.
A NAT64 session binding an IoT node's IPv6 flow to an IPv4 socket.
bool active
Session slot in use.
struct timer expiry
Session lifetime timer.
uip_ip6addr_t ip6_peer
IoT node's IPv6 address.
enum nat64_session_proto proto
UDP or TCP.
uint32_t peer_isn
IoT node's ISN (TCP only).
Representation of an IP address.
Definition uip.h:95