blob: 3c4462292ade7b1af3ae2c263158503e76a53317 [file] [log] [blame]
/*
* Copyright (c) 2018-2019 Intel Corporation
* Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tp.h"
#define is(_a, _b) (strcmp((_a), (_b)) == 0)
#ifndef MIN3
#define MIN3(_a, _b, _c) MIN((_a), MIN((_b), (_c)))
#endif
#define th_sport(_x) UNALIGNED_GET(&(_x)->th_sport)
#define th_dport(_x) UNALIGNED_GET(&(_x)->th_dport)
#define th_seq(_x) ntohl(UNALIGNED_GET(&(_x)->th_seq))
#define th_ack(_x) ntohl(UNALIGNED_GET(&(_x)->th_ack))
#define th_off(_x) ((_x)->th_off)
#define th_flags(_x) UNALIGNED_GET(&(_x)->th_flags)
#define th_win(_x) UNALIGNED_GET(&(_x)->th_win)
#define tcp_slist(_conn, _slist, _op, _type, _link) \
({ \
k_mutex_lock(&_conn->lock, K_FOREVER); \
\
sys_snode_t *_node = sys_slist_##_op(_slist); \
\
_type * _x = _node ? CONTAINER_OF(_node, _type, _link) : NULL; \
\
k_mutex_unlock(&_conn->lock); \
\
_x; \
})
#if defined(CONFIG_NET_TEST_PROTOCOL)
#define tcp_malloc(_size) \
tp_malloc(_size, tp_basename(__FILE__), __LINE__, __func__)
#define tcp_calloc(_nmemb, _size) \
tp_calloc(_nmemb, _size, tp_basename(__FILE__), __LINE__, __func__)
#define tcp_free(_ptr) tp_free(_ptr, tp_basename(__FILE__), __LINE__, __func__)
#else
#define tcp_malloc(_size) k_malloc(_size)
#define tcp_calloc(_nmemb, _size) k_calloc(_nmemb, _size)
#define tcp_free(_ptr) k_free(_ptr)
#endif
#define TCP_PKT_ALLOC_TIMEOUT K_MSEC(CONFIG_NET_TCP_PKT_ALLOC_TIMEOUT)
#if defined(CONFIG_NET_TEST_PROTOCOL)
#define tcp_pkt_clone(_pkt) tp_pkt_clone(_pkt, tp_basename(__FILE__), __LINE__)
#define tcp_pkt_unref(_pkt) tp_pkt_unref(_pkt, tp_basename(__FILE__), __LINE__)
#else
#define tcp_pkt_clone(_pkt) net_pkt_clone(_pkt, TCP_PKT_ALLOC_TIMEOUT)
#define tcp_pkt_unref(_pkt) net_pkt_unref(_pkt)
#define tp_pkt_alloc(args...)
#endif
#define tcp_pkt_ref(_pkt) net_pkt_ref(_pkt)
#define tcp_pkt_alloc(_conn, _len) \
({ \
struct net_pkt *_pkt; \
\
if ((_len) > 0) { \
_pkt = net_pkt_alloc_with_buffer( \
(_conn)->iface, \
(_len), \
net_context_get_family((_conn)->context), \
IPPROTO_TCP, \
TCP_PKT_ALLOC_TIMEOUT); \
} else { \
_pkt = net_pkt_alloc(TCP_PKT_ALLOC_TIMEOUT); \
} \
\
tp_pkt_alloc(_pkt, tp_basename(__FILE__), __LINE__); \
\
_pkt; \
})
#define tcp_rx_pkt_alloc(_conn, _len) \
({ \
struct net_pkt *_pkt; \
\
if ((_len) > 0) { \
_pkt = net_pkt_rx_alloc_with_buffer( \
(_conn)->iface, \
(_len), \
net_context_get_family((_conn)->context), \
IPPROTO_TCP, \
TCP_PKT_ALLOC_TIMEOUT); \
} else { \
_pkt = net_pkt_rx_alloc(TCP_PKT_ALLOC_TIMEOUT); \
} \
\
tp_pkt_alloc(_pkt, tp_basename(__FILE__), __LINE__); \
\
_pkt; \
})
#define tcp_pkt_alloc_no_conn(_iface, _family, _len) \
({ \
struct net_pkt *_pkt; \
\
if ((_len) > 0) { \
_pkt = net_pkt_alloc_with_buffer( \
(_iface), (_len), (_family), \
IPPROTO_TCP, \
TCP_PKT_ALLOC_TIMEOUT); \
} else { \
_pkt = net_pkt_alloc(TCP_PKT_ALLOC_TIMEOUT); \
} \
\
tp_pkt_alloc(_pkt, tp_basename(__FILE__), __LINE__); \
\
_pkt; \
})
#if defined(CONFIG_NET_TEST_PROTOCOL)
#define conn_seq(_conn, _req) \
tp_seq_track(TP_SEQ, &(_conn)->seq, (_req), tp_basename(__FILE__), \
__LINE__, __func__)
#define conn_ack(_conn, _req) \
tp_seq_track(TP_ACK, &(_conn)->ack, (_req), tp_basename(__FILE__), \
__LINE__, __func__)
#else
#define conn_seq(_conn, _req) (_conn)->seq += (_req)
#define conn_ack(_conn, _req) (_conn)->ack += (_req)
#endif
#define NET_TCP_DEFAULT_MSS 536
#define conn_mss(_conn) \
MIN((_conn)->recv_options.mss_found ? (_conn)->recv_options.mss \
: NET_TCP_DEFAULT_MSS, \
net_tcp_get_supported_mss(_conn))
#define conn_state(_conn, _s) \
({ \
NET_DBG("%s->%s", \
tcp_state_to_str((_conn)->state, false), \
tcp_state_to_str((_s), false)); \
(_conn)->state = _s; \
})
#define conn_send_data_dump(_conn) \
({ \
NET_DBG("conn: %p total=%zd, unacked_len=%d, " \
"send_win=%hu, mss=%hu", \
(_conn), net_pkt_get_len((_conn)->send_data), \
_conn->unacked_len, _conn->send_win, \
(uint16_t)conn_mss((_conn))); \
NET_DBG("conn: %p send_data_timer=%hu, send_data_retries=%hu", \
(_conn), \
(bool)k_ticks_to_ms_ceil32( \
k_work_delayable_remaining_get( \
&(_conn)->send_data_timer)), \
(_conn)->send_data_retries); \
})
enum pkt_addr {
TCP_EP_SRC = 1,
TCP_EP_DST = 0
};
struct tcphdr {
uint16_t th_sport;
uint16_t th_dport;
uint32_t th_seq;
uint32_t th_ack;
#ifdef CONFIG_LITTLE_ENDIAN
uint8_t th_x2:4; /* unused */
uint8_t th_off:4; /* data offset, in units of 32-bit words */
#else
uint8_t th_off:4;
uint8_t th_x2:4;
#endif
uint8_t th_flags;
uint16_t th_win;
uint16_t th_sum;
uint16_t th_urp;
} __packed;
enum th_flags {
FIN = BIT(0),
SYN = BIT(1),
RST = BIT(2),
PSH = BIT(3),
ACK = BIT(4),
URG = BIT(5),
ECN = BIT(6),
CWR = BIT(7),
};
struct tcp_mss_option {
uint32_t option;
};
enum tcp_state {
TCP_UNUSED = 0,
TCP_LISTEN,
TCP_SYN_SENT,
TCP_SYN_RECEIVED,
TCP_ESTABLISHED,
TCP_FIN_WAIT_1,
TCP_FIN_WAIT_2,
TCP_CLOSE_WAIT,
TCP_CLOSING,
TCP_LAST_ACK,
TCP_TIME_WAIT,
TCP_CLOSED
};
enum tcp_data_mode {
TCP_DATA_MODE_SEND = 0,
TCP_DATA_MODE_RESEND = 1
};
union tcp_endpoint {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
/* TCP Option codes */
#define NET_TCP_END_OPT 0
#define NET_TCP_NOP_OPT 1
#define NET_TCP_MSS_OPT 2
#define NET_TCP_WINDOW_SCALE_OPT 3
/* TCP Option sizes */
#define NET_TCP_END_SIZE 1
#define NET_TCP_NOP_SIZE 1
#define NET_TCP_MSS_SIZE 4
#define NET_TCP_WINDOW_SCALE_SIZE 3
struct tcp_options {
uint16_t mss;
uint16_t window;
bool mss_found : 1;
bool wnd_found : 1;
};
#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
struct tcp_collision_avoidance_reno {
uint16_t cwnd;
uint16_t ssthresh;
uint16_t pending_fast_retransmit_bytes;
};
#endif
struct tcp;
typedef void (*net_tcp_closed_cb_t)(struct tcp *conn, void *user_data);
struct tcp { /* TCP connection */
sys_snode_t next;
struct net_context *context;
struct net_pkt *send_data;
struct net_pkt *queue_recv_data;
struct net_if *iface;
void *recv_user_data;
sys_slist_t send_queue;
union {
net_tcp_accept_cb_t accept_cb;
struct tcp *accepted_conn;
};
net_context_connect_cb_t connect_cb;
#if defined(CONFIG_NET_TEST)
net_tcp_closed_cb_t test_closed_cb;
void *test_user_data;
#endif
struct k_mutex lock;
struct k_sem connect_sem; /* semaphore for blocking connect */
struct k_sem tx_sem; /* Semaphore indicating if transfers are blocked . */
struct k_fifo recv_data; /* temp queue before passing data to app */
struct tcp_options recv_options;
struct tcp_options send_options;
struct k_work_delayable send_timer;
struct k_work_delayable recv_queue_timer;
struct k_work_delayable send_data_timer;
struct k_work_delayable timewait_timer;
struct k_work_delayable persist_timer;
struct k_work_delayable ack_timer;
#if defined(CONFIG_NET_TCP_KEEPALIVE)
struct k_work_delayable keepalive_timer;
#endif /* CONFIG_NET_TCP_KEEPALIVE */
struct k_work conn_release;
union {
/* Because FIN and establish timers are never happening
* at the same time, share the timer between them to
* save memory.
*/
struct k_work_delayable fin_timer;
struct k_work_delayable establish_timer;
};
union tcp_endpoint src;
union tcp_endpoint dst;
#if defined(CONFIG_NET_TCP_IPV6_ND_REACHABILITY_HINT)
int64_t last_nd_hint_time;
#endif
size_t send_data_total;
size_t send_retries;
int unacked_len;
atomic_t ref_count;
enum tcp_state state;
enum tcp_data_mode data_mode;
uint32_t seq;
uint32_t ack;
#if defined(CONFIG_NET_TCP_KEEPALIVE)
uint32_t keep_idle;
uint32_t keep_intvl;
uint32_t keep_cnt;
uint32_t keep_cur;
#endif /* CONFIG_NET_TCP_KEEPALIVE */
uint16_t recv_win_max;
uint16_t recv_win;
uint16_t send_win_max;
uint16_t send_win;
#ifdef CONFIG_NET_TCP_RANDOMIZED_RTO
uint16_t rto;
#endif
#ifdef CONFIG_NET_TCP_CONGESTION_AVOIDANCE
struct tcp_collision_avoidance_reno ca;
#endif
uint8_t send_data_retries;
#ifdef CONFIG_NET_TCP_FAST_RETRANSMIT
uint8_t dup_ack_cnt;
#endif
uint8_t zwp_retries;
bool in_retransmission : 1;
bool in_connect : 1;
bool in_close : 1;
#if defined(CONFIG_NET_TCP_KEEPALIVE)
bool keep_alive : 1;
#endif /* CONFIG_NET_TCP_KEEPALIVE */
bool tcp_nodelay : 1;
};
#define _flags(_fl, _op, _mask, _cond) \
({ \
bool result = false; \
\
if (UNALIGNED_GET(_fl) && (_cond) && \
(UNALIGNED_GET(_fl) _op(_mask))) { \
UNALIGNED_PUT(UNALIGNED_GET(_fl) & ~(_mask), _fl); \
result = true; \
} \
\
result; \
})
#define FL(_fl, _op, _mask, _args...) \
_flags(_fl, _op, _mask, sizeof(#_args) > 1 ? _args : true)
typedef void (*net_tcp_cb_t)(struct tcp *conn, void *user_data);
#if defined(CONFIG_NET_TEST)
void tcp_install_close_cb(struct net_context *ctx,
net_tcp_closed_cb_t cb,
void *user_data);
#endif