static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
static struct tcp_hdr *
-tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, int optlen,
+tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, u16_t optlen,
u32_t seqno_be /* already in network byte order */)
{
struct tcp_hdr *tcphdr = p->payload;
if (useg != NULL &&
TCP_TCPLEN(useg) != 0 &&
!(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) &&
- !(flags & (TCP_SYN | TCP_FIN)) &&
+ (!(flags & (TCP_SYN | TCP_FIN)) || (flags == TCP_FIN)) &&
/* fit within max seg size */
(useg->len + queue->len <= pcb->mss) &&
/* only concatenate segments with the same options */
queuelen--;
pbuf_free(old_q);
}
- LWIP_ASSERT("zero-length pbuf", (queue->p != NULL) && (queue->p->len > 0));
- pbuf_cat(useg->p, queue->p);
- useg->len += queue->len;
- useg->next = queue->next;
+ if (flags & TCP_FIN) {
+ /* the new segment contains only FIN, no data -> put the FIN into the last segment */
+ LWIP_ASSERT("FIN enqueued together with data", queue->p == NULL && queue->len == 0);
+ TCPH_SET_FLAG(useg->tcphdr, TCP_FIN);
+ } else {
+ LWIP_ASSERT("zero-length pbuf", (queue->p != NULL) && (queue->p->len > 0));
+ pbuf_cat(useg->p, queue->p);
+ useg->len += queue->len;
+ useg->next = queue->next;
+ }
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len));
if (seg == queue) {
}
#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 = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+ return ERR_BUF;
+ }
+ 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);
+
+ tcphdr = tcp_output_set_header(pcb, p, optlen, htonl(pcb->snd_nxt));
+
+ /* 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
err_t
tcp_output(struct tcp_pcb *pcb)
{
- struct pbuf *p;
- struct tcp_hdr *tcphdr;
struct tcp_seg *seg, *useg;
u32_t wnd, snd_nxt;
#if TCP_CWND_DEBUG
s16_t i = 0;
#endif /* TCP_CWND_DEBUG */
- u8_t optlen = 0;
/* First, check if we are invoked by the TCP input processing
code. If so, we do not output anything. Instead, we rely on the
seg = pcb->unsent;
- /* useg should point to last segment on unacked queue */
- useg = pcb->unacked;
- if (useg != NULL) {
- for (; useg->next != NULL; useg = useg->next);
- }
-
/* 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 (pcb->flags & TF_ACK_NOW &&
(seg == NULL ||
ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
-#if LWIP_TCP_TIMESTAMPS
- if (pcb->flags & TF_TIMESTAMP)
- optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
-#endif
- p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM);
- if (p == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
- return ERR_BUF;
- }
- 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);
-
- tcphdr = tcp_output_set_header(pcb, p, optlen, htonl(pcb->snd_nxt));
-
- /* 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 tcp_send_empty_ack(pcb);
+ }
- return ERR_OK;
+ /* useg should point to last segment on unacked queue */
+ useg = pcb->unacked;
+ if (useg != NULL) {
+ for (; useg->next != NULL; useg = useg->next);
}
#if TCP_OUTPUT_DEBUG
/* Do the actual retransmission. */
snmp_inc_tcpretranssegs();
- tcp_output(pcb);
+ /* No need to call tcp_output: we are always called from tcp_input()
+ and thus tcp_output directly returns. */
}