You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
8.9 KiB
253 lines
8.9 KiB
// lwip_tcp_priv.h — lwIP TCP internal API |
|
#ifndef LWIP_TCP_PRIV_H |
|
#define LWIP_TCP_PRIV_H |
|
|
|
#include "lwip_tcp.h" |
|
#include "lwip_tcp_opts.h" |
|
#include <string.h> |
|
|
|
// Packed struct support |
|
#ifndef PACK_STRUCT_BEGIN |
|
#define PACK_STRUCT_BEGIN |
|
#endif |
|
#ifndef PACK_STRUCT_END |
|
#define PACK_STRUCT_END |
|
#endif |
|
#ifndef PACK_STRUCT_FIELD |
|
#define PACK_STRUCT_FIELD(x) x |
|
#endif |
|
#ifndef PACK_STRUCT_STRUCT |
|
#if defined(__GNUC__) || defined(__clang__) |
|
#define PACK_STRUCT_STRUCT __attribute__((packed)) |
|
#else |
|
#define PACK_STRUCT_STRUCT |
|
#endif |
|
#endif |
|
|
|
#ifndef _WIN32 |
|
#include <arpa/inet.h> |
|
#else |
|
#define htons(x) ((((x) & 0xFF) << 8) | (((x) >> 8) & 0xFF)) |
|
#define ntohs(x) htons(x) |
|
#define htonl(x) ((((x) & 0xFF) << 24) | (((x) & 0xFF00) << 8) | (((x) >> 8) & 0xFF00) | (((x) >> 24) & 0xFF)) |
|
#define ntohl(x) htonl(x) |
|
#endif |
|
|
|
// TCP header (wire format) |
|
PACK_STRUCT_BEGIN |
|
struct tcp_hdr { |
|
PACK_STRUCT_FIELD(uint16_t src); |
|
PACK_STRUCT_FIELD(uint16_t dest); |
|
PACK_STRUCT_FIELD(uint32_t seqno); |
|
PACK_STRUCT_FIELD(uint32_t ackno); |
|
PACK_STRUCT_FIELD(uint16_t _hdrlen_rsvd_flags); |
|
PACK_STRUCT_FIELD(uint16_t wnd); |
|
PACK_STRUCT_FIELD(uint16_t chksum); |
|
PACK_STRUCT_FIELD(uint16_t urgp); |
|
} PACK_STRUCT_STRUCT; |
|
PACK_STRUCT_END |
|
|
|
#define TCP_FIN 0x01U |
|
#define TCP_SYN 0x02U |
|
#define TCP_RST 0x04U |
|
#define TCP_PSH 0x08U |
|
#define TCP_ACK 0x10U |
|
#define TCP_URG 0x20U |
|
#define TCP_ECE 0x40U |
|
#define TCP_CWR 0x80U |
|
#define TCP_FLAGS 0x3fU |
|
|
|
#define TCPH_HDRLEN(phdr) ((uint16_t)(ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)) |
|
#define TCPH_HDRLEN_BYTES(phdr) ((uint8_t)(TCPH_HDRLEN(phdr) << 2)) |
|
#define TCPH_FLAGS(phdr) ((uint8_t)(ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)) |
|
|
|
#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr)) |
|
#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & htons((uint16_t)~TCP_FLAGS)) | htons(flags)) |
|
#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons((uint16_t)(((uint16_t)(len) << 12) | (flags))) |
|
|
|
#define TCPH_SET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags)) |
|
#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags & ~htons(flags)) |
|
|
|
// TCP header length |
|
#define TCP_HLEN 20 |
|
|
|
// Sequence number macros |
|
#define TCP_SEQ_LT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) < 0) |
|
#define TCP_SEQ_LEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) <= 0) |
|
#define TCP_SEQ_GT(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) > 0) |
|
#define TCP_SEQ_GEQ(a,b) ((int32_t)((uint32_t)(a) - (uint32_t)(b)) >= 0) |
|
#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c)) |
|
|
|
// TCP options |
|
#define TCP_OPT_END 0 |
|
#define TCP_OPT_NOP 1 |
|
#define TCP_OPT_MSS 2 |
|
#define TCP_OPT_MSS_LEN 4 |
|
|
|
// tcp_seg flags |
|
#define TF_SEG_OPTS_MSS 0x01U |
|
|
|
// tcp_seg structure |
|
struct tcp_seg { |
|
struct tcp_seg *next; |
|
struct pbuf *p; |
|
uint16_t len; |
|
uint8_t flags; |
|
struct tcp_hdr *tcphdr; |
|
}; |
|
|
|
#define TCP_TCPLEN(seg) ((seg)->len + (((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0) ? 1U : 0U)) |
|
|
|
// Window macros |
|
#define TCPWND16(x) (x) |
|
#define TCP_WND_MAX(pcb) TCP_WND |
|
#define RCV_WND_SCALE(pcb, wnd) (wnd) |
|
#define SND_WND_SCALE(pcb, wnd) (wnd) |
|
#define TCP_WND_INC(wnd, inc) do { \ |
|
if ((tcpwnd_size_t)(wnd + inc) >= wnd) { wnd = (tcpwnd_size_t)(wnd + inc); } \ |
|
else { wnd = (tcpwnd_size_t)-1; } \ |
|
} while(0) |
|
|
|
// TCP_STATE_IS_CLOSING |
|
#define TCP_STATE_IS_CLOSING(state) ((state) >= FIN_WAIT_1) |
|
|
|
// Nagle |
|
#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \ |
|
((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \ |
|
(((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \ |
|
((tpcb)->unsent->len >= (tpcb)->mss))) || \ |
|
(tcp_sndbuf(tpcb) == 0 || tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) ? 1 : 0) |
|
#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : LERR_OK) |
|
|
|
// Callback event macros |
|
#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) \ |
|
do { if((lpcb)->accept != NULL) (ret) = (lpcb)->accept((arg),(pcb),(err)); else (ret) = LERR_ARG; } while(0) |
|
|
|
#define TCP_EVENT_SENT(pcb,space,ret) \ |
|
do { if((pcb)->sent != NULL) (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); else (ret) = LERR_OK; } while(0) |
|
|
|
#define TCP_EVENT_RECV(pcb,p,err,ret) \ |
|
do { if((pcb)->recv != NULL) { (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); } \ |
|
else { (ret) = LERR_OK; if(p) pbuf_free(p); } } while(0) |
|
|
|
#define TCP_EVENT_CLOSED(pcb,ret) \ |
|
do { if(((pcb)->recv != NULL)) { (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,LERR_OK); } \ |
|
else { (ret) = LERR_OK; } } while(0) |
|
|
|
#define TCP_EVENT_CONNECTED(pcb,err,ret) \ |
|
do { if((pcb)->connected != NULL) (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); else (ret) = LERR_OK; } while(0) |
|
|
|
#define TCP_EVENT_POLL(pcb,ret) \ |
|
do { if((pcb)->poll != NULL) (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); else (ret) = LERR_OK; } while(0) |
|
|
|
#define TCP_EVENT_ERR(last_state,errf,arg,err) \ |
|
do { (void)(last_state); if((errf) != NULL) (errf)((arg),(err)); } while(0) |
|
|
|
// tcp_ack / tcp_ack_now |
|
#define tcp_ack(pcb) do { \ |
|
if((pcb)->flags & TF_ACK_DELAY) { tcp_clear_flags(pcb, TF_ACK_DELAY); tcp_ack_now(pcb); } \ |
|
else { tcp_set_flags(pcb, TF_ACK_DELAY); } \ |
|
} while(0) |
|
|
|
#define tcp_ack_now(pcb) tcp_set_flags(pcb, TF_ACK_NOW) |
|
|
|
// TCP timer intervals |
|
#define TCP_TMR_INTERVAL_MS TCP_TMR_INTERVAL |
|
#define TCP_FAST_INTERVAL_MS TCP_FAST_INTERVAL |
|
#define TCP_SLOW_INTERVAL_MS TCP_SLOW_INTERVAL |
|
#define TCP_FIN_WAIT_TIMEOUT_MS TCP_FIN_WAIT_TIMEOUT |
|
#define TCP_SYN_RCVD_TIMEOUT_MS TCP_SYN_RCVD_TIMEOUT |
|
#define TCP_MSL_MS TCP_MSL |
|
|
|
// build MSS option |
|
#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) |
|
|
|
// IP header |
|
PACK_STRUCT_BEGIN |
|
struct ip_hdr { |
|
uint8_t vhl; |
|
uint8_t tos; |
|
uint16_t len; |
|
uint16_t id; |
|
uint16_t offset; |
|
uint8_t ttl; |
|
uint8_t proto; |
|
uint16_t chksum; |
|
uint32_t src; |
|
uint32_t dst; |
|
} PACK_STRUCT_STRUCT; |
|
PACK_STRUCT_END |
|
|
|
#define IP_PROTO_TCP 6 |
|
|
|
// TCP backoff arrays |
|
extern const uint8_t tcp_backoff[13]; |
|
extern const uint8_t tcp_persist_backoff[7]; |
|
|
|
// Internal functions |
|
err_t tcp_send_empty_ack(struct tcp_pcb *pcb); |
|
err_t tcp_send_fin(struct tcp_pcb *pcb); |
|
err_t tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags); |
|
void tcp_rst(struct tcp_pcb *pcb, uint32_t seqno, uint32_t ackno, |
|
uint32_t local_ip, uint32_t remote_ip, |
|
uint16_t local_port, uint16_t remote_port); |
|
err_t tcp_rexmit(struct tcp_pcb *pcb); |
|
err_t tcp_rexmit_rto_prepare(struct tcp_pcb *pcb); |
|
void tcp_rexmit_rto_commit(struct tcp_pcb *pcb); |
|
void tcp_rexmit_rto(struct tcp_pcb *pcb); |
|
void tcp_rexmit_fast(struct tcp_pcb *pcb); |
|
void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); |
|
err_t tcp_keepalive(struct tcp_pcb *pcb); |
|
err_t tcp_zero_window_probe(struct tcp_pcb *pcb); |
|
err_t tcp_split_unsent_seg(struct tcp_pcb *pcb, uint16_t split); |
|
void tcp_trigger_input_pcb_close(void); |
|
err_t tcp_process_refused_data(struct tcp_pcb *pcb); |
|
|
|
// Segment management |
|
struct tcp_pcb* tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio); |
|
void tcp_free(struct tcp_pcb *pcb); |
|
void tcp_pcb_purge(struct tcp_pcb *pcb); |
|
void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); |
|
void tcp_segs_free(struct tcp_seg *seg); |
|
void tcp_seg_free(struct tcp_seg *seg); |
|
struct tcp_seg* tcp_seg_copy(struct tcp_seg *seg); |
|
void tcp_free_ooseq(struct tcp_pcb *pcb); |
|
|
|
// Timer internal |
|
void tcp_fasttmr(struct lwip_tcp_ctx *ctx); |
|
void tcp_slowtmr(struct lwip_tcp_ctx *ctx); |
|
void tcp_txnow(struct lwip_tcp_ctx *ctx); |
|
|
|
// Utils |
|
uint32_t tcp_next_iss(struct tcp_pcb *pcb); |
|
uint16_t tcp_checksum(const void *data, uint16_t len); |
|
uint16_t tcp_ip_checksum(const struct ip_hdr *iph); |
|
|
|
// Trace hook — set by test to capture internal TCP events (ring buffer) |
|
typedef void (*tcp_trace_fn)(struct lwip_tcp_ctx *ctx, char event, uint32_t seq, uint16_t len, uint16_t wnd, const char *msg); |
|
extern tcp_trace_fn g_tcp_trace; |
|
|
|
void lwip_tcp_trace_record(struct lwip_tcp_ctx *ctx, char event, uint32_t seq, |
|
uint16_t len, uint16_t wnd, uint16_t rto, int16_t rtime, uint8_t state); |
|
void lwip_tcp_trace_dump(struct lwip_tcp_ctx *ctx); |
|
void lwip_tcp_trace_clear(struct lwip_tcp_ctx *ctx); |
|
void lwip_tcp_stats_dump(struct lwip_tcp_ctx *ctx); |
|
void lwip_tcp_stats_clear(struct lwip_tcp_ctx *ctx); |
|
|
|
// PCB list management |
|
#define TCP_REG(pcbs, npcb) do { \ |
|
(npcb)->next_owner = PCB_NEXT_REG; \ |
|
(npcb)->next = *(pcbs); *(pcbs) = (npcb); \ |
|
} while(0) |
|
|
|
#define TCP_RMV(pcbs, npcb) do { \ |
|
if(*(pcbs) == (npcb)) { *(pcbs) = (*pcbs)->next; } \ |
|
else { struct tcp_pcb *_tmp; for(_tmp = *(pcbs); _tmp != NULL; _tmp = _tmp->next) { if(_tmp->next == (npcb)) { _tmp->next_owner = PCB_NEXT_SLOWTMR; _tmp->next = (npcb)->next; break; } } } \ |
|
(npcb)->next = NULL; \ |
|
(npcb)->next_owner = PCB_NEXT_RMV; \ |
|
} while(0) |
|
|
|
#define TCP_REG_ACTIVE(ctx, npcb) TCP_REG(&(ctx)->active_pcbs, npcb) |
|
#define TCP_RMV_ACTIVE(ctx, npcb) TCP_RMV(&(ctx)->active_pcbs, npcb) |
|
|
|
#endif
|
|
|