diff --git a/src/pcbuf.c b/src/pcbuf.c index ee4f3ee7..a0b727df 100644 --- a/src/pcbuf.c +++ b/src/pcbuf.c @@ -173,7 +173,7 @@ size_t W32_CALL sock_setbuf (sock_type *s, BYTE *rx_buf, size_t rx_len) } else { - size_t len = min (rx_len, USHRT_MAX-1) - 8; + size_t len = min (rx_len, 2 * USHRT_MAX) - 8; *(DWORD*)rx_buf = SAFETY_TCP; *(DWORD*)(rx_buf+4+len) = SAFETY_TCP; diff --git a/src/pctcp.c b/src/pctcp.c index 7fe2e92a..0caaef46 100644 --- a/src/pctcp.c +++ b/src/pctcp.c @@ -144,7 +144,6 @@ W32_CLANG_PACK_WARN_DEF() static void tcp_no_arp (_tcp_Socket *s); static void tcp_rtt_win (_tcp_Socket *s); - static void tcp_upd_win (_tcp_Socket *s, unsigned line); static BOOL tcp_checksum (const in_Header *ip, const tcp_Header *tcp, int len); #endif @@ -826,8 +825,7 @@ _tcp_Socket *_tcp_handler (const in_Header *ip, BOOL broadcast) } if (flags & tcp_FlagPUSH) /* EE 2002.2.28 */ - s->locflags |= LF_GOT_PUSH; - else s->locflags &= ~LF_GOT_PUSH; + s->locflags |= LF_GOT_PUSH; tcp_rtt_win (s); /* update retrans timer, windows etc. */ @@ -1273,17 +1271,6 @@ void tcp_Retransmitter (BOOL force) continue; } - /* Need to send a window update? Because we advertised a 0 window - * in a previous _tcp_send() (but only in ESTAB state). - */ - if ((s->locflags & LF_WINUPDATE) && sock_rbleft((sock_type*)s) > 0) - { - STAT (tcpstats.tcps_sndwinup++); - s->locflags &= ~LF_WINUPDATE; - s->flags |= tcp_FlagACK; - TCP_SEND (s); - } - else if (s->tx_datalen > 0 || s->unhappy || s->karn_count == 1) { if (chk_timeout(s->rtt_time)) /* retransmission timeout */ @@ -1835,22 +1822,37 @@ static int tcp_read (_tcp_Socket *s, BYTE *buf, int maxlen) { int to_move; + s->rx_datalen -= len; + + if (s->recv_win < (long)s->adv_win) + { + /* ACK every second segment, or when PUSH received. + */ + if (s->adv_win - s->recv_win > s->max_seg || + (s->locflags & LF_GOT_PUSH)) + TCP_SEND (s); + else + TCP_SENDSOON (s); + } + else if (s->rx_datalen == 0 && + s->recv_win <= 2 * s->max_seg) + { + /* Update window now if it was (nearly) closed. + */ + TCP_SEND (s); + } + + s->locflags &= ~LF_GOT_PUSH; + if (buf) memcpy (buf, s->rx_data, len); - s->rx_datalen -= len; to_move = s->rx_datalen; if (s->missed_seq[0] != s->missed_seq[1]) to_move += s->missed_seq[1] - s->recv_next; if (to_move > 0) - { - memmove (s->rx_data, s->rx_data + len, to_move); - - TCP_SENDSOON (s); /* delayed ACK */ - } - else - tcp_upd_win (s, __LINE__); + memmove (s->rx_data, s->rx_data + len, to_move); } else if (s->state == tcp_StateCLOSWT) _tcp_close (s); @@ -1981,7 +1983,7 @@ static void tcp_sockreset (_tcp_Socket *s, BOOL proxy) s->unhappy = FALSE; s->hisport = 0; s->hisaddr = 0UL; - s->locflags &= ~(LF_WINUPDATE | LF_KEEPALIVE | LF_GOT_FIN | LF_GOT_ICMP); + s->locflags &= ~(LF_KEEPALIVE | LF_GOT_FIN | LF_GOT_ICMP); s->datatimer = 0UL; s->inactive_to = 0; @@ -2145,20 +2147,6 @@ static void tcp_rtt_win (_tcp_Socket *s) s->datatimer = 0UL; /* resetting tx-timer, EE 99.08.23 */ } -/** - * Check if receive window needs an update. - */ -static void tcp_upd_win (_tcp_Socket *s, unsigned line) -{ - UINT winfree = s->max_rx_data - (UINT)s->rx_datalen; - - if (winfree < s->max_seg/2) - { - _tcp_send (s, __FILE__, line); /* update window now */ - TRACE_CONSOLE (2, "tcp_upd_win(%d): win-free %u\n", line, winfree); - } -} - /** * TCP option routines. * \note Each of these \b MUST add multiple of 4 bytes of options. @@ -2364,10 +2352,29 @@ int _tcp_send (_tcp_Socket *s, const char *file, unsigned line) int opt_len; /* total length of TCP options */ int pkt_num; /* 0 .. s->cwindow-1 */ int rtt; + int new_window; s->recent = 0; dst = (_pktserial ? NULL : &s->his_ethaddr); + new_window = s->max_rx_data - s->rx_datalen; + if (s->max_rx_data >= s->max_seg * 4) + { + /* For large buffers, use half the buffer size, and round to a + * multiple of MSS. Only update if the difference is at least + * one segment (recommended by RFC 1122). + */ + new_window = min (new_window, s->max_rx_data / 2); + if (new_window > s->max_seg) + { + if (new_window - s->recv_win >= s->max_seg) + new_window -= new_window % s->max_seg; + else + new_window = s->recv_win; + } + } + s->adv_win = s->recv_win = new_window; + #if defined(USE_IPV6) if (s->is_ip6) { @@ -2425,7 +2432,6 @@ int _tcp_send (_tcp_Socket *s, const char *file, unsigned line) tcp->seqnum = intel (s->send_next + start_data); /* unacked - no longer send_tot_len */ tcp->acknum = intel (s->recv_next); - s->adv_win = s->max_rx_data - s->rx_datalen; /* Our new advertised recv window */ tcp->window = intel16 ((WORD)s->adv_win); tcp->flags = (BYTE) (s->flags & ~tcp_FlagPUSH); tcp->unused = 0; @@ -2461,7 +2467,7 @@ int _tcp_send (_tcp_Socket *s, const char *file, unsigned line) memcpy (data, s->tx_queue+start_data, send_data_len); else memcpy (data, s->tx_data +start_data, send_data_len); - if (send_data_len == send_tot_data && + if ((unsigned)start_data + send_data_len == s->tx_datalen && (s->flags & tcp_FlagPUSH)) { /* Set PUSH bit on last segment. diff --git a/src/socket.c b/src/socket.c index 0277c916..3027359a 100644 --- a/src/socket.c +++ b/src/socket.c @@ -231,7 +231,7 @@ BOOL _sock_dos_fd (int s) */ int _sock_set_rcv_buf (sock_type *s, size_t len) { - len = min (len+8,USHRT_MAX); /* add room for head/tail markers */ + len = 2 * min (len, USHRT_MAX) + 8; /* add room for head/tail markers */ return sock_setbuf (s, (BYTE*)malloc(len), len); /* Note: the ret-val from above malloc() is freed below. diff --git a/src/tcp_fsm.c b/src/tcp_fsm.c index ed3111d2..db2ea37e 100644 --- a/src/tcp_fsm.c +++ b/src/tcp_fsm.c @@ -330,7 +330,7 @@ static int tcp_estab_state (_tcp_Socket **sp, const in_Header *ip, _tcp_Socket *s = *sp; int len; long ldiff; /* how much still ACK'ed */ - BOOL did_tx; + BOOL ack_scheduled = s->recv_win < (int)s->adv_win; /* handle lost SYN */ @@ -364,13 +364,21 @@ static int tcp_estab_state (_tcp_Socket **sp, const in_Header *ip, len = intel16 (ip->length) - in_GetHdrLen (ip); else len = intel16 (ip6->len); - if (tcp_process_data (s, tcp, len, &flags) < 0) + len = tcp_process_data (s, tcp, len, &flags); + if (len < 0) { - TCP_SEND (s); /* An out-of-order or missing segment; do fast ACK */ + /* An out-of-order or missing segment; do fast ACK. + * Three should be enough. If those get dropped, + * retransmitter will send more eventually. + */ + if (s->dup_acks < 3) + { + TCP_SEND (s); + ++s->dup_acks; + } return (1); } - - did_tx = FALSE; + s->dup_acks = 0; if (s->state != tcp_StateCLOSWT && (flags & tcp_FlagFIN) && @@ -385,7 +393,6 @@ static int tcp_estab_state (_tcp_Socket **sp, const in_Header *ip, /* Implied CLOSE-WAIT -> LAST-ACK transition here */ TCP_SEND (s); - did_tx = TRUE; TRACE_CONSOLE (2, "tcp_estab_state(): got FIN\n"); @@ -394,63 +401,47 @@ static int tcp_estab_state (_tcp_Socket **sp, const in_Header *ip, s->unhappy = TRUE; s->timeout = set_timeout (tcp_LASTACK_TIME); /* Added AGW 6 Jan 2001 */ s->state = tcp_StateLASTACK; + + return (0); } else { s->unhappy = TRUE; TCP_SEND (s); /* force a retransmit, no state change */ - did_tx = TRUE; + return (0); } } - /* - * Eliminate the spurious ACK messages bug. - * For the window update, the length should be the - * data length only, so exclude the TCP header size - * -- Joe + /* Send fast-ACK after retransmission. */ - len -= (tcp->offset << 2); + if (len > 0 && s->missed_seq[0] != s->missed_seq[1]) + { + TCP_SEND (s); + return (0); + } /* Peer ACKed some of our data, continue sending more. */ if (ldiff > 0 && s->tx_datalen > (unsigned long)s->send_una) - goto send_now; + { + s->karn_count = 0; + TCP_SEND (s); + return (0); + } - /* Send ACK for received data. + /* Schedule delayed-ACK for received data, in case + * tcp_read() doesn't get to it in time. */ if (len > 0) { - /* Need to ACK and update window, but how urgent ?? - * We need a better criteria for doing Fast-ACK. - */ - if (s->adv_win < s->max_seg) - { - send_now: - TRACE_FILE ("tcp_estab_state (%u): FastACK: ldiff %ld, " - "UNA %ld, MS-right %ld\n", - __LINE__, ldiff, s->send_una, - s->missed_seq[0] != s->missed_seq[1] ? - (u_long)(s->missed_seq[0] - s->recv_next) : 0); - s->karn_count = 0; - TCP_SEND (s); - did_tx = TRUE; - - if (s->adv_win == 0) /* need to open closed window in retransmitter */ - { - s->locflags |= LF_WINUPDATE; - s->unhappy = TRUE; - } - } - else - { - TCP_SENDSOON (s); /* delayed ACK */ - did_tx = TRUE; - } + if (!ack_scheduled) + TCP_SENDSOON (s); + return (0); } /* Check if we need to reply to keep-alive segment */ - if (!did_tx && (len == 0) && (seqnum == s->recv_next-1) && + if ((seqnum == s->recv_next-1) && ((flags & tcp_FlagACK) == tcp_FlagACK) && /* ACK only */ (s->state == tcp_StateESTAB)) { @@ -866,13 +857,14 @@ copy_in_order (_tcp_Socket *s, const BYTE *data, unsigned len) memcpy (s->rx_data + s->rx_datalen, data, len); s->recv_next += len; s->rx_datalen += len; + s->recv_win -= len; } /* * Handle any new data that increments the 'recv_next' index. * 'ldiff >= 0'. */ -static void +static int data_in_order (_tcp_Socket *s, const BYTE *data, unsigned len, unsigned diff) { /* Skip data before recv_next. We must be left with some data or @@ -907,6 +899,7 @@ data_in_order (_tcp_Socket *s, const BYTE *data, unsigned len, unsigned diff) TRACE_FILE ("data_in_order (%u): Use %lu out-of-order bytes\n", __LINE__, (u_long)(s->missed_seq[1] - s->missed_seq[0])); s->rx_datalen += (s->missed_seq[1] - s->missed_seq[0]); + s->recv_win -= (s->missed_seq[1] - s->missed_seq[0]); s->recv_next = s->missed_seq[1]; s->missed_seq[0] = s->missed_seq[1] = 0; @@ -916,6 +909,11 @@ data_in_order (_tcp_Socket *s, const BYTE *data, unsigned len, unsigned diff) */ copy_in_order (s, data + ms_end, len - ms_end); } + + /* Send fast-ACK to notify peer that we caught up. + */ + s->dup_acks = 0; + len = -1; } TRACE_FILE ("data_in_order (%u): edges %lu/%lu, recv.next %lu\n", @@ -924,6 +922,8 @@ data_in_order (_tcp_Socket *s, const BYTE *data, unsigned len, unsigned diff) TRACE_FILE ("data_in_order (%u): new data now ends at %u\n", __LINE__, s->rx_datalen); + + return (len); } /* @@ -1115,9 +1115,9 @@ static int tcp_process_data (_tcp_Socket *s, const tcp_Header *tcp, if (ldiff >= 0) { - data_in_order (s, data, len, ldiff); + len = data_in_order (s, data, len, ldiff); s->unhappy = (s->tx_datalen > 0); - return (0); + return (len); } STAT (tcpstats.tcps_rcvduppack++); /* increment dup-ACK count */ diff --git a/src/wattcp.h b/src/wattcp.h index 4b0b6116..3abd88bb 100644 --- a/src/wattcp.h +++ b/src/wattcp.h @@ -299,7 +299,7 @@ struct ulong_long { * UDP/TCP socket local flags (locflags) bits. * Mostly used to support the BSD-socket API. */ -#define LF_WINUPDATE 0x00001 /**< need to send a window-update */ +#define LF_WINUPDATE 0x00001 /**< no longer used */ #define LF_NOPUSH 0x00002 /**< don't push on write */ #define LF_NOOPT 0x00004 /**< don't use tcp options */ #define LF_REUSEADDR 0x00008 /**< \todo Reuse address not supported */ @@ -607,6 +607,7 @@ typedef struct tcp_Socket { UINT window; /**< other guy's window */ UINT adv_win; /**< our last advertised window */ + long recv_win; /**< our current remaining window */ BYTE cwindow; /**< Congestion window */ BYTE wwindow; /**< Van Jacobson's algorithm */ @@ -618,7 +619,9 @@ typedef struct tcp_Socket { UINT rto; /**< retransmission timeout */ BYTE karn_count; /**< count of packets */ BYTE tos; /**< TOS from IP-header */ - WORD fill_5; + + BYTE dup_acks; /**< number of dup-ACKs we sent recently */ + BYTE fill_5; DWORD rtt_time; /**< Round Trip Time value */ DWORD rtt_lasttran; /**< RTT at last transmission */