📄 tcp.c
字号:
/**
* Allocate a new tcp_pcb structure.
*
* @param prio priority for the new pcb
* @return a new tcp_pcb that initially is in state CLOSED
*/
struct tcp_pcb *
tcp_alloc(u8_t prio)
{
struct tcp_pcb *pcb;
u32_t iss;
pcb = memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing oldest connection in TIME-WAIT. */
LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
tcp_kill_timewait();
/* Try to allocate a tcp_pcb again. */
pcb = memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing active connections with lower priority than the new one. */
tcp_kill_prio(prio);
/* Try to allocate a tcp_pcb again. */
pcb = memp_malloc(MEMP_TCP_PCB);
}
}
if (pcb != NULL) {
memset(pcb, 0, sizeof(struct tcp_pcb));
pcb->prio = TCP_PRIO_NORMAL;
pcb->snd_buf = TCP_SND_BUF;
pcb->snd_queuelen = 0;
pcb->rcv_wnd = TCP_WND;
pcb->rcv_ann_wnd = TCP_WND;
pcb->tos = 0;
pcb->ttl = TCP_TTL;
/* As initial send MSS, we use TCP_MSS but limit it to 536.
The send MSS is updated when an MSS option is received. */
pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
pcb->rto = 3000 / TCP_SLOW_INTERVAL;
pcb->sa = 0;
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = -1;
pcb->cwnd = 1;
iss = tcp_next_iss();
pcb->snd_wl2 = iss;
pcb->snd_nxt = iss;
pcb->lastack = iss;
pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
pcb->polltmr = 0;
#if LWIP_CALLBACK_API
pcb->recv = tcp_recv_null;
#endif /* LWIP_CALLBACK_API */
/* Init KEEPALIVE timer */
pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
#if LWIP_TCP_KEEPALIVE
pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
#endif /* LWIP_TCP_KEEPALIVE */
pcb->keep_cnt_sent = 0;
}
return pcb;
}
/**
* Creates a new TCP protocol control block but doesn't place it on
* any of the TCP PCB lists.
* The pcb is not put on any list until binding using tcp_bind().
*
* @internal: Maybe there should be a idle TCP PCB list where these
* PCBs are put on. Port reservation using tcp_bind() is implemented but
* allocated pcbs that are not bound can't be killed automatically if wanting
* to allocate a pcb with higher prio (@see tcp_kill_prio())
*
* @return a new tcp_pcb that initially is in state CLOSED
*/
struct tcp_pcb *
tcp_new(void)
{
return tcp_alloc(TCP_PRIO_NORMAL);
}
/**
* Used to specify the argument that should be passed callback
* functions.
*
* @param pcb tcp_pcb to set the callback argument
* @param arg void pointer argument to pass to callback functions
*/
void
tcp_arg(struct tcp_pcb *pcb, void *arg)
{
pcb->callback_arg = arg;
}
#if LWIP_CALLBACK_API
/**
* Used to specify the function that should be called when a TCP
* connection receives data.
*
* @param pcb tcp_pcb to set the recv callback
* @param recv callback function to call for this pcb when data is received
*/
void
tcp_recv(struct tcp_pcb *pcb,
err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err))
{
pcb->recv = recv;
}
/**
* Used to specify the function that should be called when TCP data
* has been successfully delivered to the remote host.
*
* @param pcb tcp_pcb to set the sent callback
* @param sent callback function to call for this pcb when data is successfully sent
*/
void
tcp_sent(struct tcp_pcb *pcb,
err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len))
{
pcb->sent = sent;
}
/**
* Used to specify the function that should be called when a fatal error
* has occured on the connection.
*
* @param pcb tcp_pcb to set the err callback
* @param errf callback function to call for this pcb when a fatal error
* has occured on the connection
*/
void
tcp_err(struct tcp_pcb *pcb,
void (* errf)(void *arg, err_t err))
{
pcb->errf = errf;
}
/**
* Used for specifying the function that should be called when a
* LISTENing connection has been connected to another host.
*
* @param pcb tcp_pcb to set the accept callback
* @param accept callback function to call for this pcb when LISTENing
* connection has been connected to another host
*/
void
tcp_accept(struct tcp_pcb *pcb,
err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err))
{
pcb->accept = accept;
}
#endif /* LWIP_CALLBACK_API */
/**
* Used to specify the function that should be called periodically
* from TCP. The interval is specified in terms of the TCP coarse
* timer interval, which is called twice a second.
*
*/
void
tcp_poll(struct tcp_pcb *pcb,
err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval)
{
#if LWIP_CALLBACK_API
pcb->poll = poll;
#endif /* LWIP_CALLBACK_API */
pcb->pollinterval = interval;
}
/**
* Purges a TCP PCB. Removes any buffered data and frees the buffer memory
* (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
*
* @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
*/
void
tcp_pcb_purge(struct tcp_pcb *pcb)
{
if (pcb->state != CLOSED &&
pcb->state != TIME_WAIT &&
pcb->state != LISTEN) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
#if TCP_LISTEN_BACKLOG
if (pcb->state == SYN_RCVD) {
/* Need to find the corresponding listen_pcb and decrease its accepts_pending */
struct tcp_pcb_listen *lpcb;
LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
tcp_listen_pcbs.listen_pcbs != NULL);
for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if ((lpcb->local_port == pcb->local_port) &&
(ip_addr_isany(&lpcb->local_ip) ||
ip_addr_cmp(&pcb->local_ip, &lpcb->local_ip))) {
/* port and address of the listen pcb match the timed-out pcb */
LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
lpcb->accepts_pending > 0);
lpcb->accepts_pending--;
break;
}
}
}
#endif /* TCP_LISTEN_BACKLOG */
if (pcb->refused_data != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
pbuf_free(pcb->refused_data);
pcb->refused_data = NULL;
}
if (pcb->unsent != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
}
if (pcb->unacked != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
}
#if TCP_QUEUE_OOSEQ /* LW */
if (pcb->ooseq != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
}
/* Stop the retransmission timer as it will expect data on unacked
queue if it fires */
pcb->rtime = -1;
tcp_segs_free(pcb->ooseq);
pcb->ooseq = NULL;
#endif /* TCP_QUEUE_OOSEQ */
tcp_segs_free(pcb->unsent);
tcp_segs_free(pcb->unacked);
pcb->unacked = pcb->unsent = NULL;
}
}
/**
* Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
*
* @param pcblist PCB list to purge.
* @param pcb tcp_pcb to purge. The pcb itself is also deallocated!
*/
void
tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
{
TCP_RMV(pcblist, pcb);
tcp_pcb_purge(pcb);
/* if there is an outstanding delayed ACKs, send it */
if (pcb->state != TIME_WAIT &&
pcb->state != LISTEN &&
pcb->flags & TF_ACK_DELAY) {
pcb->flags |= TF_ACK_NOW;
tcp_output(pcb);
}
if (pcb->state != LISTEN) {
LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
#if TCP_QUEUE_OOSEQ
LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
#endif /* TCP_QUEUE_OOSEQ */
}
pcb->state = CLOSED;
LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
}
/**
* Calculates a new initial sequence number for new connections.
*
* @return u32_t pseudo random sequence number
*/
u32_t
tcp_next_iss(void)
{
static u32_t iss = 6510;
iss += tcp_ticks; /* XXX */
return iss;
}
#if TCP_CALCULATE_EFF_SEND_MSS
/**
* Calcluates the effective send mss that can be used for a specific IP address
* by using ip_route to determin the netif used to send to the address and
* calculating the minimum of TCP_MSS and that netif's mtu (if set).
*/
u16_t
tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr)
{
u16_t mss_s;
struct netif *outif;
outif = ip_route(addr);
if ((outif != NULL) && (outif->mtu != 0)) {
mss_s = outif->mtu - IP_HLEN - TCP_HLEN;
/* RFC 1122, chap 4.2.2.6:
* Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
* We correct for TCP options in tcp_enqueue(), and don't support
* IP options
*/
sendmss = LWIP_MIN(sendmss, mss_s);
}
return sendmss;
}
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
/**
* Print a tcp header for debugging purposes.
*
* @param tcphdr pointer to a struct tcp_hdr
*/
void
tcp_debug_print(struct tcp_hdr *tcphdr)
{
LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
ntohs(tcphdr->src), ntohs(tcphdr->dest)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
ntohl(tcphdr->seqno)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
ntohl(tcphdr->ackno)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (",
TCPH_HDRLEN(tcphdr),
TCPH_FLAGS(tcphdr) >> 5 & 1,
TCPH_FLAGS(tcphdr) >> 4 & 1,
TCPH_FLAGS(tcphdr) >> 3 & 1,
TCPH_FLAGS(tcphdr) >> 2 & 1,
TCPH_FLAGS(tcphdr) >> 1 & 1,
TCPH_FLAGS(tcphdr) & 1,
ntohs(tcphdr->wnd)));
tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n",
ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
}
/**
* Print a tcp state for debugging purposes.
*
* @param s enum tcp_state to print
*/
void
tcp_debug_print_state(enum tcp_state s)
{
LWIP_DEBUGF(TCP_DEBUG, ("State: "));
switch (s) {
case CLOSED:
LWIP_DEBUGF(TCP_DEBUG, ("CLOSED\n"));
break;
case LISTEN:
LWIP_DEBUGF(TCP_DEBUG, ("LISTEN\n"));
break;
case SYN_SENT:
LWIP_DEBUGF(TCP_DEBUG, ("SYN_SENT\n"));
break;
case SYN_RCVD:
LWIP_DEBUGF(TCP_DEBUG, ("SYN_RCVD\n"));
break;
case ESTABLISHED:
LWIP_DEBUGF(TCP_DEBUG, ("ESTABLISHED\n"));
break;
case FIN_WAIT_1:
LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_1\n"));
break;
case FIN_WAIT_2:
LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_2\n"));
break;
case CLOSE_WAIT:
LWIP_DEBUGF(TCP_DEBUG, ("CLOSE_WAIT\n"));
break;
case CLOSING:
LWIP_DEBUGF(TCP_DEBUG, ("CLOSING\n"));
break;
case LAST_ACK:
LWIP_DEBUGF(TCP_DEBUG, ("LAST_ACK\n"));
break;
case TIME_WAIT:
LWIP_DEBUGF(TCP_DEBUG, ("TIME_WAIT\n"));
break;
}
}
/**
* Print tcp flags for debugging purposes.
*
* @param flags tcp flags, all active flags are printed
*/
void
tcp_debug_print_flags(u8_t flags)
{
if (flags & TCP_FIN) {
LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
}
if (flags & TCP_SYN) {
LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
}
if (flags & TCP_RST) {
LWIP_DEBUGF(TCP_DEBUG, ("RST "));
}
if (flags & TCP_PSH) {
LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
}
if (flags & TCP_ACK) {
LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
}
if (flags & TCP_URG) {
LWIP_DEBUGF(TCP_DEBUG, ("URG "));
}
if (flags & TCP_ECE) {
LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
}
if (flags & TCP_CWR) {
LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
}
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
}
/**
* Print all tcp_pcbs in every list for debugging purposes.
*/
void
tcp_debug_print_pcbs(void)
{
struct tcp_pcb *pcb;
LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
}
LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
}
LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
pcb->local_port, pcb->remote_port,
pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
}
}
/**
* Check state consistency of the tcp_pcb lists.
*/
s16_t
tcp_pcbs_sane(void)
{
struct tcp_pcb *pcb;
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
}
for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
}
return 1;
}
#endif /* TCP_DEBUG */
#endif /* LWIP_TCP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -