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:
/** * 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 * 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 LWIP_3RD_PARTY_BUFS if ((seg = external_tcp_seg_alloc(pcb)) == NULL) { #else if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { #endif LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); tcp_tx_pbuf_free(pcb, p); return NULL; } seg->flags = optflags; seg->next = NULL; seg->p = p; seg->dataptr = p->payload; 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 */ seg->seqno = seqno; /* 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_tx_seg_free(pcb, 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 */ static struct pbuf * tcp_pbuf_prealloc(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 (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 + pcb->tcp_oversize_val)); } } p = tcp_tx_pbuf_alloc(pcb, 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; } /** 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 tcp_write_checks(struct tcp_pcb *pcb, u32_t len) { /* connection is in invalid state for data transmission? */ if ((get_tcp_state(pcb) != ESTABLISHED) && (get_tcp_state(pcb) != CLOSE_WAIT) && (get_tcp_state(pcb) != SYN_SENT) && (get_tcp_state(pcb) != 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=%"U32_F" > snd_buf=%"U32_F")\n", len, pcb->snd_buf)); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U32_F"\n", (u32_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 >= pcb->max_unsent_len) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U32_F" (max %"U32_F")\n", pcb->snd_queuelen, pcb->max_unsent_len)); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } if (pcb->snd_queuelen != 0) { } 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). * * 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(). * * @param pcb Protocol control block for the TCP connection to enqueue data for. * @param arg Pointer to the data to be enqueued for sending. * @param len Data length in bytes * @param apiflags combination of following flags : * - 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, u32_t len, u8_t apiflags) { struct pbuf *concat_p = NULL; struct tcp_seg *seg = NULL, *prev_seg = NULL, *queue = NULL; u32_t pos = 0; /* position in 'arg' data */ u32_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; /* don't allocate segments bigger than half the maximum window we ever received */ u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); mss_local = mss_local ? mss_local : pcb->mss; int byte_queued = pcb->snd_nxt - pcb->lastack; if ( len < pcb->mss) pcb->snd_sml_add = (pcb->unacked ? pcb->unacked->len : 0) + byte_queued; #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; /* ensure that segments can hold at least one data byte... */ mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1); } #endif /* LWIP_TCP_TIMESTAMPS */ optlen = LWIP_TCP_OPT_LENGTH( optflags ); /* * 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) { u16_t space; u16_t unsent_optlen; if (!pcb->last_unsent || pcb->last_unsent->next) { /* @todo: this could be sped up by keeping last_unsent in the pcb */ for (pcb->last_unsent = pcb->unsent; pcb->last_unsent->next != NULL; pcb->last_unsent = pcb->last_unsent->next); } /* Usable space at the end of the last unsent segment */ unsent_optlen = LWIP_TCP_OPT_LENGTH(pcb->last_unsent->flags); LWIP_ASSERT("mss_local is too small", mss_local >= pcb->last_unsent->len + unsent_optlen); space = mss_local - (pcb->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 == pcb->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 = pcb->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 */ /* * 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) && (pcb->last_unsent->len > 0)) { u16_t seglen = space < len - pos ? space : len - pos; seg = pcb->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(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 pcb->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 { LWIP_ASSERT("tcp_write : we are never here", 0); goto memerr; } pos += seglen; queuelen += pbuf_clen(concat_p); } } else { #if TCP_OVERSIZE pcb->last_unsent = NULL; 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; u32_t left = len - pos; u16_t max_len = mss_local - optlen; u16_t seglen = left > max_len ? max_len : left; if (apiflags & TCP_WRITE_FLAG_COPY) { /* If copy is set, memory should be allocated and data copied * into pbuf */ if ((p = tcp_pbuf_prealloc(seglen + optlen, mss_local, &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 { LWIP_ASSERT("tcp_write: we are never here",0); goto memerr; } 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 > pcb->max_unsent_len) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U32_F" (%"U32_F")\n", queuelen, pcb->max_unsent_len)); tcp_tx_pbuf_free(pcb, 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 */ /* Fix dataptr for the nocopy case */ if ((apiflags & TCP_WRITE_FLAG_COPY) == 0) { seg->dataptr = (u8_t*)arg + pos; } /* 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 = pcb->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, pcb->last_unsent); p->len += oversize_used; } } pcb->last_unsent->len += oversize_used; #if TCP_OVERSIZE_DBGCHECK pcb->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 pcb->last_unsent->p */ if (concat_p != NULL) { LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty", (pcb->last_unsent != NULL)); pbuf_cat(pcb->last_unsent->p, concat_p); pcb->last_unsent->len += concat_p->tot_len; #if TCP_CHECKSUM_ON_COPY if (concat_chksummed) { tcp_seg_add_chksum(concat_chksum, concat_chksummed, &pcb->last_unsent->chksum, &pcb->last_unsent->chksum_swapped); pcb->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 (pcb->last_unsent == NULL) { pcb->unsent = queue; } else { pcb->last_unsent->next = queue; } pcb->last_unsent = seg; /* * 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) { TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); } return ERR_OK; memerr: pcb->flags |= TF_NAGLEMEMERR; TCP_STATS_INC(tcp.memerr); if (concat_p != NULL) { tcp_tx_pbuf_free(pcb, concat_p); } if (queue != NULL) { tcp_tx_segs_free(pcb, 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; }