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