2014-12-22 12:35:05 +01:00
/**
* @ file
* Transmission Control Protocol , outgoing traffic
*
* The output functions of TCP .
*
*/
/*
* Copyright ( c ) 2001 - 2004 Swedish Institute of Computer Science .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without modification ,
* are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL ,
* EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE .
*
* This file is part of the lwIP TCP / IP stack .
*
* Author : Adam Dunkels < adam @ sics . se >
*
*/
# include "lwip/opt.h"
# if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
# include "lwip/tcp_impl.h"
# include "lwip/def.h"
# include "lwip/mem.h"
# include "lwip/memp.h"
# include "lwip/sys.h"
# include "lwip/ip_addr.h"
# include "lwip/netif.h"
# include "lwip/inet_chksum.h"
# include "lwip/stats.h"
# include "lwip/snmp.h"
2015-10-12 05:31:04 +02:00
# include "netif/etharp.h"
2014-12-22 12:35:05 +01:00
# include <string.h>
2015-10-12 05:31:04 +02:00
# ifdef MEMLEAK_DEBUG
static const char mem_debug_file [ ] ICACHE_RODATA_ATTR = __FILE__ ;
# endif
2014-12-22 12:35:05 +01:00
/* Define some copy-macros for checksum-on-copy so that the code looks
nicer by preventing too many ifdef ' s . */
# if TCP_CHECKSUM_ON_COPY
# define TCP_DATA_COPY(dst, src, len, seg) do { \
tcp_seg_add_chksum ( LWIP_CHKSUM_COPY ( dst , src , len ) , \
len , & seg - > chksum , & seg - > chksum_swapped ) ; \
seg - > flags | = TF_SEG_DATA_CHECKSUMMED ; } while ( 0 )
# define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \
tcp_seg_add_chksum ( LWIP_CHKSUM_COPY ( dst , src , len ) , len , chksum , chksum_swapped ) ;
# else /* TCP_CHECKSUM_ON_COPY*/
# define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)
# define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
# endif /* TCP_CHECKSUM_ON_COPY*/
/** Define this to 1 for an extra check that the output checksum is valid
* ( usefule when the checksum is generated by the application , not the stack ) */
# ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
# define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
# endif
/* Forward declarations.*/
static void tcp_output_segment ( struct tcp_seg * seg , struct tcp_pcb * pcb ) ;
/** Allocate a pbuf and create a tcphdr at p->payload, used for output
* functions other than the default tcp_output - > tcp_output_segment
* ( e . g . tcp_send_empty_ack , etc . )
*
* @ param pcb tcp pcb for which to send a packet ( used to initialize tcp_hdr )
* @ param optlen length of header - options
* @ param datalen length of tcp data to reserve in pbuf
* @ param seqno_be seqno in network byte order ( big - endian )
* @ return pbuf with p - > payload being the tcp_hdr
*/
static struct pbuf * ICACHE_FLASH_ATTR
tcp_output_alloc_header ( struct tcp_pcb * pcb , u16_t optlen , u16_t datalen ,
u32_t seqno_be /* already in network byte order */ )
{
struct tcp_hdr * tcphdr ;
struct pbuf * p = pbuf_alloc ( PBUF_IP , TCP_HLEN + optlen + datalen , PBUF_RAM ) ;
if ( p ! = NULL ) {
LWIP_ASSERT ( " check that first pbuf can hold struct tcp_hdr " ,
( p - > len > = TCP_HLEN + optlen ) ) ;
tcphdr = ( struct tcp_hdr * ) p - > payload ;
tcphdr - > src = htons ( pcb - > local_port ) ;
tcphdr - > dest = htons ( pcb - > remote_port ) ;
tcphdr - > seqno = seqno_be ;
tcphdr - > ackno = htonl ( pcb - > rcv_nxt ) ;
TCPH_HDRLEN_FLAGS_SET ( tcphdr , ( 5 + optlen / 4 ) , TCP_ACK ) ;
tcphdr - > wnd = htons ( pcb - > rcv_ann_wnd ) ;
tcphdr - > chksum = 0 ;
tcphdr - > urgp = 0 ;
/* If we're sending a packet, update the announced right window edge */
pcb - > rcv_ann_right_edge = pcb - > rcv_nxt + pcb - > rcv_ann_wnd ;
}
return p ;
}
/**
* Called by tcp_close ( ) to send a segment including FIN flag but not data .
*
* @ param pcb the tcp_pcb over which to send a segment
* @ return ERR_OK if sent , another err_t otherwise
*/
err_t
tcp_send_fin ( struct tcp_pcb * pcb )
{
/* first, try to add the fin to the last unsent segment */
if ( pcb - > unsent ! = NULL ) {
struct tcp_seg * last_unsent ;
for ( last_unsent = pcb - > unsent ; last_unsent - > next ! = NULL ;
last_unsent = last_unsent - > next ) ;
if ( ( TCPH_FLAGS ( last_unsent - > tcphdr ) & ( TCP_SYN | TCP_FIN | TCP_RST ) ) = = 0 ) {
/* no SYN/FIN/RST flag in the header, we can add the FIN flag */
TCPH_SET_FLAG ( last_unsent - > tcphdr , TCP_FIN ) ;
return ERR_OK ;
}
}
/* no data, no length, flags, copy=1, no optdata */
return tcp_enqueue_flags ( pcb , TCP_FIN ) ;
}
/**
* Create a TCP segment with prefilled header .
*
* Called by tcp_write and tcp_enqueue_flags .
*
* @ param pcb Protocol control block for the TCP connection .
* @ param p pbuf that is used to hold the TCP header .
* @ param flags TCP flags for header .
* @ param seqno TCP sequence number of this packet
* @ param optflags options to include in TCP header
* @ return a new tcp_seg pointing to p , or NULL .
* The TCP header is filled in except ackno and wnd .
* p is freed on failure .
*/
static struct tcp_seg * ICACHE_FLASH_ATTR
tcp_create_segment ( struct tcp_pcb * pcb , struct pbuf * p , u8_t flags , u32_t seqno , u8_t optflags )
{
struct tcp_seg * seg ;
u8_t optlen = LWIP_TCP_OPT_LENGTH ( optflags ) ;
if ( ( seg = ( struct tcp_seg * ) memp_malloc ( MEMP_TCP_SEG ) ) = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 , ( " tcp_create_segment: no memory. \n " ) ) ;
pbuf_free ( p ) ;
return NULL ;
}
seg - > flags = optflags ;
seg - > next = NULL ;
seg - > p = p ;
seg - > len = p - > tot_len - optlen ;
# if TCP_OVERSIZE_DBGCHECK
seg - > oversize_left = 0 ;
# endif /* TCP_OVERSIZE_DBGCHECK */
# if TCP_CHECKSUM_ON_COPY
seg - > chksum = 0 ;
seg - > chksum_swapped = 0 ;
/* check optflags */
LWIP_ASSERT ( " invalid optflags passed: TF_SEG_DATA_CHECKSUMMED " ,
( optflags & TF_SEG_DATA_CHECKSUMMED ) = = 0 ) ;
# endif /* TCP_CHECKSUM_ON_COPY */
/* build TCP header */
if ( pbuf_header ( p , TCP_HLEN ) ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 , ( " tcp_create_segment: no room for TCP header in pbuf. \n " ) ) ;
TCP_STATS_INC ( tcp . err ) ;
tcp_seg_free ( seg ) ;
return NULL ;
}
seg - > tcphdr = ( struct tcp_hdr * ) seg - > p - > payload ;
seg - > tcphdr - > src = htons ( pcb - > local_port ) ;
seg - > tcphdr - > dest = htons ( pcb - > remote_port ) ;
seg - > tcphdr - > seqno = htonl ( seqno ) ;
/* ackno is set in tcp_output */
TCPH_HDRLEN_FLAGS_SET ( seg - > tcphdr , ( 5 + optlen / 4 ) , flags ) ;
/* wnd and chksum are set in tcp_output */
seg - > tcphdr - > urgp = 0 ;
return seg ;
}
/**
* Allocate a PBUF_RAM pbuf , perhaps with extra space at the end .
*
* This function is like pbuf_alloc ( layer , length , PBUF_RAM ) except
* there may be extra bytes available at the end .
*
* @ param layer flag to define header size .
* @ param length size of the pbuf ' s payload .
* @ param max_length maximum usable size of payload + oversize .
* @ param oversize pointer to a u16_t that will receive the number of usable tail bytes .
* @ param pcb The TCP connection that willo enqueue the pbuf .
* @ param apiflags API flags given to tcp_write .
* @ param first_seg true when this pbuf will be used in the first enqueued segment .
* @ param
*/
# if TCP_OVERSIZE
static struct pbuf * ICACHE_FLASH_ATTR
tcp_pbuf_prealloc ( pbuf_layer layer , u16_t length , u16_t max_length ,
u16_t * oversize , struct tcp_pcb * pcb , u8_t apiflags ,
u8_t first_seg )
{
struct pbuf * p ;
u16_t alloc = length ;
# if LWIP_NETIF_TX_SINGLE_PBUF
LWIP_UNUSED_ARG ( max_length ) ;
LWIP_UNUSED_ARG ( pcb ) ;
LWIP_UNUSED_ARG ( apiflags ) ;
LWIP_UNUSED_ARG ( first_seg ) ;
/* always create MSS-sized pbufs */
2016-12-11 21:03:00 +01:00
alloc = pcb - > mss ; //TCP_MSS;
2014-12-22 12:35:05 +01:00
# else /* LWIP_NETIF_TX_SINGLE_PBUF */
if ( length < max_length ) {
/* Should we allocate an oversized pbuf, or just the minimum
* length required ? If tcp_write is going to be called again
* before this segment is transmitted , we want the oversized
* buffer . If the segment will be transmitted immediately , we can
* save memory by allocating only length . We use a simple
* heuristic based on the following information :
*
* Did the user set TCP_WRITE_FLAG_MORE ?
*
* Will the Nagle algorithm defer transmission of this segment ?
*/
if ( ( apiflags & TCP_WRITE_FLAG_MORE ) | |
( ! ( pcb - > flags & TF_NODELAY ) & &
( ! first_seg | |
pcb - > unsent ! = NULL | |
pcb - > unacked ! = NULL ) ) ) {
alloc = LWIP_MIN ( max_length , LWIP_MEM_ALIGN_SIZE ( length + TCP_OVERSIZE ) ) ;
}
}
# endif /* LWIP_NETIF_TX_SINGLE_PBUF */
p = pbuf_alloc ( layer , alloc , PBUF_RAM ) ;
if ( p = = NULL ) {
return NULL ;
}
LWIP_ASSERT ( " need unchained pbuf " , p - > next = = NULL ) ;
* oversize = p - > len - length ;
/* trim p->len to the currently used size */
p - > len = p - > tot_len = length ;
return p ;
}
# else /* TCP_OVERSIZE */
# define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
# endif /* TCP_OVERSIZE */
# if TCP_CHECKSUM_ON_COPY
/** Add a checksum of newly added data to the segment */
static void ICACHE_FLASH_ATTR
tcp_seg_add_chksum ( u16_t chksum , u16_t len , u16_t * seg_chksum ,
u8_t * seg_chksum_swapped )
{
u32_t helper ;
/* add chksum to old chksum and fold to u16_t */
helper = chksum + * seg_chksum ;
chksum = FOLD_U32T ( helper ) ;
if ( ( len & 1 ) ! = 0 ) {
* seg_chksum_swapped = 1 - * seg_chksum_swapped ;
chksum = SWAP_BYTES_IN_WORD ( chksum ) ;
}
* seg_chksum = chksum ;
}
# endif /* TCP_CHECKSUM_ON_COPY */
/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
*
* @ param pcb the tcp pcb to check for
* @ param len length of data to send ( checked agains snd_buf )
* @ return ERR_OK if tcp_write is allowed to proceed , another err_t otherwise
*/
static err_t ICACHE_FLASH_ATTR
tcp_write_checks ( struct tcp_pcb * pcb , u16_t len )
{
/* connection is in invalid state for data transmission? */
if ( ( pcb - > state ! = ESTABLISHED ) & &
( pcb - > state ! = CLOSE_WAIT ) & &
( pcb - > state ! = SYN_SENT ) & &
( pcb - > state ! = SYN_RCVD ) ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE , ( " tcp_write() called in invalid state \n " ) ) ;
return ERR_CONN ;
} else if ( len = = 0 ) {
return ERR_OK ;
}
/* fail on too much data */
if ( len > pcb - > snd_buf ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 3 , ( " tcp_write: too much data (len=% " U16_F " > snd_buf=% " U16_F " ) \n " ,
len , pcb - > snd_buf ) ) ;
pcb - > flags | = TF_NAGLEMEMERR ;
return ERR_MEM ;
}
LWIP_DEBUGF ( TCP_QLEN_DEBUG , ( " tcp_write: queuelen: % " U16_F " \n " , ( u16_t ) pcb - > snd_queuelen ) ) ;
/* If total number of pbufs on the unsent/unacked queues exceeds the
* configured maximum , return an error */
/* check for configured max queuelen and possible overflow */
if ( ( pcb - > snd_queuelen > = TCP_SND_QUEUELEN ) | | ( pcb - > snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW ) ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 3 , ( " tcp_write: too long queue % " U16_F " (max % " U16_F " ) \n " ,
pcb - > snd_queuelen , TCP_SND_QUEUELEN ) ) ;
TCP_STATS_INC ( tcp . memerr ) ;
pcb - > flags | = TF_NAGLEMEMERR ;
return ERR_MEM ;
}
if ( pcb - > snd_queuelen ! = 0 ) {
LWIP_ASSERT ( " tcp_write: pbufs on queue => at least one queue non-empty " ,
pcb - > unacked ! = NULL | | pcb - > unsent ! = NULL ) ;
} else {
LWIP_ASSERT ( " tcp_write: no pbufs on queue => both queues empty " ,
pcb - > unacked = = NULL & & pcb - > unsent = = NULL ) ;
}
return ERR_OK ;
}
/**
* Write data for sending ( but does not send it immediately ) .
2015-10-01 07:07:16 +02:00
* <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> һ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ݣ <EFBFBD> <EFBFBD> ú <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> һ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ķ β <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ڿ <EFBFBD> <EFBFBD> ƿ 黺 <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
2014-12-22 12:35:05 +01:00
* It waits in the expectation of more data being sent soon ( as
* it can send them more efficiently by combining them together ) .
* To prompt the system to send data now , call tcp_output ( ) after
* calling tcp_write ( ) .
*
2015-10-01 07:07:16 +02:00
* @ param pcb Protocol control block for the TCP connection to enqueue data for . <EFBFBD> <EFBFBD> Ӧ <EFBFBD> <EFBFBD> <EFBFBD> ӿ <EFBFBD> <EFBFBD> ƿ <EFBFBD>
* @ param arg Pointer to the data to be enqueued for sending . <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ʼ <EFBFBD> <EFBFBD> ַ
* @ param len Data length in bytes <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ݳ <EFBFBD> <EFBFBD> <EFBFBD>
* @ param apiflags combination of following flags : <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ƿ <EFBFBD> <EFBFBD> <EFBFBD> п <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Լ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> Ķ <EFBFBD> <EFBFBD> ײ <EFBFBD> <EFBFBD> Ƿ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> PSH <EFBFBD> <EFBFBD> ־
2014-12-22 12:35:05 +01:00
* - TCP_WRITE_FLAG_COPY ( 0x01 ) data will be copied into memory belonging to the stack
* - TCP_WRITE_FLAG_MORE ( 0x02 ) for TCP connection , PSH flag will be set on last segment sent ,
* @ return ERR_OK if enqueued , another err_t on error
*/
err_t
tcp_write ( struct tcp_pcb * pcb , const void * arg , u16_t len , u8_t apiflags )
{
struct pbuf * concat_p = NULL ;
struct tcp_seg * last_unsent = NULL , * seg = NULL , * prev_seg = NULL , * queue = NULL ;
u16_t pos = 0 ; /* position in 'arg' data */
u16_t queuelen ;
u8_t optlen = 0 ;
u8_t optflags = 0 ;
# if TCP_OVERSIZE
u16_t oversize = 0 ;
u16_t oversize_used = 0 ;
# endif /* TCP_OVERSIZE */
# if TCP_CHECKSUM_ON_COPY
u16_t concat_chksum = 0 ;
u8_t concat_chksum_swapped = 0 ;
u16_t concat_chksummed = 0 ;
# endif /* TCP_CHECKSUM_ON_COPY */
err_t err ;
# if LWIP_NETIF_TX_SINGLE_PBUF
/* Always copy to try to create single pbufs for TX */
apiflags | = TCP_WRITE_FLAG_COPY ;
# endif /* LWIP_NETIF_TX_SINGLE_PBUF */
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG , ( " tcp_write(pcb=%p, data=%p, len=% " U16_F " , apiflags=% " U16_F " ) \n " ,
( void * ) pcb , arg , len , ( u16_t ) apiflags ) ) ;
LWIP_ERROR ( " tcp_write: arg == NULL (programmer violates API) " ,
arg ! = NULL , return ERR_ARG ; ) ;
err = tcp_write_checks ( pcb , len ) ;
if ( err ! = ERR_OK ) {
return err ;
}
queuelen = pcb - > snd_queuelen ;
# if LWIP_TCP_TIMESTAMPS
if ( ( pcb - > flags & TF_TIMESTAMP ) ) {
optflags = TF_SEG_OPTS_TS ;
optlen = LWIP_TCP_OPT_LENGTH ( TF_SEG_OPTS_TS ) ;
}
# endif /* LWIP_TCP_TIMESTAMPS */
/*
* TCP segmentation is done in three phases with increasing complexity :
*
* 1. Copy data directly into an oversized pbuf .
* 2. Chain a new pbuf to the end of pcb - > unsent .
* 3. Create new segments .
*
* We may run out of memory at any point . In that case we must
* return ERR_MEM and not change anything in pcb . Therefore , all
* changes are recorded in local variables and committed at the end
* of the function . Some pcb fields are maintained in local copies :
*
* queuelen = pcb - > snd_queuelen
* oversize = pcb - > unsent_oversize
*
* These variables are set consistently by the phases :
*
* seg points to the last segment tampered with .
*
* pos records progress as data is segmented .
*/
/* Find the tail of the unsent queue. */
if ( pcb - > unsent ! = NULL ) {
2016-12-11 21:03:00 +01:00
u16_t space = 0 ;
u16_t unsent_optlen = 0 ;
2014-12-22 12:35:05 +01:00
/* @todo: this could be sped up by keeping last_unsent in the pcb */
for ( last_unsent = pcb - > unsent ; last_unsent - > next ! = NULL ;
last_unsent = last_unsent - > next ) ;
/* Usable space at the end of the last unsent segment */
unsent_optlen = LWIP_TCP_OPT_LENGTH ( last_unsent - > flags ) ;
space = pcb - > mss - ( last_unsent - > len + unsent_optlen ) ;
/*
* Phase 1 : Copy data directly into an oversized pbuf .
*
* The number of bytes copied is recorded in the oversize_used
* variable . The actual copying is done at the bottom of the
* function .
*/
# if TCP_OVERSIZE
# if TCP_OVERSIZE_DBGCHECK
/* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
LWIP_ASSERT ( " unsent_oversize mismatch (pcb vs. last_unsent) " ,
pcb - > unsent_oversize = = last_unsent - > oversize_left ) ;
# endif /* TCP_OVERSIZE_DBGCHECK */
oversize = pcb - > unsent_oversize ;
if ( oversize > 0 ) {
LWIP_ASSERT ( " inconsistent oversize vs. space " , oversize_used < = space ) ;
seg = last_unsent ;
oversize_used = oversize < len ? oversize : len ;
pos + = oversize_used ;
oversize - = oversize_used ;
space - = oversize_used ;
}
/* now we are either finished or oversize is zero */
LWIP_ASSERT ( " inconsistend oversize vs. len " , ( oversize = = 0 ) | | ( pos = = len ) ) ;
# endif /* TCP_OVERSIZE */
2016-12-11 21:03:00 +01:00
if ( pos > len ) {
return ERR_MEM ;
}
/*
2014-12-22 12:35:05 +01:00
* Phase 2 : Chain a new pbuf to the end of pcb - > unsent .
*
* We don ' t extend segments containing SYN / FIN flags or options
* ( len = = 0 ) . The new pbuf is kept in concat_p and pbuf_cat ' ed at
* the end .
*/
if ( ( pos < len ) & & ( space > 0 ) & & ( last_unsent - > len > 0 ) ) {
u16_t seglen = space < len - pos ? space : len - pos ;
seg = last_unsent ;
/* Create a pbuf with a copy or reference to seglen bytes. We
* can use PBUF_RAW here since the data appears in the middle of
* a segment . A header will never be prepended . */
if ( apiflags & TCP_WRITE_FLAG_COPY ) {
/* Data is copied */
if ( ( concat_p = tcp_pbuf_prealloc ( PBUF_RAW , seglen , space , & oversize , pcb , apiflags , 1 ) ) = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 ,
( " tcp_write : could not allocate memory for pbuf copy size % " U16_F " \n " ,
seglen ) ) ;
goto memerr ;
}
# if TCP_OVERSIZE_DBGCHECK
last_unsent - > oversize_left = oversize ;
# endif /* TCP_OVERSIZE_DBGCHECK */
TCP_DATA_COPY2 ( concat_p - > payload , ( u8_t * ) arg + pos , seglen , & concat_chksum , & concat_chksum_swapped ) ;
# if TCP_CHECKSUM_ON_COPY
concat_chksummed + = seglen ;
# endif /* TCP_CHECKSUM_ON_COPY */
} else {
/* Data is not copied */
if ( ( concat_p = pbuf_alloc ( PBUF_RAW , seglen , PBUF_ROM ) ) = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 ,
( " tcp_write: could not allocate memory for zero-copy pbuf \n " ) ) ;
goto memerr ;
}
# if TCP_CHECKSUM_ON_COPY
/* calculate the checksum of nocopy-data */
tcp_seg_add_chksum ( ~ inet_chksum ( ( u8_t * ) arg + pos , seglen ) , seglen ,
& concat_chksum , & concat_chksum_swapped ) ;
concat_chksummed + = seglen ;
# endif /* TCP_CHECKSUM_ON_COPY */
/* reference the non-volatile payload data */
concat_p - > payload = ( u8_t * ) arg + pos ;
}
pos + = seglen ;
queuelen + = pbuf_clen ( concat_p ) ;
}
} else {
# if TCP_OVERSIZE
LWIP_ASSERT ( " unsent_oversize mismatch (pcb->unsent is NULL) " ,
pcb - > unsent_oversize = = 0 ) ;
# endif /* TCP_OVERSIZE */
}
/*
* Phase 3 : Create new segments .
*
* The new segments are chained together in the local ' queue '
* variable , ready to be appended to pcb - > unsent .
*/
while ( pos < len ) {
struct pbuf * p ;
u16_t left = len - pos ;
u16_t max_len = pcb - > mss - optlen ;
u16_t seglen = left > max_len ? max_len : left ;
# if TCP_CHECKSUM_ON_COPY
u16_t chksum = 0 ;
u8_t chksum_swapped = 0 ;
# endif /* TCP_CHECKSUM_ON_COPY */
if ( apiflags & TCP_WRITE_FLAG_COPY ) {
/* If copy is set, memory should be allocated and data copied
* into pbuf */
if ( ( p = tcp_pbuf_prealloc ( PBUF_TRANSPORT , seglen + optlen , pcb - > mss , & oversize , pcb , apiflags , queue = = NULL ) ) = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 , ( " tcp_write : could not allocate memory for pbuf copy size % " U16_F " \n " , seglen ) ) ;
goto memerr ;
}
LWIP_ASSERT ( " tcp_write: check that first pbuf can hold the complete seglen " ,
( p - > len > = seglen ) ) ;
TCP_DATA_COPY2 ( ( char * ) p - > payload + optlen , ( u8_t * ) arg + pos , seglen , & chksum , & chksum_swapped ) ;
} else {
/* Copy is not set: First allocate a pbuf for holding the data.
* Since the referenced data is available at least until it is
* sent out on the link ( as it has to be ACKed by the remote
* party ) we can safely use PBUF_ROM instead of PBUF_REF here .
*/
struct pbuf * p2 ;
# if TCP_OVERSIZE
LWIP_ASSERT ( " oversize == 0 " , oversize = = 0 ) ;
# endif /* TCP_OVERSIZE */
if ( ( p2 = pbuf_alloc ( PBUF_TRANSPORT , seglen , PBUF_ROM ) ) = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 , ( " tcp_write: could not allocate memory for zero-copy pbuf \n " ) ) ;
goto memerr ;
}
# if TCP_CHECKSUM_ON_COPY
/* calculate the checksum of nocopy-data */
chksum = ~ inet_chksum ( ( u8_t * ) arg + pos , seglen ) ;
# endif /* TCP_CHECKSUM_ON_COPY */
/* reference the non-volatile payload data */
p2 - > payload = ( u8_t * ) arg + pos ;
/* Second, allocate a pbuf for the headers. */
if ( ( p = pbuf_alloc ( PBUF_TRANSPORT , optlen , PBUF_RAM ) ) = = NULL ) {
/* If allocation fails, we have to deallocate the data pbuf as
* well . */
pbuf_free ( p2 ) ;
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 , ( " tcp_write: could not allocate memory for header pbuf \n " ) ) ;
goto memerr ;
}
/* Concatenate the headers and data pbufs together. */
pbuf_cat ( p /*header*/ , p2 /*data*/ ) ;
}
queuelen + = pbuf_clen ( p ) ;
/* Now that there are more segments queued, we check again if the
* length of the queue exceeds the configured maximum or
* overflows . */
if ( ( queuelen > TCP_SND_QUEUELEN ) | | ( queuelen > TCP_SNDQUEUELEN_OVERFLOW ) ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 2 , ( " tcp_write: queue too long % " U16_F " (% " U16_F " ) \n " , queuelen , TCP_SND_QUEUELEN ) ) ;
pbuf_free ( p ) ;
goto memerr ;
}
if ( ( seg = tcp_create_segment ( pcb , p , 0 , pcb - > snd_lbb + pos , optflags ) ) = = NULL ) {
goto memerr ;
}
# if TCP_OVERSIZE_DBGCHECK
seg - > oversize_left = oversize ;
# endif /* TCP_OVERSIZE_DBGCHECK */
# if TCP_CHECKSUM_ON_COPY
seg - > chksum = chksum ;
seg - > chksum_swapped = chksum_swapped ;
seg - > flags | = TF_SEG_DATA_CHECKSUMMED ;
# endif /* TCP_CHECKSUM_ON_COPY */
/* first segment of to-be-queued data? */
if ( queue = = NULL ) {
queue = seg ;
} else {
/* Attach the segment to the end of the queued segments */
LWIP_ASSERT ( " prev_seg != NULL " , prev_seg ! = NULL ) ;
prev_seg - > next = seg ;
}
/* remember last segment of to-be-queued data for next iteration */
prev_seg = seg ;
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE , ( " tcp_write: queueing % " U32_F " :% " U32_F " \n " ,
ntohl ( seg - > tcphdr - > seqno ) ,
ntohl ( seg - > tcphdr - > seqno ) + TCP_TCPLEN ( seg ) ) ) ;
pos + = seglen ;
}
/*
* All three segmentation phases were successful . We can commit the
* transaction .
*/
/*
* Phase 1 : If data has been added to the preallocated tail of
* last_unsent , we update the length fields of the pbuf chain .
*/
# if TCP_OVERSIZE
if ( oversize_used > 0 ) {
struct pbuf * p ;
/* Bump tot_len of whole chain, len of tail */
for ( p = last_unsent - > p ; p ; p = p - > next ) {
p - > tot_len + = oversize_used ;
if ( p - > next = = NULL ) {
TCP_DATA_COPY ( ( char * ) p - > payload + p - > len , arg , oversize_used , last_unsent ) ;
p - > len + = oversize_used ;
}
}
last_unsent - > len + = oversize_used ;
# if TCP_OVERSIZE_DBGCHECK
last_unsent - > oversize_left - = oversize_used ;
# endif /* TCP_OVERSIZE_DBGCHECK */
}
pcb - > unsent_oversize = oversize ;
# endif /* TCP_OVERSIZE */
/*
* Phase 2 : concat_p can be concatenated onto last_unsent - > p
*/
if ( concat_p ! = NULL ) {
LWIP_ASSERT ( " tcp_write: cannot concatenate when pcb->unsent is empty " ,
( last_unsent ! = NULL ) ) ;
pbuf_cat ( last_unsent - > p , concat_p ) ;
last_unsent - > len + = concat_p - > tot_len ;
# if TCP_CHECKSUM_ON_COPY
if ( concat_chksummed ) {
tcp_seg_add_chksum ( concat_chksum , concat_chksummed , & last_unsent - > chksum ,
& last_unsent - > chksum_swapped ) ;
last_unsent - > flags | = TF_SEG_DATA_CHECKSUMMED ;
}
# endif /* TCP_CHECKSUM_ON_COPY */
}
/*
* Phase 3 : Append queue to pcb - > unsent . Queue may be NULL , but that
* is harmless
*/
if ( last_unsent = = NULL ) {
pcb - > unsent = queue ;
} else {
last_unsent - > next = queue ;
}
/*
* Finally update the pcb state .
*/
pcb - > snd_lbb + = len ;
pcb - > snd_buf - = len ;
pcb - > snd_queuelen = queuelen ;
LWIP_DEBUGF ( TCP_QLEN_DEBUG , ( " tcp_write: % " S16_F " (after enqueued) \n " ,
pcb - > snd_queuelen ) ) ;
if ( pcb - > snd_queuelen ! = 0 ) {
LWIP_ASSERT ( " tcp_write: valid queue length " ,
pcb - > unacked ! = NULL | | pcb - > unsent ! = NULL ) ;
}
/* Set the PSH flag in the last segment that we enqueued. */
if ( seg ! = NULL & & seg - > tcphdr ! = NULL & & ( ( apiflags & TCP_WRITE_FLAG_MORE ) = = 0 ) ) {
TCPH_SET_FLAG ( seg - > tcphdr , TCP_PSH ) ;
}
return ERR_OK ;
memerr :
pcb - > flags | = TF_NAGLEMEMERR ;
TCP_STATS_INC ( tcp . memerr ) ;
if ( concat_p ! = NULL ) {
pbuf_free ( concat_p ) ;
}
if ( queue ! = NULL ) {
tcp_segs_free ( queue ) ;
}
if ( pcb - > snd_queuelen ! = 0 ) {
LWIP_ASSERT ( " tcp_write: valid queue length " , pcb - > unacked ! = NULL | |
pcb - > unsent ! = NULL ) ;
}
LWIP_DEBUGF ( TCP_QLEN_DEBUG | LWIP_DBG_STATE , ( " tcp_write: % " S16_F " (with mem err) \n " , pcb - > snd_queuelen ) ) ;
return ERR_MEM ;
}
/**
* Enqueue TCP options for transmission .
*
* Called by tcp_connect ( ) , tcp_listen_input ( ) , and tcp_send_ctrl ( ) .
*
* @ param pcb Protocol control block for the TCP connection .
* @ param flags TCP header flags to set in the outgoing segment .
* @ param optdata pointer to TCP options , or NULL .
* @ param optlen length of TCP options in bytes .
*/
err_t
tcp_enqueue_flags ( struct tcp_pcb * pcb , u8_t flags )
{
struct pbuf * p ;
struct tcp_seg * seg ;
u8_t optflags = 0 ;
u8_t optlen = 0 ;
LWIP_DEBUGF ( TCP_QLEN_DEBUG , ( " tcp_enqueue_flags: queuelen: % " U16_F " \n " , ( u16_t ) pcb - > snd_queuelen ) ) ;
LWIP_ASSERT ( " tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API) " ,
( flags & ( TCP_SYN | TCP_FIN ) ) ! = 0 ) ;
/* check for configured max queuelen and possible overflow */
if ( ( pcb - > snd_queuelen > = TCP_SND_QUEUELEN ) | | ( pcb - > snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW ) ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 3 , ( " tcp_enqueue_flags: too long queue % " U16_F " (max % " U16_F " ) \n " ,
pcb - > snd_queuelen , TCP_SND_QUEUELEN ) ) ;
TCP_STATS_INC ( tcp . memerr ) ;
pcb - > flags | = TF_NAGLEMEMERR ;
return ERR_MEM ;
}
if ( flags & TCP_SYN ) {
optflags = TF_SEG_OPTS_MSS ;
}
# if LWIP_TCP_TIMESTAMPS
if ( ( pcb - > flags & TF_TIMESTAMP ) ) {
optflags | = TF_SEG_OPTS_TS ;
}
# endif /* LWIP_TCP_TIMESTAMPS */
optlen = LWIP_TCP_OPT_LENGTH ( optflags ) ;
/* tcp_enqueue_flags is always called with either SYN or FIN in flags.
* We need one available snd_buf byte to do that .
* This means we can ' t send FIN while snd_buf = = 0. A better fix would be to
* not include SYN and FIN sequence numbers in the snd_buf count . */
if ( pcb - > snd_buf = = 0 ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | 3 , ( " tcp_enqueue_flags: no send buffer available \n " ) ) ;
TCP_STATS_INC ( tcp . memerr ) ;
return ERR_MEM ;
}
/* Allocate pbuf with room for TCP header + options */
if ( ( p = pbuf_alloc ( PBUF_TRANSPORT , optlen , PBUF_RAM ) ) = = NULL ) {
pcb - > flags | = TF_NAGLEMEMERR ;
TCP_STATS_INC ( tcp . memerr ) ;
return ERR_MEM ;
}
LWIP_ASSERT ( " tcp_enqueue_flags: check that first pbuf can hold optlen " ,
( p - > len > = optlen ) ) ;
/* Allocate memory for tcp_seg, and fill in fields. */
if ( ( seg = tcp_create_segment ( pcb , p , flags , pcb - > snd_lbb , optflags ) ) = = NULL ) {
pcb - > flags | = TF_NAGLEMEMERR ;
TCP_STATS_INC ( tcp . memerr ) ;
return ERR_MEM ;
}
LWIP_ASSERT ( " seg->tcphdr not aligned " , ( ( mem_ptr_t ) seg - > tcphdr % MEM_ALIGNMENT ) = = 0 ) ;
LWIP_ASSERT ( " tcp_enqueue_flags: invalid segment length " , seg - > len = = 0 ) ;
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE ,
( " tcp_enqueue_flags: queueing % " U32_F " :% " U32_F " (0x% " X16_F " ) \n " ,
ntohl ( seg - > tcphdr - > seqno ) ,
ntohl ( seg - > tcphdr - > seqno ) + TCP_TCPLEN ( seg ) ,
( u16_t ) flags ) ) ;
/* Now append seg to pcb->unsent queue */
if ( pcb - > unsent = = NULL ) {
pcb - > unsent = seg ;
} else {
struct tcp_seg * useg ;
for ( useg = pcb - > unsent ; useg - > next ! = NULL ; useg = useg - > next ) ;
useg - > next = seg ;
}
# if TCP_OVERSIZE
/* The new unsent tail has no space */
pcb - > unsent_oversize = 0 ;
# endif /* TCP_OVERSIZE */
/* SYN and FIN bump the sequence number */
if ( ( flags & TCP_SYN ) | | ( flags & TCP_FIN ) ) {
pcb - > snd_lbb + + ;
/* optlen does not influence snd_buf */
pcb - > snd_buf - - ;
}
if ( flags & TCP_FIN ) {
pcb - > flags | = TF_FIN ;
}
/* update number of segments on the queues */
pcb - > snd_queuelen + = pbuf_clen ( seg - > p ) ;
LWIP_DEBUGF ( TCP_QLEN_DEBUG , ( " tcp_enqueue_flags: % " S16_F " (after enqueued) \n " , pcb - > snd_queuelen ) ) ;
if ( pcb - > snd_queuelen ! = 0 ) {
LWIP_ASSERT ( " tcp_enqueue_flags: invalid queue length " ,
pcb - > unacked ! = NULL | | pcb - > unsent ! = NULL ) ;
}
return ERR_OK ;
}
# if LWIP_TCP_TIMESTAMPS
/* Build a timestamp option (12 bytes long) at the specified options pointer)
*
* @ param pcb tcp_pcb
* @ param opts option pointer where to store the timestamp option
*/
static void ICACHE_FLASH_ATTR
tcp_build_timestamp_option ( struct tcp_pcb * pcb , u32_t * opts )
{
/* Pad with two NOP options to make everything nicely aligned */
opts [ 0 ] = PP_HTONL ( 0x0101080A ) ;
opts [ 1 ] = htonl ( sys_now ( ) ) ;
opts [ 2 ] = htonl ( pcb - > ts_recent ) ;
}
# endif
/** Send an ACK without data.
*
* @ param pcb Protocol control block for the TCP connection to send the ACK
*/
err_t
tcp_send_empty_ack ( struct tcp_pcb * pcb )
{
struct pbuf * p ;
struct tcp_hdr * tcphdr ;
u8_t optlen = 0 ;
# if LWIP_TCP_TIMESTAMPS
if ( pcb - > flags & TF_TIMESTAMP ) {
optlen = LWIP_TCP_OPT_LENGTH ( TF_SEG_OPTS_TS ) ;
}
# endif
p = tcp_output_alloc_header ( pcb , optlen , 0 , htonl ( pcb - > snd_nxt ) ) ;
if ( p = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG , ( " tcp_output: (ACK) could not allocate pbuf \n " ) ) ;
return ERR_BUF ;
}
tcphdr = ( struct tcp_hdr * ) p - > payload ;
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG ,
( " tcp_output: sending ACK for % " U32_F " \n " , pcb - > rcv_nxt ) ) ;
/* remove ACK flags from the PCB, as we send an empty ACK now */
pcb - > flags & = ~ ( TF_ACK_DELAY | TF_ACK_NOW ) ;
/* NB. MSS option is only sent on SYNs, so ignore it here */
# if LWIP_TCP_TIMESTAMPS
pcb - > ts_lastacksent = pcb - > rcv_nxt ;
if ( pcb - > flags & TF_TIMESTAMP ) {
tcp_build_timestamp_option ( pcb , ( u32_t * ) ( tcphdr + 1 ) ) ;
}
# endif
# if CHECKSUM_GEN_TCP
tcphdr - > chksum = inet_chksum_pseudo ( p , & ( pcb - > local_ip ) , & ( pcb - > remote_ip ) ,
IP_PROTO_TCP , p - > tot_len ) ;
# endif
# if LWIP_NETIF_HWADDRHINT
ip_output_hinted ( p , & ( pcb - > local_ip ) , & ( pcb - > remote_ip ) , pcb - > ttl , pcb - > tos ,
IP_PROTO_TCP , & ( pcb - > addr_hint ) ) ;
# else /* LWIP_NETIF_HWADDRHINT*/
ip_output ( p , & ( pcb - > local_ip ) , & ( pcb - > remote_ip ) , pcb - > ttl , pcb - > tos ,
IP_PROTO_TCP ) ;
# endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free ( p ) ;
return ERR_OK ;
}
/**
* Find out what we can send and send it
2015-10-01 07:07:16 +02:00
* <EFBFBD> <EFBFBD> <EFBFBD> Ϳ <EFBFBD> <EFBFBD> ƿ 黺 <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> е ı <EFBFBD> <EFBFBD> Ķ <EFBFBD>
2014-12-22 12:35:05 +01:00
* @ param pcb Protocol control block for the TCP connection to send data
* @ return ERR_OK if data has been sent or nothing to send
* another err_t on error
*/
err_t ICACHE_FLASH_ATTR
tcp_output ( struct tcp_pcb * pcb )
{
struct tcp_seg * seg , * useg ;
u32_t wnd , snd_nxt ;
# if TCP_CWND_DEBUG
s16_t i = 0 ;
# endif /* TCP_CWND_DEBUG */
/* First, check if we are invoked by the TCP input processing
code . If so , we do not output anything . Instead , we rely on the
input processing code to call us when input processing is done
2015-10-01 07:07:16 +02:00
with . <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ƿ 鵱 ǰ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ݱ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ? ֱ <EFBFBD> ӷ <EFBFBD> <EFBFBD> <EFBFBD> */
2014-12-22 12:35:05 +01:00
if ( tcp_input_pcb = = pcb ) {
return ERR_OK ;
}
2015-10-01 07:07:16 +02:00
wnd = LWIP_MIN ( pcb - > snd_wnd , pcb - > cwnd ) ; //<2F> ӷ<EFBFBD> <D3B7> ʹ <EFBFBD> <CDB4> ں<EFBFBD> <DABA> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ȡС <C8A1> ߵ õ<DFB5> <C3B5> <EFBFBD> Ч<EFBFBD> <D0A7> <EFBFBD> ʹ <EFBFBD> <CDB4> <EFBFBD>
2014-12-22 12:35:05 +01:00
seg = pcb - > unsent ;
/* If the TF_ACK_NOW flag is set and no data will be sent (either
* because the - > unsent queue is empty or because the window does
* not allow it ) , construct an empty ACK segment and send it .
*
* If data is to be sent , we will just piggyback the ACK ( see below ) .
*/
if ( pcb - > flags & TF_ACK_NOW & &
( seg = = NULL | |
ntohl ( seg - > tcphdr - > seqno ) - pcb - > lastack + seg - > len > wnd ) ) {
2015-10-01 07:07:16 +02:00
return tcp_send_empty_ack ( pcb ) ; //<2F> <> <EFBFBD> <EFBFBD> ֻ<EFBFBD> <D6BB> ACK<43> ı <EFBFBD> <C4B1> Ķ<EFBFBD>
2014-12-22 12:35:05 +01:00
}
/* useg should point to last segment on unacked queue */
useg = pcb - > unacked ;
if ( useg ! = NULL ) {
2015-10-01 07:07:16 +02:00
for ( ; useg - > next ! = NULL ; useg = useg - > next ) ; //<2F> õ<EFBFBD> β<EFBFBD> <CEB2>
2014-12-22 12:35:05 +01:00
}
# if TCP_OUTPUT_DEBUG
if ( seg = = NULL ) {
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG , ( " tcp_output: nothing to send (%p) \n " ,
( void * ) pcb - > unsent ) ) ;
}
# endif /* TCP_OUTPUT_DEBUG */
# if TCP_CWND_DEBUG
if ( seg = = NULL ) {
LWIP_DEBUGF ( TCP_CWND_DEBUG , ( " tcp_output: snd_wnd % " U16_F
" , cwnd % " U16_F " , wnd % " U32_F
" , seg == NULL, ack % " U32_F " \n " ,
pcb - > snd_wnd , pcb - > cwnd , wnd , pcb - > lastack ) ) ;
} else {
LWIP_DEBUGF ( TCP_CWND_DEBUG ,
( " tcp_output: snd_wnd % " U16_F " , cwnd % " U16_F " , wnd % " U32_F
" , effwnd % " U32_F " , seq % " U32_F " , ack % " U32_F " \n " ,
pcb - > snd_wnd , pcb - > cwnd , wnd ,
ntohl ( seg - > tcphdr - > seqno ) - pcb - > lastack + seg - > len ,
ntohl ( seg - > tcphdr - > seqno ) , pcb - > lastack ) ) ;
}
# endif /* TCP_CWND_DEBUG */
/* data available and window allows it to be sent?
2015-10-01 07:07:16 +02:00
* <EFBFBD> <EFBFBD> ǰ <EFBFBD> <EFBFBD> Ч <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ? <EFBFBD> ķ <EFBFBD> <EFBFBD> ͣ <EFBFBD> ѭ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ͱ <EFBFBD> <EFBFBD> ģ <EFBFBD> ֱ <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> */
2014-12-22 12:35:05 +01:00
while ( seg ! = NULL & &
2016-12-11 21:03:00 +01:00
ntohl ( seg - > tcphdr - > seqno ) - pcb - > lastack + seg - > len < = wnd
& & ( seg - > p - > ref < 2 ) ) {
2014-12-22 12:35:05 +01:00
LWIP_ASSERT ( " RST not expected here! " ,
( TCPH_FLAGS ( seg - > tcphdr ) & TCP_RST ) = = 0 ) ;
/* Stop sending if the nagle algorithm would prevent it
* Don ' t stop :
* - if tcp_write had a memory error before ( prevent delayed ACK timeout ) or
* - if FIN was already enqueued for this PCB ( SYN is always alone in a segment -
* either seg - > next ! = NULL or pcb - > unacked = = NULL ;
* RST is no sent using tcp_write / tcp_output .
*/
if ( ( tcp_do_output_nagle ( pcb ) = = 0 ) & &
( ( pcb - > flags & ( TF_NAGLEMEMERR | TF_FIN ) ) = = 0 ) ) {
break ;
}
# if TCP_CWND_DEBUG
LWIP_DEBUGF ( TCP_CWND_DEBUG , ( " tcp_output: snd_wnd % " U16_F " , cwnd % " U16_F " , wnd % " U32_F " , effwnd % " U32_F " , seq % " U32_F " , ack % " U32_F " , i % " S16_F " \n " ,
pcb - > snd_wnd , pcb - > cwnd , wnd ,
ntohl ( seg - > tcphdr - > seqno ) + seg - > len -
pcb - > lastack ,
ntohl ( seg - > tcphdr - > seqno ) , pcb - > lastack , i ) ) ;
+ + i ;
# endif /* TCP_CWND_DEBUG */
pcb - > unsent = seg - > next ;
if ( pcb - > state ! = SYN_SENT ) {
2015-10-01 07:07:16 +02:00
TCPH_SET_FLAG ( seg - > tcphdr , TCP_ACK ) ; //<2F> <> д<EFBFBD> ײ<EFBFBD> ACK<43> <4B> ־
pcb - > flags & = ~ ( TF_ACK_DELAY | TF_ACK_NOW ) ; //<2F> <> <EFBFBD> <EFBFBD> ־λ
2014-12-22 12:35:05 +01:00
}
2015-10-01 07:07:16 +02:00
tcp_output_segment ( seg , pcb ) ; //<2F> <> <EFBFBD> ú<EFBFBD> <C3BA> <EFBFBD> <EFBFBD> ͱ<EFBFBD> <CDB1> Ķ<EFBFBD>
2014-12-22 12:35:05 +01:00
2015-10-01 07:07:16 +02:00
snd_nxt = ntohl ( seg - > tcphdr - > seqno ) + TCP_TCPLEN ( seg ) ; //<2F> <> <EFBFBD> <EFBFBD> snd_nxt
2014-12-22 12:35:05 +01:00
if ( TCP_SEQ_LT ( pcb - > snd_nxt , snd_nxt ) ) {
2015-10-01 07:07:16 +02:00
pcb - > snd_nxt = snd_nxt ; //<2F> <> <EFBFBD> <EFBFBD> Ҫ<EFBFBD> <D2AA> <EFBFBD> ͵<EFBFBD> <CDB5> <EFBFBD> ݱ<EFBFBD> <DDB1>
2014-12-22 12:35:05 +01:00
}
2015-10-01 07:07:16 +02:00
/* put segment on unacknowledged list if length > 0
*/
2014-12-22 12:35:05 +01:00
if ( TCP_TCPLEN ( seg ) > 0 ) {
seg - > next = NULL ;
2015-10-01 07:07:16 +02:00
/* unacked list is empty? ֱ<> ӹҽ <D3B9> */
2014-12-22 12:35:05 +01:00
if ( pcb - > unacked = = NULL ) {
pcb - > unacked = seg ;
useg = seg ;
2015-10-01 07:07:16 +02:00
/* unacked list is not empty?<3F> <> <EFBFBD> <EFBFBD> ǰ<EFBFBD> <C7B0> <EFBFBD> İ<EFBFBD> ˳<EFBFBD> <CBB3> <EFBFBD> <EFBFBD> ֯<EFBFBD> ڶ<EFBFBD> <DAB6> <EFBFBD> <EFBFBD> <EFBFBD> */
2014-12-22 12:35:05 +01:00
} else {
/* In the case of fast retransmit, the packet should not go to the tail
* of the unacked queue , but rather somewhere before it . We need to check for
2015-10-01 07:07:16 +02:00
* this case . - STJ Jul 27 , 2004 */ //<2F> <> <EFBFBD> ǰ<EFBFBD> <C7B0> <EFBFBD> ĵ<EFBFBD> <C4B5> <EFBFBD> <EFBFBD> кŵ<D0BA> <C5B5> ڶ<EFBFBD> <DAB6> <EFBFBD> β<EFBFBD> <CEB2> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> кţ<D0BA>
//<2F> Ӷ<EFBFBD> <D3B6> <EFBFBD> <EFBFBD> ײ<EFBFBD> <D7B2> <EFBFBD> ʼ
2014-12-22 12:35:05 +01:00
if ( TCP_SEQ_LT ( ntohl ( seg - > tcphdr - > seqno ) , ntohl ( useg - > tcphdr - > seqno ) ) ) {
/* add segment to before tail of unacked list, keeping the list sorted */
struct tcp_seg * * cur_seg = & ( pcb - > unacked ) ;
while ( * cur_seg & &
TCP_SEQ_LT ( ntohl ( ( * cur_seg ) - > tcphdr - > seqno ) , ntohl ( seg - > tcphdr - > seqno ) ) ) {
cur_seg = & ( ( * cur_seg ) - > next ) ;
}
seg - > next = ( * cur_seg ) ;
( * cur_seg ) = seg ;
2015-10-01 07:07:16 +02:00
} else { //<2F> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ߣ<EFBFBD> <DFA3> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> δȷ<CEB4> ϶<EFBFBD> <CFB6> <EFBFBD> ĩβ
2014-12-22 12:35:05 +01:00
/* add segment to tail of unacked list */
useg - > next = seg ;
useg = useg - > next ;
}
}
/* do not queue empty segments on the unacked list */
2015-10-01 07:07:16 +02:00
} else { //<2F> <> <EFBFBD> Ķγ <C4B6> <CEB3> <EFBFBD> Ϊ0<CEAA> <30> ֱ<EFBFBD> <D6B1> ɾ<EFBFBD> <C9BE> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ش<EFBFBD>
2014-12-22 12:35:05 +01:00
tcp_seg_free ( seg ) ;
}
2015-10-01 07:07:16 +02:00
seg = pcb - > unsent ; //<2F> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> һ <EFBFBD> <D2BB> <EFBFBD> <EFBFBD> <EFBFBD> Ķ<EFBFBD>
2014-12-22 12:35:05 +01:00
}
# if TCP_OVERSIZE
if ( pcb - > unsent = = NULL ) {
/* last unsent has been removed, reset unsent_oversize */
pcb - > unsent_oversize = 0 ;
}
# endif /* TCP_OVERSIZE */
2015-10-01 07:07:16 +02:00
//<2F> <> <EFBFBD> ʹ <EFBFBD> <CDB4> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> ±<EFBFBD> <C2B1> IJ<EFBFBD> <C4B2> ܷ<EFBFBD> <DCB7> ͣ<EFBFBD> <CDA3> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> 㴰<EFBFBD> <E3B4B0> ̽<EFBFBD> ⡣
2014-12-22 12:35:05 +01:00
if ( seg ! = NULL & & pcb - > persist_backoff = = 0 & &
ntohl ( seg - > tcphdr - > seqno ) - pcb - > lastack + seg - > len > pcb - > snd_wnd ) {
/* prepare for persist timer */
pcb - > persist_cnt = 0 ;
pcb - > persist_backoff = 1 ;
}
2015-10-01 07:07:16 +02:00
pcb - > flags & = ~ TF_NAGLEMEMERR ; //<2F> <> <EFBFBD> ڴ<EFBFBD> <DAB4> <EFBFBD> <EFBFBD> <EFBFBD> ־
2014-12-22 12:35:05 +01:00
return ERR_OK ;
}
/**
* Called by tcp_output ( ) to actually send a TCP segment over IP .
*
* @ param seg the tcp_seg to send
* @ param pcb the tcp_pcb for the TCP connection used to send the segment
*/
static void
tcp_output_segment ( struct tcp_seg * seg , struct tcp_pcb * pcb )
{
u16_t len ;
struct netif * netif ;
u32_t * opts ;
/** @bug Exclude retransmitted segments from this count. */
snmp_inc_tcpoutsegs ( ) ;
/* The TCP header has already been constructed, but the ackno and
wnd fields remain . */
seg - > tcphdr - > ackno = htonl ( pcb - > rcv_nxt ) ;
/* advertise our receive window size in this TCP segment */
seg - > tcphdr - > wnd = htons ( pcb - > rcv_ann_wnd ) ;
pcb - > rcv_ann_right_edge = pcb - > rcv_nxt + pcb - > rcv_ann_wnd ;
/* Add any requested options. NB MSS option is only set on SYN
packets , so ignore it here */
LWIP_ASSERT ( " seg->tcphdr not aligned " , ( ( mem_ptr_t ) seg - > tcphdr % MEM_ALIGNMENT ) = = 0 ) ;
opts = ( u32_t * ) ( void * ) ( seg - > tcphdr + 1 ) ;
if ( seg - > flags & TF_SEG_OPTS_MSS ) {
TCP_BUILD_MSS_OPTION ( * opts ) ;
opts + = 1 ;
}
# if LWIP_TCP_TIMESTAMPS
pcb - > ts_lastacksent = pcb - > rcv_nxt ;
if ( seg - > flags & TF_SEG_OPTS_TS ) {
tcp_build_timestamp_option ( pcb , opts ) ;
opts + = 3 ;
}
# endif
/* Set retransmission timer running if it is not currently enabled
This must be set before checking the route . modify by ives at 2014.4 .24 */
if ( pcb - > rtime = = - 1 ) {
pcb - > rtime = 0 ;
}
/* If we don't have a local IP address, we get one by
calling ip_route ( ) . */
if ( ip_addr_isany ( & ( pcb - > local_ip ) ) ) {
netif = ip_route ( & ( pcb - > remote_ip ) ) ;
if ( netif = = NULL ) {
return ;
}
ip_addr_copy ( pcb - > local_ip , netif - > ip_addr ) ;
}
if ( pcb - > rttest = = 0 ) {
pcb - > rttest = tcp_ticks ;
pcb - > rtseq = ntohl ( seg - > tcphdr - > seqno ) ;
LWIP_DEBUGF ( TCP_RTO_DEBUG , ( " tcp_output_segment: rtseq % " U32_F " \n " , pcb - > rtseq ) ) ;
}
LWIP_DEBUGF ( TCP_OUTPUT_DEBUG , ( " tcp_output_segment: % " U32_F " :% " U32_F " \n " ,
htonl ( seg - > tcphdr - > seqno ) , htonl ( seg - > tcphdr - > seqno ) +
seg - > len ) ) ;
len = ( u16_t ) ( ( u8_t * ) seg - > tcphdr - ( u8_t * ) seg - > p - > payload ) ;
seg - > p - > len - = len ;
seg - > p - > tot_len - = len ;
seg - > p - > payload = seg - > tcphdr ;
seg - > tcphdr - > chksum = 0 ;
# if CHECKSUM_GEN_TCP
# if TCP_CHECKSUM_ON_COPY
{
u32_t acc ;
# if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
u16_t chksum_slow = inet_chksum_pseudo ( seg - > p , & ( pcb - > local_ip ) ,
& ( pcb - > remote_ip ) ,
IP_PROTO_TCP , seg - > p - > tot_len ) ;
# endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
if ( ( seg - > flags & TF_SEG_DATA_CHECKSUMMED ) = = 0 ) {
LWIP_ASSERT ( " data included but not checksummed " ,
seg - > p - > tot_len = = ( TCPH_HDRLEN ( seg - > tcphdr ) * 4 ) ) ;
}
/* rebuild TCP header checksum (TCP header changes for retransmissions!) */
acc = inet_chksum_pseudo_partial ( seg - > p , & ( pcb - > local_ip ) ,
& ( pcb - > remote_ip ) ,
IP_PROTO_TCP , seg - > p - > tot_len , TCPH_HDRLEN ( seg - > tcphdr ) * 4 ) ;
/* add payload checksum */
if ( seg - > chksum_swapped ) {
seg - > chksum = SWAP_BYTES_IN_WORD ( seg - > chksum ) ;
seg - > chksum_swapped = 0 ;
}
acc + = ( u16_t ) ~ ( seg - > chksum ) ;
seg - > tcphdr - > chksum = FOLD_U32T ( acc ) ;
# if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
if ( chksum_slow ! = seg - > tcphdr - > chksum ) {
LWIP_DEBUGF ( TCP_DEBUG | LWIP_DBG_LEVEL_WARNING ,
( " tcp_output_segment: calculated checksum is % " X16_F " instead of % " X16_F " \n " ,
seg - > tcphdr - > chksum , chksum_slow ) ) ;
seg - > tcphdr - > chksum = chksum_slow ;
}
# endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
}
# else /* TCP_CHECKSUM_ON_COPY */
seg - > tcphdr - > chksum = inet_chksum_pseudo ( seg - > p , & ( pcb - > local_ip ) ,
& ( pcb - > remote_ip ) ,
IP_PROTO_TCP , seg - > p - > tot_len ) ;
# endif /* TCP_CHECKSUM_ON_COPY */
# endif /* CHECKSUM_GEN_TCP */
TCP_STATS_INC ( tcp . xmit ) ;
# if LWIP_NETIF_HWADDRHINT
ip_output_hinted ( seg - > p , & ( pcb - > local_ip ) , & ( pcb - > remote_ip ) , pcb - > ttl , pcb - > tos ,
IP_PROTO_TCP , & ( pcb - > addr_hint ) ) ;
# else /* LWIP_NETIF_HWADDRHINT*/
ip_output ( seg - > p , & ( pcb - > local_ip ) , & ( pcb - > remote_ip ) , pcb - > ttl , pcb - > tos ,
IP_PROTO_TCP ) ;
# endif /* LWIP_NETIF_HWADDRHINT*/
}
/**
* Send a TCP RESET packet ( empty segment with RST flag set ) either to
* abort a connection or to show that there is no matching local connection
* for a received segment .
*
* Called by tcp_abort ( ) ( to abort a local connection ) , tcp_input ( ) ( if no
* matching local pcb was found ) , tcp_listen_input ( ) ( if incoming segment
* has ACK flag set ) and tcp_process ( ) ( received segment in the wrong state )
*
* Since a RST segment is in most cases not sent for an active connection ,
* tcp_rst ( ) has a number of arguments that are taken from a tcp_pcb for
* most other segment output functions .
*
* @ param seqno the sequence number to use for the outgoing segment
* @ param ackno the acknowledge number to use for the outgoing segment
* @ param local_ip the local IP address to send the segment from
* @ param remote_ip the remote IP address to send the segment to
* @ param local_port the local TCP port to send the segment from
* @ param remote_port the remote TCP port to send the segment to
*/
void
tcp_rst ( u32_t seqno , u32_t ackno ,
ip_addr_t * local_ip , ip_addr_t * remote_ip ,
u16_t local_port , u16_t remote_port )
{
struct pbuf * p ;
struct tcp_hdr * tcphdr ;
p = pbuf_alloc ( PBUF_IP , TCP_HLEN , PBUF_RAM ) ;
if ( p = = NULL ) {
LWIP_DEBUGF ( TCP_DEBUG , ( " tcp_rst: could not allocate memory for pbuf \n " ) ) ;
return ;
}
LWIP_ASSERT ( " check that first pbuf can hold struct tcp_hdr " ,
( p - > len > = sizeof ( struct tcp_hdr ) ) ) ;
tcphdr = ( struct tcp_hdr * ) p - > payload ;
tcphdr - > src = htons ( local_port ) ;
tcphdr - > dest = htons ( remote_port ) ;
tcphdr - > seqno = htonl ( seqno ) ;
tcphdr - > ackno = htonl ( ackno ) ;
TCPH_HDRLEN_FLAGS_SET ( tcphdr , TCP_HLEN / 4 , TCP_RST | TCP_ACK ) ;
tcphdr - > wnd = PP_HTONS ( TCP_WND ) ;
tcphdr - > chksum = 0 ;
tcphdr - > urgp = 0 ;
# if CHECKSUM_GEN_TCP
tcphdr - > chksum = inet_chksum_pseudo ( p , local_ip , remote_ip ,
IP_PROTO_TCP , p - > tot_len ) ;
# endif
TCP_STATS_INC ( tcp . xmit ) ;
snmp_inc_tcpoutrsts ( ) ;
/* Send output with hardcoded TTL since we have no access to the pcb */
ip_output ( p , local_ip , remote_ip , TCP_TTL , 0 , IP_PROTO_TCP ) ;
pbuf_free ( p ) ;
LWIP_DEBUGF ( TCP_RST_DEBUG , ( " tcp_rst: seqno % " U32_F " ackno % " U32_F " . \n " , seqno , ackno ) ) ;
}
/**
* Requeue all unacked segments for retransmission
*
* Called by tcp_slowtmr ( ) for slow retransmission .
*
* @ param pcb the tcp_pcb for which to re - enqueue all unacked segments
*/
void
tcp_rexmit_rto ( struct tcp_pcb * pcb )
{
struct tcp_seg * seg ;
2015-10-01 07:07:16 +02:00
struct tcp_seg * t0_head = NULL , * t0_tail = NULL ; /* keep in unacked */
struct tcp_seg * t1_head = NULL , * t1_tail = NULL ; /* link to unsent */
bool t0_1st = true , t1_1st = true ;
2014-12-22 12:35:05 +01:00
if ( pcb - > unacked = = NULL ) {
return ;
}
2015-10-01 07:07:16 +02:00
# if 1 /* by Snake: resolve the bug of pbuf reuse */
seg = pcb - > unacked ;
while ( seg ! = NULL ) {
if ( seg - > p - > eb ) {
if ( t0_1st ) {
t0_head = t0_tail = seg ;
t0_1st = false ;
} else {
t0_tail - > next = seg ;
t0_tail = seg ;
}
seg = seg - > next ;
t0_tail - > next = NULL ;
} else {
if ( t1_1st ) {
t1_head = t1_tail = seg ;
t1_1st = false ;
} else {
t1_tail - > next = seg ;
t1_tail = seg ;
}
seg = seg - > next ;
t1_tail - > next = NULL ;
}
}
if ( t1_head & & t1_tail ) {
t1_tail - > next = pcb - > unsent ;
pcb - > unsent = t1_head ;
}
pcb - > unacked = t0_head ;
# else
2014-12-22 12:35:05 +01:00
/* Move all unacked segments to the head of the unsent queue */
for ( seg = pcb - > unacked ; seg - > next ! = NULL ; seg = seg - > next ) ;
/* concatenate unsent queue after unacked queue */
seg - > next = pcb - > unsent ;
/* unsent queue is the concatenated queue (of unacked, unsent) */
pcb - > unsent = pcb - > unacked ;
/* unacked queue is now empty */
pcb - > unacked = NULL ;
2015-10-01 07:07:16 +02:00
# endif
2016-12-11 21:03:00 +01:00
/* last unsent hasn't changed, no need to reset unsent_oversize */
2014-12-22 12:35:05 +01:00
/* increment number of retransmissions */
+ + pcb - > nrtx ;
/* Don't take any RTT measurements after retransmitting. */
pcb - > rttest = 0 ;
/* Do the actual retransmission */
tcp_output ( pcb ) ;
}
/**
* Requeue the first unacked segment for retransmission
*
* Called by tcp_receive ( ) for fast retramsmit .
*
* @ param pcb the tcp_pcb for which to retransmit the first unacked segment
*/
void
tcp_rexmit ( struct tcp_pcb * pcb )
{
struct tcp_seg * seg ;
struct tcp_seg * * cur_seg ;
if ( pcb - > unacked = = NULL ) {
return ;
}
/* Move the first unacked segment to the unsent queue */
/* Keep the unsent queue sorted. */
seg = pcb - > unacked ;
pcb - > unacked = seg - > next ;
cur_seg = & ( pcb - > unsent ) ;
while ( * cur_seg & &
TCP_SEQ_LT ( ntohl ( ( * cur_seg ) - > tcphdr - > seqno ) , ntohl ( seg - > tcphdr - > seqno ) ) ) {
cur_seg = & ( ( * cur_seg ) - > next ) ;
}
seg - > next = * cur_seg ;
* cur_seg = seg ;
+ + pcb - > nrtx ;
/* Don't take any rtt measurements after retransmitting. */
pcb - > rttest = 0 ;
/* Do the actual retransmission. */
snmp_inc_tcpretranssegs ( ) ;
/* No need to call tcp_output: we are always called from tcp_input()
and thus tcp_output directly returns . */
}
/**
* Handle retransmission after three dupacks received
*
* @ param pcb the tcp_pcb for which to retransmit the first unacked segment
*/
void
tcp_rexmit_fast ( struct tcp_pcb * pcb )
{
if ( pcb - > unacked ! = NULL & & ! ( pcb - > flags & TF_INFR ) ) {
/* This is fast retransmit. Retransmit the first unacked segment. */
LWIP_DEBUGF ( TCP_FR_DEBUG ,
( " tcp_receive: dupacks % " U16_F " (% " U32_F
" ), fast retransmit % " U32_F " \n " ,
( u16_t ) pcb - > dupacks , pcb - > lastack ,
ntohl ( pcb - > unacked - > tcphdr - > seqno ) ) ) ;
tcp_rexmit ( pcb ) ;
/* Set ssthresh to half of the minimum of the current
* cwnd and the advertised window */
if ( pcb - > cwnd > pcb - > snd_wnd ) {
pcb - > ssthresh = pcb - > snd_wnd / 2 ;
} else {
pcb - > ssthresh = pcb - > cwnd / 2 ;
}
/* The minimum value for ssthresh should be 2 MSS */
if ( pcb - > ssthresh < 2 * pcb - > mss ) {
LWIP_DEBUGF ( TCP_FR_DEBUG ,
( " tcp_receive: The minimum value for ssthresh % " U16_F
" should be min 2 mss % " U16_F " ... \n " ,
pcb - > ssthresh , 2 * pcb - > mss ) ) ;
pcb - > ssthresh = 2 * pcb - > mss ;
}
pcb - > cwnd = pcb - > ssthresh + 3 * pcb - > mss ;
pcb - > flags | = TF_INFR ;
}
}
/**
* Send keepalive packets to keep a connection active although
* no data is sent over it .
*
* Called by tcp_slowtmr ( )
*
* @ param pcb the tcp_pcb for which to send a keepalive packet
*/
void
tcp_keepalive ( struct tcp_pcb * pcb )
{
struct pbuf * p ;
struct tcp_hdr * tcphdr ;
LWIP_DEBUGF ( TCP_DEBUG , ( " tcp_keepalive: sending KEEPALIVE probe to % " U16_F " .% " U16_F " .% " U16_F " .% " U16_F " \n " ,
ip4_addr1_16 ( & pcb - > remote_ip ) , ip4_addr2_16 ( & pcb - > remote_ip ) ,
ip4_addr3_16 ( & pcb - > remote_ip ) , ip4_addr4_16 ( & pcb - > remote_ip ) ) ) ;
LWIP_DEBUGF ( TCP_DEBUG , ( " tcp_keepalive: tcp_ticks % " U32_F " pcb->tmr % " U32_F " pcb->keep_cnt_sent % " U16_F " \n " ,
tcp_ticks , pcb - > tmr , pcb - > keep_cnt_sent ) ) ;
p = tcp_output_alloc_header ( pcb , 0 , 0 , htonl ( pcb - > snd_nxt - 1 ) ) ;
if ( p = = NULL ) {
LWIP_DEBUGF ( TCP_DEBUG ,
( " tcp_keepalive: could not allocate memory for pbuf \n " ) ) ;
return ;
}
tcphdr = ( struct tcp_hdr * ) p - > payload ;
# if CHECKSUM_GEN_TCP
tcphdr - > chksum = inet_chksum_pseudo ( p , & pcb - > local_ip , & pcb - > remote_ip ,
IP_PROTO_TCP , p - > tot_len ) ;
# endif
TCP_STATS_INC ( tcp . xmit ) ;
/* Send output to IP */
# if LWIP_NETIF_HWADDRHINT
ip_output_hinted ( p , & pcb - > local_ip , & pcb - > remote_ip , pcb - > ttl , 0 , IP_PROTO_TCP ,
& ( pcb - > addr_hint ) ) ;
# else /* LWIP_NETIF_HWADDRHINT*/
ip_output ( p , & pcb - > local_ip , & pcb - > remote_ip , pcb - > ttl , 0 , IP_PROTO_TCP ) ;
# endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free ( p ) ;
LWIP_DEBUGF ( TCP_DEBUG , ( " tcp_keepalive: seqno % " U32_F " ackno % " U32_F " . \n " ,
pcb - > snd_nxt - 1 , pcb - > rcv_nxt ) ) ;
}
/**
* Send persist timer zero - window probes to keep a connection active
* when a window update is lost .
*
* Called by tcp_slowtmr ( )
*
* @ param pcb the tcp_pcb for which to send a zero - window probe packet
*/
void
tcp_zero_window_probe ( struct tcp_pcb * pcb )
{
struct pbuf * p ;
struct tcp_hdr * tcphdr ;
struct tcp_seg * seg ;
2015-10-12 05:31:04 +02:00
u16_t offset = 0 ;
2014-12-22 12:35:05 +01:00
u16_t len ;
u8_t is_fin ;
LWIP_DEBUGF ( TCP_DEBUG ,
( " tcp_zero_window_probe: sending ZERO WINDOW probe to % "
U16_F " .% " U16_F " .% " U16_F " .% " U16_F " \n " ,
ip4_addr1_16 ( & pcb - > remote_ip ) , ip4_addr2_16 ( & pcb - > remote_ip ) ,
ip4_addr3_16 ( & pcb - > remote_ip ) , ip4_addr4_16 ( & pcb - > remote_ip ) ) ) ;
LWIP_DEBUGF ( TCP_DEBUG ,
( " tcp_zero_window_probe: tcp_ticks % " U32_F
" pcb->tmr % " U32_F " pcb->keep_cnt_sent % " U16_F " \n " ,
tcp_ticks , pcb - > tmr , pcb - > keep_cnt_sent ) ) ;
seg = pcb - > unacked ;
if ( seg = = NULL ) {
seg = pcb - > unsent ;
2015-10-12 05:31:04 +02:00
} else {
struct ip_hdr * iphdr = NULL ;
iphdr = ( struct ip_hdr * ) ( ( char * ) seg - > p - > payload + SIZEOF_ETH_HDR ) ;
offset = IPH_HL ( iphdr ) * 4 ;
offset + = SIZEOF_ETH_HDR ;
2014-12-22 12:35:05 +01:00
}
if ( seg = = NULL ) {
return ;
}
is_fin = ( ( TCPH_FLAGS ( seg - > tcphdr ) & TCP_FIN ) ! = 0 ) & & ( seg - > len = = 0 ) ;
/* we want to send one seqno: either FIN or data (no options) */
len = is_fin ? 0 : 1 ;
p = tcp_output_alloc_header ( pcb , 0 , len , seg - > tcphdr - > seqno ) ;
if ( p = = NULL ) {
LWIP_DEBUGF ( TCP_DEBUG , ( " tcp_zero_window_probe: no memory for pbuf \n " ) ) ;
return ;
}
tcphdr = ( struct tcp_hdr * ) p - > payload ;
if ( is_fin ) {
/* FIN segment, no data */
TCPH_FLAGS_SET ( tcphdr , TCP_ACK | TCP_FIN ) ;
} else {
/* Data segment, copy in one byte from the head of the unacked queue */
struct tcp_hdr * thdr = ( struct tcp_hdr * ) seg - > p - > payload ;
char * d = ( ( char * ) p - > payload + TCP_HLEN ) ;
2015-10-12 05:31:04 +02:00
if ( pcb - > unacked = = NULL )
pbuf_copy_partial ( seg - > p , d , 1 , TCPH_HDRLEN ( thdr ) * 4 ) ;
else {
thdr = ( struct tcp_hdr * ) ( ( char * ) seg - > p - > payload + offset ) ;
pbuf_copy_partial ( seg - > p , d , 1 , TCPH_HDRLEN ( thdr ) * 4 + offset ) ;
}
2014-12-22 12:35:05 +01:00
}
# if CHECKSUM_GEN_TCP
tcphdr - > chksum = inet_chksum_pseudo ( p , & pcb - > local_ip , & pcb - > remote_ip ,
IP_PROTO_TCP , p - > tot_len ) ;
# endif
TCP_STATS_INC ( tcp . xmit ) ;
/* Send output to IP */
# if LWIP_NETIF_HWADDRHINT
ip_output_hinted ( p , & pcb - > local_ip , & pcb - > remote_ip , pcb - > ttl , 0 , IP_PROTO_TCP ,
& ( pcb - > addr_hint ) ) ;
# else /* LWIP_NETIF_HWADDRHINT*/
ip_output ( p , & pcb - > local_ip , & pcb - > remote_ip , pcb - > ttl , 0 , IP_PROTO_TCP ) ;
# endif /* LWIP_NETIF_HWADDRHINT*/
pbuf_free ( p ) ;
LWIP_DEBUGF ( TCP_DEBUG , ( " tcp_zero_window_probe: seqno % " U32_F
" ackno % " U32_F " . \n " ,
pcb - > snd_nxt - 1 , pcb - > rcv_nxt ) ) ;
}
# endif /* LWIP_TCP */