Contiki-NG
Loading...
Searching...
No Matches
nat64-tcp.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 TCP splice proxy.
37 *
38 * Implements per-session sequence-number state, RFC 6528
39 * ISN generation, fabrication of IPv6/TCP segments back to
40 * the IoT node, ACK-paced delivery of server data and
41 * half-close handling. See nat64-tcp.h for the public API
42 * and `os/services/nat64/README.md` for the high-level
43 * design rationale.
44 * \author
45 * Nicolas Tsiftes <nicolas.tsiftes@ri.se>
46 */
47
48#include "nat64-tcp.h"
49#include "nat64.h"
50#include "nat64-platform.h"
51#include "ipv6/ip64-addr.h"
52#include "net/ipv6/tcpip.h"
53#include "lib/sha-256.h"
54
55#include <string.h>
56#include <sys/time.h>
57
58/* Log configuration */
59#include "sys/log.h"
60#define LOG_MODULE "NAT64"
61#define LOG_LEVEL LOG_LEVEL_INFO
62
63#define IPV6_HDRLEN 40
64#define TCP_HDRLEN 20
65#define IP_PROTO_TCP 6
66#define DEFAULT_HOPLIM 64
67
68#define TCP_FIN 0x01
69#define TCP_SYN 0x02
70#define TCP_RST 0x04
71#define TCP_PSH 0x08
72#define TCP_ACK 0x10
73
74
75#ifndef NAT64_MAX_TCP_SESSIONS
76#define NAT64_MAX_TCP_SESSIONS 16
77#endif
78
79/* Maximum TCP payload per injected segment. Sized to fit a single
80 * IEEE 802.15.4 frame after 6LoWPAN IPHC compression + TCP header,
81 * avoiding 6LoWPAN fragmentation on constrained links. */
82#ifndef NAT64_TCP_SEGMENT_SIZE
83#define NAT64_TCP_SEGMENT_SIZE 76
84#endif
85
86/* Per-session buffer for paced server-to-IoT delivery. Must hold at
87 * least one recv() worth of data from the IPv4 socket. */
88#ifndef NAT64_TCP_RXBUF_SIZE
89#define NAT64_TCP_RXBUF_SIZE 1500
90#endif
91
92/* Retransmit timeout for an injected paced segment that has not been
93 * ACKed by the IoT node. Sized for typical 6LoWPAN RTTs (100 ms - 1 s)
94 * with margin; the IoT-facing TCP layer does not generate dup-ACKs for
95 * never-received data, so we rely on this timer to recover from radio
96 * losses. */
97#ifndef NAT64_TCP_RTX_TIMEOUT
98#define NAT64_TCP_RTX_TIMEOUT (3 * CLOCK_SECOND)
99#endif
100
101/* Number of retransmits attempted before declaring the IoT-side TCP
102 * peer unreachable and tearing down the session. */
103#ifndef NAT64_TCP_MAX_RETRIES
104#define NAT64_TCP_MAX_RETRIES 5
105#endif
106
107struct v6hdr {
108 uint8_t vtc, tcflow;
109 uint16_t flow;
110 uint8_t plen[2];
111 uint8_t nexthdr, hoplim;
112 uip_ip6addr_t src, dst;
113};
114
115struct tcphdr {
116 uint16_t sport, dport;
117 uint8_t seqno[4];
118 uint8_t ackno[4];
119 uint8_t offset;
120 uint8_t flags;
121 uint8_t wnd[2];
122 uint16_t tchksum;
123 uint8_t urgp[2];
124};
125
126/*---------------------------------------------------------------------------*/
127static inline uint16_t
128get16(const uint8_t *p)
129{
130 return ((uint16_t)p[0] << 8) | p[1];
131}
132/*---------------------------------------------------------------------------*/
133static inline void
134put16(uint8_t *p, uint16_t v)
135{
136 p[0] = (uint8_t)(v >> 8);
137 p[1] = (uint8_t)v;
138}
139/*---------------------------------------------------------------------------*/
140static inline uint32_t
141get32(const uint8_t *p)
142{
143 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
144 ((uint32_t)p[2] << 8) | p[3];
145}
146/*---------------------------------------------------------------------------*/
147static inline void
148put32(uint8_t *p, uint32_t v)
149{
150 p[0] = (uint8_t)(v >> 24);
151 p[1] = (uint8_t)(v >> 16);
152 p[2] = (uint8_t)(v >> 8);
153 p[3] = (uint8_t)v;
154}
155/*---------------------------------------------------------------------------*/
156static uint32_t
157cksum_acc(uint32_t acc, const void *buf, uint16_t nbytes)
158{
159 const uint8_t *p = buf;
160 while(nbytes > 1) {
161 acc += ((uint16_t)p[0] << 8) | p[1];
162 p += 2;
163 nbytes -= 2;
164 }
165 if(nbytes == 1) {
166 acc += (uint16_t)p[0] << 8;
167 }
168 return acc;
169}
170/*---------------------------------------------------------------------------*/
171static uint16_t
172cksum_fold(uint32_t acc)
173{
174 while(acc >> 16) {
175 acc = (acc & 0xffff) + (acc >> 16);
176 }
177 return ~((uint16_t)acc);
178}
179/*---------------------------------------------------------------------------*/
180static uint16_t
181tcp6_checksum(const struct v6hdr *ip6, const void *tcp, uint16_t tcp_len)
182{
183 uint32_t acc = 0;
184
185 acc = cksum_acc(acc, &ip6->src, sizeof(uip_ip6addr_t));
186 acc = cksum_acc(acc, &ip6->dst, sizeof(uip_ip6addr_t));
187 acc += tcp_len;
188 acc += IP_PROTO_TCP;
189 acc = cksum_acc(acc, tcp, tcp_len);
190
191 uint16_t result = cksum_fold(acc);
192 return (result == 0) ? 0xffff : result;
193}
194
195/*---------------------------------------------------------------------------*/
196/* Per-session TCP sequence number state. */
197/*---------------------------------------------------------------------------*/
198
199struct tcp_seqstate {
200 bool in_use;
201 bool pending_ack;
202 bool peer_fin_received;
203 bool server_fin_pending;
204 struct nat64_session *session;
205 uint32_t our_seq;
206 uint32_t peer_next;
207 /* Paced delivery buffer for server→IoT data. */
208 uint8_t rxbuf[NAT64_TCP_RXBUF_SIZE];
209 uint16_t rxbuf_len;
210 uint16_t rxbuf_offset;
211 /* Retransmit state for the in-flight injected segment. in_flight
212 * is the size of the most recently injected segment that has not
213 * yet been ACKed by the IoT node; while non-zero, rxbuf_offset and
214 * our_seq are NOT advanced, so a retransmit replays the same bytes
215 * with the same sequence number. */
216 uint16_t in_flight;
217 uint8_t rtx_count;
218 struct timer rtx_timer;
219};
220
221static struct tcp_seqstate tcp_seq[NAT64_MAX_TCP_SESSIONS];
222static uint8_t isn_key[16];
223
224static void nat64_tcp_send_pending(struct tcp_seqstate *ts);
225static void nat64_tcp_ack_confirmed(struct tcp_seqstate *ts);
226/*---------------------------------------------------------------------------*/
227static struct tcp_seqstate *
228find_seqstate(const struct nat64_session *s)
229{
230 unsigned i;
231 for(i = 0; i < NAT64_MAX_TCP_SESSIONS; i++) {
232 if(tcp_seq[i].in_use && tcp_seq[i].session == s) {
233 return &tcp_seq[i];
234 }
235 }
236 return NULL;
237}
238/*---------------------------------------------------------------------------*/
239/*
240 * Generate an ISN per RFC 6528:
241 * ISN = M + F(localip, localport, remoteip, remoteport, secretkey)
242 * where M is a monotonic timer (~4 µs granularity) and F is HMAC-SHA-256.
243 *
244 * Note: the practical threat from predictable ISNs is low here — the
245 * IoT-facing TCP runs over a 6LoWPAN mesh where an attacker would need
246 * radio access to inject segments, at which point ISN prediction is
247 * the least concern. We follow RFC 6528 anyway since the cost is
248 * negligible (one HMAC per connection) and it is good practice.
249 */
250static uint32_t
251generate_isn(const struct nat64_session *s)
252{
253 struct {
254 uip_ip6addr_t ip6_peer;
255 uint16_t ip6_peer_port;
256 uip_ip4addr_t ip4_remote;
257 uint16_t ip4_remote_port;
258 } tuple;
259 struct timeval tv;
260 uint8_t digest[SHA_256_DIGEST_LENGTH];
261 uint32_t f;
262
263 memcpy(&tuple.ip6_peer, &s->ip6_peer, sizeof(uip_ip6addr_t));
264 tuple.ip6_peer_port = s->ip6_peer_port;
265 memcpy(&tuple.ip4_remote, &s->ip4_remote, sizeof(uip_ip4addr_t));
266 tuple.ip4_remote_port = s->ip4_remote_port;
267
268 sha_256_hmac(isn_key, sizeof(isn_key),
269 (const uint8_t *)&tuple, sizeof(tuple), digest);
270 memcpy(&f, digest, sizeof(f));
271
272 gettimeofday(&tv, NULL);
273 uint32_t m = (uint32_t)((uint64_t)tv.tv_sec * 250000 + tv.tv_usec / 4);
274
275 return m + f;
276}
277/*---------------------------------------------------------------------------*/
278static struct tcp_seqstate *
279alloc_seqstate(struct nat64_session *s, uint32_t peer_isn)
280{
281 unsigned i;
282 for(i = 0; i < NAT64_MAX_TCP_SESSIONS; i++) {
283 if(!tcp_seq[i].in_use) {
284 tcp_seq[i].in_use = true;
285 tcp_seq[i].pending_ack = false;
286 tcp_seq[i].peer_fin_received = false;
287 tcp_seq[i].server_fin_pending = false;
288 tcp_seq[i].session = s;
289 tcp_seq[i].our_seq = generate_isn(s);
290 tcp_seq[i].peer_next = peer_isn + 1;
291 tcp_seq[i].rxbuf_len = 0;
292 tcp_seq[i].rxbuf_offset = 0;
293 tcp_seq[i].in_flight = 0;
294 tcp_seq[i].rtx_count = 0;
295 return &tcp_seq[i];
296 }
297 }
298 LOG_WARN("TCP sequence state table full\n");
299 return NULL;
300}
301/*---------------------------------------------------------------------------*/
302static struct tcp_seqstate *
303find_seqstate_by_addrs(const uip_ip6addr_t *ip6_peer, uint16_t peer_port,
304 const uip_ip4addr_t *ip4_remote, uint16_t remote_port)
305{
306 unsigned i;
307 for(i = 0; i < NAT64_MAX_TCP_SESSIONS; i++) {
308 struct tcp_seqstate *ts = &tcp_seq[i];
309 if(!ts->in_use || ts->session == NULL) {
310 continue;
311 }
312 struct nat64_session *s = ts->session;
313 if(s->ip6_peer_port == peer_port &&
314 s->ip4_remote_port == remote_port &&
315 uip_ip6addr_cmp(&s->ip6_peer, ip6_peer) &&
317 return ts;
318 }
319 }
320 return NULL;
321}
322
323/*---------------------------------------------------------------------------*/
324/**
325 * \brief Fabricate and inject an IPv6+TCP segment toward the IoT node.
326 * \param s The NAT64 session this segment belongs to.
327 * \param ts Per-session sequence state (provides seq/ack numbers).
328 * \param flags TCP flag byte (combination of TCP_SYN/ACK/FIN/PSH/RST).
329 * \param payload Optional segment payload, or NULL for header-only.
330 * \param payload_len Payload length in bytes, or 0.
331 *
332 * Builds the IPv6 and TCP headers in `uip_buf`, copies the payload,
333 * computes the TCP checksum (with IPv6 pseudo-header) and hands the
334 * resulting packet to ::tcpip_input for delivery up the uIP stack.
335 * The seqstate's sequence/ack counters are NOT advanced here — the
336 * caller is responsible for updating them after the segment is sent.
337 */
338static void
339inject_tcp(const struct nat64_session *s, struct tcp_seqstate *ts,
340 uint8_t flags, const uint8_t *payload, uint16_t payload_len)
341{
342 struct v6hdr *ip6;
343 struct tcphdr *tcp;
344 uint16_t tcp_total;
345
346 tcp_total = TCP_HDRLEN + payload_len;
347 if(IPV6_HDRLEN + tcp_total > UIP_BUFSIZE) {
348 LOG_WARN("inject_tcp: packet too large\n");
349 return;
350 }
351
352 ip6 = (struct v6hdr *)uip_buf;
353 ip6->vtc = 0x60;
354 ip6->tcflow = 0;
355 ip6->flow = 0;
356 put16(ip6->plen, tcp_total);
357 ip6->nexthdr = IP_PROTO_TCP;
358 ip6->hoplim = DEFAULT_HOPLIM;
359
360 ip64_addr_4to6(&s->ip4_remote, &ip6->src);
361 uip_ip6addr_copy(&ip6->dst, &s->ip6_peer);
362
363 tcp = (struct tcphdr *)(uip_buf + IPV6_HDRLEN);
364 tcp->sport = uip_htons(s->ip4_remote_port);
365 tcp->dport = uip_htons(s->ip6_peer_port);
366 put32(tcp->seqno, ts->our_seq);
367 put32(tcp->ackno, ts->peer_next);
368 tcp->offset = (TCP_HDRLEN / 4) << 4;
369 tcp->flags = flags;
370 put16(tcp->wnd, 4096);
371 tcp->tchksum = 0;
372 put16(tcp->urgp, 0);
373
374 if(payload_len > 0) {
375 memcpy(uip_buf + IPV6_HDRLEN + TCP_HDRLEN, payload, payload_len);
376 }
377
378 tcp->tchksum = uip_htons(tcp6_checksum(ip6, tcp, tcp_total));
379
380 uip_len = IPV6_HDRLEN + tcp_total;
381
382 LOG_INFO("inject_tcp: %u bytes, flags=0x%02x seq=%lu ack=%lu\n",
383 uip_len, flags, (unsigned long)ts->our_seq,
384 (unsigned long)ts->peer_next);
385 tcpip_input();
386}
387
388/*---------------------------------------------------------------------------*/
389/* Process outgoing TCP from the IoT node (received at fallback interface). */
390/*---------------------------------------------------------------------------*/
391
392int
393nat64_tcp_output(const uint8_t *pkt, uint16_t len)
394{
395 const struct v6hdr *ip6 = (const struct v6hdr *)pkt;
396 uint16_t payload_len = get16(ip6->plen);
397 const struct tcphdr *tcp;
398 uint16_t data_offset, data_len;
399 uint32_t seq;
400 uip_ip4addr_t dst4;
401
402 if(payload_len < TCP_HDRLEN) {
403 LOG_WARN("tcp_output: payload too short (%u bytes)\n", payload_len);
404 return 0;
405 }
406
407 if(!ip64_addr_6to4(&ip6->dst, &dst4)) {
408 LOG_WARN("tcp_output: destination is not a NAT64 address\n");
409 return 0;
410 }
411
412 tcp = (const struct tcphdr *)(pkt + IPV6_HDRLEN);
413 data_offset = ((tcp->offset >> 4) & 0x0f) * 4;
414 if(data_offset > payload_len) {
415 LOG_WARN("tcp_output: data offset %u exceeds payload %u\n",
416 data_offset, payload_len);
417 return 0;
418 }
419 data_len = payload_len - data_offset;
420 seq = get32(tcp->seqno);
421
422 LOG_INFO("tcp_output: flags=0x%02x data=%u seq=%lu\n",
423 tcp->flags, data_len, (unsigned long)seq);
424
425 if(tcp->flags & TCP_SYN) {
426 LOG_INFO("TCP SYN: port %u -> %u.%u.%u.%u:%u\n",
427 uip_ntohs(tcp->sport),
428 dst4.u8[0], dst4.u8[1], dst4.u8[2], dst4.u8[3],
429 uip_ntohs(tcp->dport));
430
432 &dst4, uip_ntohs(tcp->dport),
433 &ip6->src, uip_ntohs(tcp->sport), seq);
434 return (s != NULL) ? 1 : 0;
435 }
436
437 struct tcp_seqstate *ts = find_seqstate_by_addrs(
438 &ip6->src, uip_ntohs(tcp->sport),
439 &dst4, uip_ntohs(tcp->dport));
440
441 if(ts == NULL) {
442 LOG_WARN("TCP packet for unknown session (flags=0x%02x)\n", tcp->flags);
443 return 0;
444 }
445
446 struct nat64_session *s = ts->session;
447
448 if(tcp->flags & TCP_RST) {
449 LOG_INFO("TCP RST from IoT, aborting session\n");
450 /* Full teardown: the IPv4 socket is closed with SO_LINGER=0 so
451 * the upstream server sees an equivalent RST instead of a
452 * delayed graceful FIN. */
454 return 1;
455 }
456
457 /* ACK from the IoT node: confirm the in-flight paced segment if the
458 * ackno covers its end. Otherwise the segment was lost in flight;
459 * we leave the retransmit timer to recover rather than guessing
460 * from dup-ACK heuristics, since uIP-side TCP does not consistently
461 * dup-ACK in the way classic TCP stacks do. */
462 if(tcp->flags & TCP_ACK) {
463 if(ts->in_flight > 0) {
464 uint32_t ackno = get32(tcp->ackno);
465 uint32_t end_of_inflight = ts->our_seq + ts->in_flight;
466 if((int32_t)(ackno - end_of_inflight) >= 0) {
468 }
469 } else if(ts->rxbuf_len > ts->rxbuf_offset) {
470 /* No segment in flight but data is buffered (e.g., recovery
471 * after retransmit-limit teardown of a sibling state). */
473 }
474 }
475
476 if(data_len > 0) {
477 const uint8_t *data = pkt + IPV6_HDRLEN + data_offset;
478 uint32_t seq_end = seq + (uint32_t)data_len;
479 int32_t gap = (int32_t)(seq - ts->peer_next);
480
481 if(gap > 0) {
482 /* The IoT node skipped ahead in the sequence space — we never
483 * saw the bytes between peer_next and seq. Drop the segment
484 * (including any FIN) so it retransmits from peer_next. */
485 LOG_WARN("TCP out-of-order seq=%lu peer_next=%lu, dropping\n",
486 (unsigned long)seq, (unsigned long)ts->peer_next);
487 ts->pending_ack = true;
488 return 1;
489 }
490
491 if((int32_t)(seq_end - ts->peer_next) <= 0) {
492 /* Pure retransmit: every byte was already forwarded to the IPv4
493 * server. Re-ACK so the IoT node stops resending, but do not
494 * forward the duplicate payload — that would corrupt the
495 * server-side stream. */
496 LOG_DBG("TCP retransmit seq=%lu len=%u (already forwarded)\n",
497 (unsigned long)seq, data_len);
498 ts->pending_ack = true;
499 } else {
500 /* Partial overlap: skip the prefix that was already forwarded
501 * and send only the new tail. */
502 uint32_t skip = ts->peer_next - seq;
503 const uint8_t *new_data = data + skip;
504 uint16_t new_len = data_len - (uint16_t)skip;
505
506 LOG_INFO("TCP forwarding %u bytes to IPv4 server%s\n",
507 new_len, skip > 0 ? " (skipped retransmitted prefix)" : "");
508 int sent = nat64_platform_tcp_send(s, new_data, new_len);
509 if(sent < 0) {
510 LOG_ERR("TCP send failed, aborting session\n");
512 return 1;
513 }
514 ts->peer_next += (uint32_t)sent;
515 ts->pending_ack = true;
516 if((uint32_t)sent < new_len) {
517 /* Short write — only ACK what was forwarded. Don't process
518 * FIN yet; the IoT node will retransmit the remaining data. */
519 return 1;
520 }
521 }
522 }
523
524 if(tcp->flags & TCP_FIN) {
525 if(!ts->peer_fin_received) {
526 LOG_INFO("TCP FIN from IoT node (half-close)\n");
527 ts->peer_next++;
528 ts->peer_fin_received = true;
529 /* Forward the half-close to the IPv4 server (SHUT_WR), but
530 * keep the read side open: server->IoT data can still arrive
531 * and must be delivered. Our own FIN toward the IoT node is
532 * deferred until nat64_tcp_closed() fires when the IPv4 server
533 * eventually closes its end. */
535 ts->pending_ack = true;
536
537 if(s->tcp_state == NAT64_TCP_CLOSING) {
538 /* Server already closed and we already injected our FIN;
539 * receiving the IoT-side FIN means both halves are done.
540 * Tear down the session now rather than waiting for the
541 * idle timer. */
542 LOG_INFO("TCP both sides FIN'd, destroying session\n");
544 return 1;
545 }
546 } else {
547 LOG_DBG("TCP duplicate FIN from IoT node (already half-closed)\n");
548 }
549 }
550
551 return 1;
552}
553
554/*---------------------------------------------------------------------------*/
555/**
556 * \brief Inject the next paced chunk from a session's receive buffer.
557 * \param ts The sequence state whose rxbuf has data to deliver.
558 *
559 * Sends up to ::NAT64_TCP_SEGMENT_SIZE bytes per call, fitting one
560 * 802.15.4 frame after 6LoWPAN compression. Stop-and-wait: only one
561 * segment is in flight at a time, with the retransmit timer armed for
562 * recovery if the IoT node never ACKs (e.g., radio loss). The
563 * sequence number and rxbuf offset are NOT advanced here — that
564 * happens in ::nat64_tcp_ack_confirmed once the ACK arrives.
565 */
566static void
567nat64_tcp_send_pending(struct tcp_seqstate *ts)
568{
569 uint16_t remaining;
570 uint16_t chunk;
571
572 if(ts->in_flight > 0) {
573 /* A previous segment is still awaiting ACK or retransmit. */
574 return;
575 }
576
577 remaining = ts->rxbuf_len - ts->rxbuf_offset;
578 if(remaining == 0) {
579 return;
580 }
581
582 chunk = remaining > NAT64_TCP_SEGMENT_SIZE
583 ? NAT64_TCP_SEGMENT_SIZE : remaining;
584
585 LOG_INFO("TCP paced: %u/%u bytes -> IoT node\n", chunk, remaining);
586 inject_tcp(ts->session, ts, TCP_PSH | TCP_ACK,
587 ts->rxbuf + ts->rxbuf_offset, chunk);
588 ts->in_flight = chunk;
589 ts->rtx_count = 0;
590 timer_set(&ts->rtx_timer, NAT64_TCP_RTX_TIMEOUT);
591}
592/*---------------------------------------------------------------------------*/
593/**
594 * \brief Promote the in-flight segment to acknowledged and queue what's next.
595 * \param ts The sequence state whose latest segment has been ACKed.
596 *
597 * Advances rxbuf_offset and our_seq past the now-acknowledged bytes,
598 * clears the retransmit state, and either sends the next chunk, emits
599 * a previously-deferred server FIN, or leaves the session idle.
600 */
601static void
602nat64_tcp_ack_confirmed(struct tcp_seqstate *ts)
603{
604 ts->our_seq += ts->in_flight;
605 ts->rxbuf_offset += ts->in_flight;
606 ts->in_flight = 0;
607 ts->rtx_count = 0;
608
609 if(ts->rxbuf_offset >= ts->rxbuf_len) {
610 ts->rxbuf_len = 0;
611 ts->rxbuf_offset = 0;
612 if(ts->server_fin_pending) {
613 ts->server_fin_pending = false;
614 LOG_INFO("TCP deferred FIN: sending now\n");
615 inject_tcp(ts->session, ts, TCP_FIN | TCP_ACK, NULL, 0);
616 ts->our_seq++;
617 }
618 return;
619 }
620
622}
623
624/*---------------------------------------------------------------------------*/
625/* Flush deferred ACKs and send paced data. Called from the select loop. */
626/*---------------------------------------------------------------------------*/
627
628void
630{
631 unsigned i;
632 for(i = 0; i < NAT64_MAX_TCP_SESSIONS; i++) {
633 struct tcp_seqstate *ts = &tcp_seq[i];
634 if(!ts->in_use || ts->session == NULL) {
635 continue;
636 }
637
638 /* Retransmit a paced segment that the IoT node never ACKed. The
639 * IoT-facing TCP layer does not dup-ACK for never-received data,
640 * so we recover from radio losses purely on this timer. */
641 if(ts->in_flight > 0 && timer_expired(&ts->rtx_timer)) {
642 if(++ts->rtx_count > NAT64_TCP_MAX_RETRIES) {
643 LOG_ERR("TCP retransmit limit reached, aborting session\n");
644 nat64_platform_tcp_abort(ts->session);
645 continue;
646 }
647 LOG_WARN("TCP retransmit %u/%u (%u bytes)\n",
648 ts->rtx_count, NAT64_TCP_MAX_RETRIES, ts->in_flight);
649 inject_tcp(ts->session, ts, TCP_PSH | TCP_ACK,
650 ts->rxbuf + ts->rxbuf_offset, ts->in_flight);
651 timer_reset(&ts->rtx_timer);
652 }
653
654 if(ts->pending_ack) {
655 ts->pending_ack = false;
656 /* Pure ACK: never bundle our own FIN here, even after a peer
657 * FIN. Our FIN toward the IoT node is emitted by
658 * nat64_tcp_closed() when the IPv4 server closes its end.
659 * Bundling FIN with this ACK would break TCP half-close
660 * semantics by actively closing the IoT-facing side as part
661 * of ACK processing. */
662 inject_tcp(ts->session, ts, TCP_ACK, NULL, 0);
663 }
664 }
665}
666
667/*---------------------------------------------------------------------------*/
668/* Callbacks from the platform layer. */
669/*---------------------------------------------------------------------------*/
670
671void
673{
674 struct tcp_seqstate *ts = alloc_seqstate(s, s->peer_isn);
675 if(ts == NULL) {
676 LOG_ERR("TCP seqstate table full, closing connection\n");
678 return;
679 }
680
681 LOG_INFO("TCP established: sending SYN-ACK\n");
682 inject_tcp(s, ts, TCP_SYN | TCP_ACK, NULL, 0);
683 ts->our_seq++;
684}
685/*---------------------------------------------------------------------------*/
686void
688 const uint8_t *data, uint16_t len)
689{
690 struct tcp_seqstate *ts = find_seqstate(s);
691 if(ts == NULL) {
692 LOG_WARN("tcp_data_in: no sequence state\n");
693 return;
694 }
695
696 if(ts->rxbuf_len > 0) {
697 LOG_WARN("tcp_data_in: buffer busy, dropping %u bytes\n", len);
698 return;
699 }
700
701 if(len > NAT64_TCP_RXBUF_SIZE) {
702 len = NAT64_TCP_RXBUF_SIZE;
703 }
704
705 memcpy(ts->rxbuf, data, len);
706 ts->rxbuf_len = len;
707 ts->rxbuf_offset = 0;
708
709 /* Send the first chunk immediately; the rest will be paced by
710 * nat64_tcp_send_pending() as ACKs arrive from the IoT node. */
712}
713/*---------------------------------------------------------------------------*/
714void
716{
717 struct tcp_seqstate *ts = find_seqstate(s);
718 if(ts == NULL) {
719 return;
720 }
721
722 if(ts->rxbuf_len > ts->rxbuf_offset) {
723 /* Data still buffered — defer FIN until the buffer drains. */
724 LOG_INFO("TCP remote closed: deferring FIN (%u bytes pending)\n",
725 ts->rxbuf_len - ts->rxbuf_offset);
726 ts->server_fin_pending = true;
727 return;
728 }
729
730 LOG_INFO("TCP remote closed: sending FIN to IoT node\n");
731 inject_tcp(s, ts, TCP_FIN | TCP_ACK, NULL, 0);
732 ts->our_seq++;
733 /* Keep ts->in_use = true so we can handle the FIN-ACK from the IoT
734 * node. The seqstate is freed when we see the peer's FIN-ACK or RST
735 * in nat64_tcp_output(), or by nat64_tcp_free_seqstate() when the
736 * platform layer closes the session. */
737}
738/*---------------------------------------------------------------------------*/
739void
741{
742 memset(tcp_seq, 0, sizeof(tcp_seq));
743}
744/*---------------------------------------------------------------------------*/
745void
746nat64_tcp_set_isn_secret(const uint8_t key[16])
747{
748 memcpy(isn_key, key, 16);
749}
750/*---------------------------------------------------------------------------*/
751bool
753{
754 struct tcp_seqstate *ts = find_seqstate(s);
755 return ts != NULL && ts->rxbuf_len > ts->rxbuf_offset;
756}
757/*---------------------------------------------------------------------------*/
758bool
760{
761 struct tcp_seqstate *ts = find_seqstate(s);
762 return ts != NULL && ts->peer_fin_received;
763}
764/*---------------------------------------------------------------------------*/
765void
767{
768 struct tcp_seqstate *ts = find_seqstate(s);
769 if(ts != NULL) {
770 ts->rxbuf_len = 0;
771 ts->rxbuf_offset = 0;
772 ts->in_flight = 0;
773 ts->rtx_count = 0;
774 ts->in_use = false;
775 ts->session = NULL;
776 }
777}
778/*---------------------------------------------------------------------------*/
779/** @} */
static volatile at86rf215_flags_t flags
The radio driver uses the following flags to keep track of the current state of the radio and IRQ eve...
Definition at86rf215.c:144
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
static void nat64_tcp_send_pending(struct tcp_seqstate *ts)
Inject the next paced chunk from a session's receive buffer.
Definition nat64-tcp.c:567
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
static void nat64_tcp_ack_confirmed(struct tcp_seqstate *ts)
Promote the in-flight segment to acknowledged and queue what's next.
Definition nat64-tcp.c:602
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
void nat64_platform_tcp_close(struct nat64_session *s)
Half-close a TCP session (send FIN).
Definition nat64-sock.c:554
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
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
static void inject_tcp(const struct nat64_session *s, struct tcp_seqstate *ts, uint8_t flags, const uint8_t *payload, uint16_t payload_len)
Fabricate and inject an IPv6+TCP segment toward the IoT node.
Definition nat64-tcp.c:339
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
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_tcp_init(void)
Initialize the TCP splice proxy.
Definition nat64-tcp.c:740
@ NAT64_TCP_CLOSING
Half-closed (SHUT_WR sent).
void tcpip_input(void)
Deliver an incoming packet to the TCP/IP stack.
Definition tcpip.c:433
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
void timer_reset(struct timer *t)
Reset the timer with the same interval.
Definition timer.c:84
#define uip_ip4addr_cmp(addr1, addr2)
Compare two IP addresses.
Definition uip.h:998
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 platform interface — socket-based.
NAT64 TCP splice proxy.
NAT64 gateway core API.
Platform-independent SHA-256 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.
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.
A timer.
Definition timer.h:84
Header for the Contiki/uIP interface.
Representation of an IP address.
Definition uip.h:95