📄 tcp.c
字号:
we have to check the pcbs in TIME-WAIT state, also.
We do not dump TIME_WAIT pcb's; they can still be matched by incoming
packets using both local and remote IP addresses and ports to distinguish.
*/
if ((pcb->so_options & SOF_REUSEADDR) != 0) {
max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
}
#endif /* SO_REUSE */
if (port == 0) {
port = tcp_new_port();
}
/* Check if the address already is in use (on all lists) */
for (i = 0; i < max_pcb_list; i++) {
for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
if (cpcb->local_port == port) {
#if SO_REUSE
/* Omit checking for the same port if both pcbs have REUSEADDR set.
For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
tcp_connect. */
if (((pcb->so_options & SOF_REUSEADDR) == 0) ||
((cpcb->so_options & SOF_REUSEADDR) == 0))
#endif /* SO_REUSE */
{
if (ip_addr_isany(&(cpcb->local_ip)) ||
ip_addr_isany(ipaddr) ||
ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
return ERR_USE;
}
}
}
}
}
if (!ip_addr_isany(ipaddr)) {
pcb->local_ip = *ipaddr;
}
pcb->local_port = port;
TCP_REG(&tcp_bound_pcbs, pcb);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
return ERR_OK;
}
#if LWIP_CALLBACK_API
/**
* Default accept callback if no accept callback is specified by the user.
*/
static err_t
tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
{
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(err);
return ERR_ABRT;
}
#endif /* LWIP_CALLBACK_API */
/**
* Set the state of the connection to be LISTEN, which means that it
* is able to accept incoming connections. The protocol control block
* is reallocated in order to consume less memory. Setting the
* connection to LISTEN is an irreversible process.
*
* @param pcb the original tcp_pcb
* @param backlog the incoming connections queue limit
* @return tcp_pcb used for listening, consumes less memory.
*
* @note The original tcp_pcb is freed. This function therefore has to be
* called like this:
* tpcb = tcp_listen(tpcb);
*/
struct tcp_pcb *
tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
{
struct tcp_pcb_listen *lpcb;
LWIP_UNUSED_ARG(backlog);
LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
/* already listening? */
if (pcb->state == LISTEN) {
return pcb;
}
#if SO_REUSE
if ((pcb->so_options & SOF_REUSEADDR) != 0) {
/* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
is declared (listen-/connection-pcb), we have to make sure now that
this port is only used once for every local IP. */
for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if (lpcb->local_port == pcb->local_port) {
if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) {
/* this address/port is already used */
return NULL;
}
}
}
}
#endif /* SO_REUSE */
lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
if (lpcb == NULL) {
return NULL;
}
lpcb->callback_arg = pcb->callback_arg;
lpcb->local_port = pcb->local_port;
lpcb->state = LISTEN;
lpcb->prio = pcb->prio;
lpcb->so_options = pcb->so_options;
lpcb->so_options |= SOF_ACCEPTCONN;
lpcb->ttl = pcb->ttl;
lpcb->tos = pcb->tos;
ip_addr_copy(lpcb->local_ip, pcb->local_ip);
if (pcb->local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
memp_free(MEMP_TCP_PCB, pcb);
#if LWIP_CALLBACK_API
lpcb->accept = tcp_accept_null;
#endif /* LWIP_CALLBACK_API */
#if TCP_LISTEN_BACKLOG
lpcb->accepts_pending = 0;
lpcb->backlog = (backlog ? backlog : 1);
#endif /* TCP_LISTEN_BACKLOG */
TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
return (struct tcp_pcb *)lpcb;
}
/**
* Update the state that tracks the available window space to advertise.
*
* Returns how much extra window would be advertised if we sent an
* update now.
*/
u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
{
u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
/* we can advertise more window */
pcb->rcv_ann_wnd = pcb->rcv_wnd;
return new_right_edge - pcb->rcv_ann_right_edge;
} else {
if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
/* Can happen due to other end sending out of advertised window,
* but within actual available (but not yet advertised) window */
pcb->rcv_ann_wnd = 0;
} else {
/* keep the right edge of window constant */
u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
}
return 0;
}
}
/**
* This function should be called by the application when it has
* processed the data. The purpose is to advertise a larger window
* when the data has been processed.
*
* @param pcb the tcp_pcb for which data is read
* @param len the amount of bytes that have been read by the application
*/
void
tcp_recved(struct tcp_pcb *pcb, u16_t len)
{
int wnd_inflation;
LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n",
len <= 0xffff - pcb->rcv_wnd );
pcb->rcv_wnd += len;
if (pcb->rcv_wnd > TCP_WND) {
pcb->rcv_wnd = TCP_WND;
}
wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
/* If the change in the right edge of window is significant (default
* watermark is TCP_WND/4), then send an explicit update now.
* Otherwise wait for a packet to be sent in the normal course of
* events (or more window to be available later) */
if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
tcp_ack_now(pcb);
tcp_output(pcb);
}
LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
}
/**
* A nastly hack featuring 'goto' statements that allocates a
* new TCP local port.
*
* @return a new (free) local TCP port number
*/
static u16_t
tcp_new_port(void)
{
int i;
struct tcp_pcb *pcb;
#ifndef TCP_LOCAL_PORT_RANGE_START
/* From http://www.iana.org/assignments/port-numbers:
"The Dynamic and/or Private Ports are those from 49152 through 65535" */
#define TCP_LOCAL_PORT_RANGE_START 0xc000
#define TCP_LOCAL_PORT_RANGE_END 0xffff
#endif
static u16_t port = TCP_LOCAL_PORT_RANGE_START;
again:
if (port++ >= TCP_LOCAL_PORT_RANGE_END) {
port = TCP_LOCAL_PORT_RANGE_START;
}
/* Check all PCB lists. */
for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
if (pcb->local_port == port) {
goto again;
}
}
}
return port;
}
/**
* Connects to another host. The function given as the "connected"
* argument will be called when the connection has been established.
*
* @param pcb the tcp_pcb used to establish the connection
* @param ipaddr the remote ip address to connect to
* @param port the remote tcp port to connect to
* @param connected callback function to call when connected (or on error)
* @return ERR_VAL if invalid arguments are given
* ERR_OK if connect request has been sent
* other err_t values if connect request couldn't be sent
*/
err_t
tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
tcp_connected_fn connected)
{
err_t ret;
u32_t iss;
u16_t old_local_port;
LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
if (ipaddr != NULL) {
pcb->remote_ip = *ipaddr;
} else {
return ERR_VAL;
}
pcb->remote_port = port;
/* check if we have a route to the remote host */
if (ip_addr_isany(&(pcb->local_ip))) {
/* no local IP address set, yet. */
struct netif *netif = ip_route(&(pcb->remote_ip));
if (netif == NULL) {
/* Don't even try to send a SYN packet if we have no route
since that will fail. */
return ERR_RTE;
}
/* Use the netif's IP address as local address. */
ip_addr_copy(pcb->local_ip, netif->ip_addr);
}
old_local_port = pcb->local_port;
if (pcb->local_port == 0) {
pcb->local_port = tcp_new_port();
}
#if SO_REUSE
if ((pcb->so_options & SOF_REUSEADDR) != 0) {
/* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
now that the 5-tuple is unique. */
struct tcp_pcb *cpcb;
int i;
/* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
if ((cpcb->local_port == pcb->local_port) &&
(cpcb->remote_port == port) &&
ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
ip_addr_cmp(&cpcb->remote_ip, ipaddr)) {
/* linux returns EISCONN here, but ERR_USE should be OK for us */
return ERR_USE;
}
}
}
}
#endif /* SO_REUSE */
iss = tcp_next_iss();
pcb->rcv_nxt = 0;
pcb->snd_nxt = iss;
pcb->lastack = iss - 1;
pcb->snd_lbb = iss - 1;
pcb->rcv_wnd = TCP_WND;
pcb->rcv_ann_wnd = TCP_WND;
pcb->rcv_ann_right_edge = pcb->rcv_nxt;
pcb->snd_wnd = TCP_WND;
/* 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;
#if TCP_CALCULATE_EFF_SEND_MSS
pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
pcb->cwnd = 1;
pcb->ssthresh = pcb->mss * 10;
#if LWIP_CALLBACK_API
pcb->connected = connected;
#else /* LWIP_CALLBACK_API */
LWIP_UNUSED_ARG(connected);
#endif /* LWIP_CALLBACK_API */
/* Send a SYN together with the MSS option. */
ret = tcp_enqueue_flags(pcb, TCP_SYN);
if (ret == ERR_OK) {
/* SYN segment was enqueued, changed the pcbs state now */
pcb->state = SYN_SENT;
if (old_local_port != 0) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
TCP_REG(&tcp_active_pcbs, pcb);
snmp_inc_tcpactiveopens();
tcp_output(pcb);
}
return ret;
}
/**
* Called every 500 ms and implements the retransmission timer and the timer that
* removes PCBs that have been in TIME-WAIT for enough time. It also increments
* various timers such as the inactivity timer in each PCB.
*
* Automatically called from tcp_tmr().
*/
void
tcp_slowtmr(void)
{
struct tcp_pcb *pcb, *prev;
u16_t eff_wnd;
u8_t pcb_remove; /* flag if a PCB should be removed */
u8_t pcb_reset; /* flag if a RST should be sent when removing */
err_t err;
err = ERR_OK;
++tcp_ticks;
/* Steps through all of the active PCBs. */
prev = NULL;
pcb = tcp_active_pcbs;
if (pcb == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
}
while (pcb != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
pcb_remove = 0;
pcb_reset = 0;
if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
}
else if (pcb->nrtx == TCP_MAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
} else {
if (pcb->persist_backoff > 0) {
/* If snd_wnd is zero, use persist timer to send 1 byte probes
* instead of using the standard retransmission mechanism. */
pcb->persist_cnt++;
if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
pcb->persist_cnt = 0;
if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
pcb->persist_backoff++;
}
tcp_zero_window_probe(pcb);
}
} else {
/* Increase the retransmission timer if it is running */
if(pcb->rtime >= 0)
++pcb->rtime;
if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
/* Time for a retransmission. */
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
" pcb->rto %"S16_F"\n",
pcb->rtime, pcb->rto));
/* Double retransmission time-out unless we are trying to
* connect to somebody (i.e., we are in SYN_SENT). */
if (pcb->state != SYN_SENT) {
pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
}
/* Reset the retransmission timer. */
pcb->rtime = 0;
/* Reduce congestion window and ssthresh. */
eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
pcb->ssthresh = eff_wnd >> 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -