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];
84static bool nat64_enabled;
85
86/*---------------------------------------------------------------------------*/
87/**
88 * \brief Map a Linux errno to an ICMPv6 Destination Unreachable code.
89 * \param err A `errno` value reported by a connect()/send()/recv()
90 * failure on an IPv4 socket.
91 * \return One of the NAT64_ICMP6_* codes (RFC 4443 §3.1).
92 *
93 * Used by the platform layer to translate socket-level failures into
94 * the ICMPv6 errors returned to the IoT node via
95 * ::nat64_queue_icmp6_unreach_tuple. Unrecognized errors fall back
96 * to "no route to destination".
97 */
98static uint8_t
100{
101 switch(err) {
102 case ECONNREFUSED:
103 return NAT64_ICMP6_PORT;
104 case EHOSTUNREACH:
105 case ETIMEDOUT:
106 return NAT64_ICMP6_ADDR;
107 case ENETUNREACH:
108 return NAT64_ICMP6_NOROUTE;
109 case EACCES:
110 case EPERM:
111 return NAT64_ICMP6_ADMIN;
112 default:
113 return NAT64_ICMP6_NOROUTE;
114 }
115}
116
117/*---------------------------------------------------------------------------*/
118/* Session helpers. */
119/*---------------------------------------------------------------------------*/
120
121static void
122close_session(struct nat64_session *s)
123{
124 if(s->proto == NAT64_PROTO_TCP) {
126 }
127 if(s->fd >= 0) {
128 select_set_callback(s->fd, NULL);
129 close(s->fd);
130 s->fd = -1;
131 }
132 s->active = false;
133 s->proto = NAT64_PROTO_NONE;
134}
135/*---------------------------------------------------------------------------*/
136/**
137 * \brief Reap an expired session, notifying its peer if applicable.
138 * \param s The session whose expiry timer has fired.
139 *
140 * For ESTABLISHED TCP sessions this synthesizes a FIN toward the IoT
141 * node before tearing down, so the IoT-side TCP layer doesn't keep a
142 * zombie connection until its own keepalive fires.
143 */
144static void
146{
147 if(s->proto == NAT64_PROTO_TCP &&
150 }
151 close_session(s);
152}
153/*---------------------------------------------------------------------------*/
154static struct nat64_session *
155find_session(enum nat64_session_proto proto,
156 const uip_ip6addr_t *ip6_src, uint16_t srcport,
157 const uip_ip4addr_t *dst, uint16_t dstport)
158{
159 unsigned i;
160 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
161 struct nat64_session *s = &sessions[i];
162 if(s->active &&
163 s->proto == proto &&
164 s->ip6_peer_port == srcport &&
165 s->ip4_remote_port == dstport &&
166 uip_ip6addr_cmp(&s->ip6_peer, ip6_src) &&
167 uip_ip4addr_cmp(&s->ip4_remote, dst)) {
168 if(timer_expired(&s->expiry)) {
170 return NULL;
171 }
172 return s;
173 }
174 }
175 return NULL;
176}
177/*---------------------------------------------------------------------------*/
178static unsigned
179count_node_sessions(const uip_ip6addr_t *ip6_src)
180{
181 unsigned i, count = 0;
182 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
183 if(sessions[i].active &&
184 !timer_expired(&sessions[i].expiry) &&
185 uip_ip6addr_cmp(&sessions[i].ip6_peer, ip6_src)) {
186 count++;
187 }
188 }
189 return count;
190}
191/*---------------------------------------------------------------------------*/
192static struct nat64_session *
193alloc_session(const uip_ip6addr_t *ip6_src)
194{
195 unsigned i;
196
197 if(count_node_sessions(ip6_src) >= NAT64_MAX_SESSIONS_PER_NODE) {
198 LOG_WARN("Per-node session limit reached (%u)\n",
199 NAT64_MAX_SESSIONS_PER_NODE);
200 return NULL;
201 }
202
203 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
204 if(!sessions[i].active) {
205 return &sessions[i];
206 }
207 if(timer_expired(&sessions[i].expiry)) {
208 expire_session(&sessions[i]);
209 return &sessions[i];
210 }
211 }
212 LOG_WARN("Session table full\n");
213 return NULL;
214}
215/*---------------------------------------------------------------------------*/
216static void
217fill_session(struct nat64_session *s, enum nat64_session_proto proto,
218 const uip_ip6addr_t *ip6_src, uint16_t srcport,
219 const uip_ip4addr_t *dst, uint16_t dstport)
220{
221 s->proto = proto;
222 uip_ip6addr_copy(&s->ip6_peer, ip6_src);
223 s->ip6_peer_port = srcport;
224 memcpy(&s->ip4_remote, dst, sizeof(uip_ip4addr_t));
225 s->ip4_remote_port = dstport;
226 s->active = true;
227 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
228}
229/*---------------------------------------------------------------------------*/
230static bool
231register_fd(struct nat64_session *s);
232
233static struct sockaddr_in
234make_addr(const uip_ip4addr_t *ip, uint16_t port)
235{
236 struct sockaddr_in sa;
237 memset(&sa, 0, sizeof(sa));
238 sa.sin_family = AF_INET;
239 sa.sin_port = htons(port);
240 memcpy(&sa.sin_addr, ip, sizeof(uip_ip4addr_t));
241 return sa;
242}
243
244/*---------------------------------------------------------------------------*/
245/* Select callbacks. */
246/*---------------------------------------------------------------------------*/
247
248static void
249handle_tcp_connect_complete(struct nat64_session *s)
250{
251 int err = 0;
252 socklen_t errlen = sizeof(err);
253
254 if(getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0 || err != 0) {
255 int e = err ? err : errno;
256 LOG_WARN("TCP connect failed: %s\n", strerror(e));
259 IPPROTO_TCP, errno_to_icmp6_code(e));
260 close_session(s);
261 return;
262 }
263
264 LOG_INFO("TCP connected to %u.%u.%u.%u:%u (fd %d)\n",
265 s->ip4_remote.u8[0], s->ip4_remote.u8[1],
266 s->ip4_remote.u8[2], s->ip4_remote.u8[3],
267 s->ip4_remote_port, s->fd);
268
271}
272/*---------------------------------------------------------------------------*/
273static int
274generic_set_fd(fd_set *rset, fd_set *wset)
275{
276 unsigned i;
277
278 /* Flush deferred TCP ACKs and any pending ICMPv6 errors. set_fd is
279 * called on every main-loop iteration, so this ensures ACKs and
280 * errors are delivered promptly even when select() times out with no
281 * ready fds. */
284
285 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
286 struct nat64_session *s = &sessions[i];
287 if(!s->active || s->fd < 0) {
288 continue;
289 }
290 if(s->proto == NAT64_PROTO_TCP &&
292 FD_SET(s->fd, wset);
293 } else if(s->proto == NAT64_PROTO_TCP &&
295 /* Don't read more data while the previous chunk is still being
296 * paced to the IoT node — this provides back-pressure. */
297 } else {
298 FD_SET(s->fd, rset);
299 }
300 }
301 return 1;
302}
303/*---------------------------------------------------------------------------*/
304static void
305generic_handle_fd(fd_set *rset, fd_set *wset)
306{
307 unsigned i;
308 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
309 struct nat64_session *s = &sessions[i];
310 if(!s->active || s->fd < 0) {
311 continue;
312 }
313
314 if(timer_expired(&s->expiry)) {
316 continue;
317 }
318
319 if(s->proto == NAT64_PROTO_TCP &&
321 FD_ISSET(s->fd, wset)) {
322 handle_tcp_connect_complete(s);
323 continue;
324 }
325
326 if(!FD_ISSET(s->fd, rset)) {
327 continue;
328 }
329
330 if(s->proto == NAT64_PROTO_TCP &&
332 uint8_t buf[1500];
333 ssize_t n = recv(s->fd, buf, sizeof(buf), 0);
334 if(n > 0) {
335 LOG_INFO("TCP recv %zd bytes from server (fd %d)\n", n, s->fd);
336 nat64_tcp_data_in(s, buf, (uint16_t)n);
337 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
338 } else if(n == 0) {
339 LOG_INFO("TCP server closed connection (fd %d)\n", s->fd);
343 /* IoT side already FIN'd — both halves done, reap now. */
344 LOG_INFO("TCP both sides FIN'd, destroying session\n");
345 close_session(s);
346 }
347 } else if(errno != EAGAIN && errno != EWOULDBLOCK) {
348 LOG_ERR("TCP recv error (fd %d): %s\n", s->fd, strerror(errno));
352 LOG_INFO("TCP both sides done, destroying session\n");
353 close_session(s);
354 }
355 }
356 } else if(s->proto == NAT64_PROTO_UDP) {
357 uint8_t buf[1500];
358 ssize_t n = recv(s->fd, buf, sizeof(buf), 0);
359 if(n > 0) {
360 nat64_udp_input(s, buf, (uint16_t)n);
361 } else if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
362 LOG_ERR("UDP recvfrom error (fd %d): %s\n", s->fd, strerror(errno));
363 }
364 } else if(s->proto == NAT64_PROTO_ICMP) {
365 uint8_t buf[256];
366 ssize_t n = recv(s->fd, buf, sizeof(buf), 0);
367 if(n > 0) {
368 nat64_icmp_input(s, buf, (uint16_t)n);
369 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
370 } else if(n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
371 LOG_ERR("ICMP recv error (fd %d): %s\n", s->fd, strerror(errno));
372 }
373 }
374 }
375}
376/*---------------------------------------------------------------------------*/
377static const struct select_callback nat64_select_cb = {
378 generic_set_fd,
379 generic_handle_fd,
380};
381/*---------------------------------------------------------------------------*/
382static bool
383register_fd(struct nat64_session *s)
384{
385 if(fcntl(s->fd, F_SETFL, O_NONBLOCK) < 0) {
386 /* If the socket stays blocking, a single slow IPv4 server can
387 * stall the entire main loop on the next send/recv. Refuse the
388 * session rather than risk that. */
389 LOG_ERR("fcntl(F_SETFL, O_NONBLOCK) failed for fd %d: %s\n",
390 s->fd, strerror(errno));
391 close(s->fd);
392 s->fd = -1;
393 s->active = false;
394 return false;
395 }
396 if(!select_set_callback(s->fd, &nat64_select_cb)) {
397 LOG_ERR("select_set_callback failed for fd %d\n", s->fd);
398 close(s->fd);
399 s->fd = -1;
400 s->active = false;
401 return false;
402 }
403 return true;
404}
405
406/*---------------------------------------------------------------------------*/
407/* Platform API. */
408/*---------------------------------------------------------------------------*/
409
410int
411nat64_platform_udp_send(const uip_ip4addr_t *dst, uint16_t dstport,
412 const uip_ip6addr_t *ip6_src, uint16_t srcport,
413 const uint8_t *payload, uint16_t len)
414{
415 struct nat64_session *s;
416 ssize_t sent;
417
418 s = find_session(NAT64_PROTO_UDP, ip6_src, srcport, dst, dstport);
419 if(s == NULL) {
420 s = alloc_session(ip6_src);
421 if(s == NULL) {
422 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
423 IPPROTO_UDP, NAT64_ICMP6_ADMIN);
424 return -1;
425 }
426 s->fd = socket(AF_INET, SOCK_DGRAM, 0);
427 if(s->fd < 0) {
428 LOG_ERR("socket(DGRAM): %s\n", strerror(errno));
429 return -1;
430 }
431 fill_session(s, NAT64_PROTO_UDP, ip6_src, srcport, dst, dstport);
432 if(!register_fd(s)) {
433 return -1;
434 }
435 /* Connect the UDP socket so the kernel filters incoming packets
436 * by source address, preventing spoofed responses. */
437 struct sockaddr_in peer = make_addr(dst, dstport);
438 if(connect(s->fd, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
439 int e = errno;
440 LOG_ERR("UDP connect: %s\n", strerror(e));
441 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
442 IPPROTO_UDP, errno_to_icmp6_code(e));
443 close_session(s);
444 return -1;
445 }
446 LOG_DBG("New UDP session fd %d\n", s->fd);
447 }
448
449 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
450
451 sent = send(s->fd, payload, len, 0);
452 if(sent < 0) {
453 int e = errno;
454 LOG_ERR("sendto: %s\n", strerror(e));
455 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
456 IPPROTO_UDP, errno_to_icmp6_code(e));
457 return -1;
458 }
459 return (int)sent;
460}
461/*---------------------------------------------------------------------------*/
462struct nat64_session *
463nat64_platform_tcp_connect(const uip_ip4addr_t *dst, uint16_t dstport,
464 const uip_ip6addr_t *ip6_src, uint16_t srcport,
465 uint32_t peer_isn)
466{
467 struct nat64_session *s;
468 int ret;
469
470 s = find_session(NAT64_PROTO_TCP, ip6_src, srcport, dst, dstport);
471 if(s != NULL) {
472 return s;
473 }
474
475 s = alloc_session(ip6_src);
476 if(s == NULL) {
477 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
478 IPPROTO_TCP, NAT64_ICMP6_ADMIN);
479 return NULL;
480 }
481
482 s->fd = socket(AF_INET, SOCK_STREAM, 0);
483 if(s->fd < 0) {
484 LOG_ERR("socket(STREAM): %s\n", strerror(errno));
485 return NULL;
486 }
487
488 fill_session(s, NAT64_PROTO_TCP, ip6_src, srcport, dst, dstport);
489 s->peer_isn = peer_isn;
491
492 if(!register_fd(s)) {
493 return NULL;
494 }
495
496 struct sockaddr_in dest = make_addr(dst, dstport);
497 ret = connect(s->fd, (struct sockaddr *)&dest, sizeof(dest));
498 if(ret < 0 && errno != EINPROGRESS) {
499 int e = errno;
500 LOG_ERR("connect: %s\n", strerror(e));
501 nat64_queue_icmp6_unreach_tuple(ip6_src, srcport, dst, dstport,
502 IPPROTO_TCP, errno_to_icmp6_code(e));
503 close_session(s);
504 return NULL;
505 }
506
507 if(ret == 0) {
508 handle_tcp_connect_complete(s);
509 }
510
511 LOG_DBG("TCP connecting fd %d to %u.%u.%u.%u:%u\n",
512 s->fd,
513 dst->u8[0], dst->u8[1], dst->u8[2], dst->u8[3], dstport);
514 return s;
515}
516/*---------------------------------------------------------------------------*/
517int
519 const uint8_t *data, uint16_t len)
520{
521 ssize_t sent;
522
523 if(s == NULL || s->tcp_state != NAT64_TCP_ESTABLISHED) {
524 return -1;
525 }
526
527 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
528
529 sent = send(s->fd, data, len, 0);
530 if(sent < 0) {
531 if(errno == EAGAIN || errno == EWOULDBLOCK) {
532 /* Kernel send buffer full. Returning 0 leaves peer_next un-
533 * advanced in nat64_tcp_output(), so the IoT-side TCP layer
534 * sees no ACK progress and retransmits on its RTO. We don't
535 * register for write-readiness here because we don't buffer
536 * the IoT-side payload locally — the source of truth for the
537 * unsent bytes is the IoT TCP send queue, not us, so polling
538 * for writability would have nothing to flush. Sustained
539 * EAGAIN under 6LoWPAN throughput is essentially unreachable
540 * (kernel buffers >>> radio bandwidth); log it and rely on
541 * IoT retransmits for recovery. */
542 LOG_WARN("TCP send would block (fd %d), IoT will retransmit\n",
543 s->fd);
544 return 0;
545 }
546 LOG_ERR("TCP send error (fd %d): %s\n", s->fd, strerror(errno));
547 return -1;
548 }
549 LOG_INFO("TCP sent %zd bytes to server (fd %d)\n", sent, s->fd);
550 return (int)sent;
551}
552/*---------------------------------------------------------------------------*/
553void
555{
556 if(s == NULL) {
557 return;
558 }
559 LOG_DBG("TCP shutdown(WR) fd %d\n", s->fd);
560 /* Half-close: signal EOF to the IPv4 server but keep the read side
561 * open so any remaining server->IoT data can still be delivered.
562 * The session transitions to CLOSING only when the server itself
563 * closes (recv() returns 0 in generic_handle_fd) or when an explicit
564 * teardown occurs. */
565 shutdown(s->fd, SHUT_WR);
566}
567/*---------------------------------------------------------------------------*/
568void
570{
571 if(s == NULL) {
572 return;
573 }
574 LOG_DBG("TCP destroy fd %d\n", s->fd);
575 close_session(s);
576}
577/*---------------------------------------------------------------------------*/
578void
580{
581 if(s == NULL) {
582 return;
583 }
584 if(s->fd >= 0) {
585 /* SO_LINGER with l_linger=0 makes the subsequent close() emit a
586 * TCP RST instead of a graceful FIN, so the upstream server sees
587 * the connection abort directly. */
588 struct linger lin = { .l_onoff = 1, .l_linger = 0 };
589 setsockopt(s->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(lin));
590 }
591 LOG_DBG("TCP abort fd %d\n", s->fd);
592 close_session(s);
593}
594/*---------------------------------------------------------------------------*/
595int
597 const uip_ip6addr_t *ip6_src, uint16_t identifier,
598 const uint8_t *icmp_pkt, uint16_t icmp_len)
599{
600 struct nat64_session *s;
601 ssize_t sent;
602
603 /* Sessions are keyed on (ip6_src, identifier, dst, 0). ip4_remote_port
604 * is unused for ICMP and stored as 0; ip6_peer_port stores the
605 * ICMPv6 Echo identifier. */
606 s = find_session(NAT64_PROTO_ICMP, ip6_src, identifier, dst, 0);
607 if(s == NULL) {
608 s = alloc_session(ip6_src);
609 if(s == NULL) {
610 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
611 IPPROTO_ICMPV6, NAT64_ICMP6_ADMIN);
612 return -1;
613 }
614
615 /* Open an unprivileged ICMP socket. Requires either CAP_NET_RAW
616 * or the running GID to be in net.ipv4.ping_group_range. */
617 s->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
618 if(s->fd < 0) {
619 int e = errno;
620 LOG_ERR("socket(ICMP): %s\n", strerror(e));
621 if(e == EACCES || e == EPERM) {
622 LOG_ERR("Hint: add the running user's GID to "
623 "net.ipv4.ping_group_range\n");
624 }
625 s->active = false;
626 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
627 IPPROTO_ICMPV6,
629 return -1;
630 }
631
632 fill_session(s, NAT64_PROTO_ICMP, ip6_src, identifier, dst, 0);
633
634 if(!register_fd(s)) {
635 return -1;
636 }
637
638 /* Connect so the kernel filters ICMP replies by source address. */
639 struct sockaddr_in peer = make_addr(dst, 0);
640 if(connect(s->fd, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
641 int e = errno;
642 LOG_ERR("ICMP connect: %s\n", strerror(e));
643 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
644 IPPROTO_ICMPV6,
646 close_session(s);
647 return -1;
648 }
649 LOG_DBG("New ICMP session fd %d id=%u\n", s->fd, identifier);
650 }
651
652 timer_set(&s->expiry, NAT64_SESSION_TIMEOUT);
653
654 sent = send(s->fd, icmp_pkt, icmp_len, 0);
655 if(sent < 0) {
656 int e = errno;
657 LOG_ERR("ICMP send: %s\n", strerror(e));
658 nat64_queue_icmp6_unreach_tuple(ip6_src, identifier, dst, 0,
659 IPPROTO_ICMPV6, errno_to_icmp6_code(e));
660 return -1;
661 }
662 return (int)sent;
663}
664/*---------------------------------------------------------------------------*/
665static bool
666read_urandom(void *buf, size_t len)
667{
668 int fd = open("/dev/urandom", O_RDONLY);
669 if(fd < 0) {
670 LOG_ERR("Failed to open /dev/urandom: %s\n", strerror(errno));
671 return false;
672 }
673 ssize_t n = read(fd, buf, len);
674 close(fd);
675 if(n != (ssize_t)len) {
676 LOG_ERR("Short read from /dev/urandom\n");
677 return false;
678 }
679 return true;
680}
681/*---------------------------------------------------------------------------*/
682bool
684{
685 unsigned i;
686 uint8_t isn_key[16];
687
688 memset(sessions, 0, sizeof(sessions));
689 for(i = 0; i < NAT64_MAX_SESSIONS; i++) {
690 sessions[i].fd = -1;
691 }
692
693 if(!read_urandom(isn_key, sizeof(isn_key))) {
694 LOG_ERR("Cannot seed ISN secret — /dev/urandom unavailable\n");
695 return false;
696 }
698 memset(isn_key, 0, sizeof(isn_key));
699
701 LOG_INFO("Socket-based NAT64 initialized (%u max sessions)\n",
702 NAT64_MAX_SESSIONS);
703 return true;
704}
705/*---------------------------------------------------------------------------*/
706static int
707nat64_option_callback(const char *optarg)
708{
709 nat64_enabled = true;
710 return 0;
711}
712CONTIKI_OPTION(NAT64_PRIO, { "nat64", no_argument, NULL, 0 },
713 nat64_option_callback,
714 "Enable NAT64 gateway (socket-based, no TUN device needed)\n");
715/*---------------------------------------------------------------------------*/
716bool
718{
719 return nat64_enabled;
720}
721/*---------------------------------------------------------------------------*/
722/** @} */
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:629
void nat64_platform_tcp_destroy(struct nat64_session *s)
Fully tear down a TCP session.
Definition nat64-sock.c:569
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
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
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:463
bool nat64_tcp_has_pending_data(const struct nat64_session *s)
Check whether a session has buffered data awaiting delivery.
Definition nat64-tcp.c:752
void nat64_tcp_free_seqstate(const struct nat64_session *s)
Free any TCP sequence state associated with a session.
Definition nat64-tcp.c:766
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:759
static void expire_session(struct nat64_session *s)
Reap an expired session, notifying its peer if applicable.
Definition nat64-sock.c:145
void nat64_platform_tcp_close(struct nat64_session *s)
Half-close a TCP session (send FIN).
Definition nat64-sock.c:554
bool nat64_platform_init(void)
Initialize the platform layer.
Definition nat64-sock.c:683
void nat64_platform_tcp_abort(struct nat64_session *s)
Abort a TCP session by sending RST upstream.
Definition nat64-sock.c:579
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:746
#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:567
void nat64_tcp_closed(struct nat64_session *s)
Notify that an IPv4 server closed a TCP connection.
Definition nat64-tcp.c:715
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:687
void nat64_tcp_established(struct nat64_session *s)
Notify that a TCP connection to an IPv4 server completed.
Definition nat64-tcp.c:672
void nat64_activate(void)
Initialize the NAT64 gateway.
Definition nat64.c:687
static uint8_t errno_to_icmp6_code(int err)
Map a Linux errno to an ICMPv6 Destination Unreachable code.
Definition nat64-sock.c:99
#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:518
bool nat64_is_enabled(void)
Check whether the NAT64 gateway has been enabled at runtime.
Definition nat64-sock.c:717
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:630
#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.
uint16_t ip4_remote_port
IPv4 server port.
struct timer expiry
Session lifetime timer.
int fd
IPv4 socket file descriptor.
uip_ip6addr_t ip6_peer
IoT node's IPv6 address.
enum nat64_session_proto proto
UDP or TCP.
enum nat64_tcp_state tcp_state
TCP connection state.
uint32_t peer_isn
IoT node's ISN (TCP only).
uip_ip4addr_t ip4_remote
IPv4 server address.
uint16_t ip6_peer_port
IoT node's transport port.
Representation of an IP address.
Definition uip.h:95