1195972f6Sopenharmony_ci/**
2195972f6Sopenharmony_ci * @file
3195972f6Sopenharmony_ci * Transmission Control Protocol, outgoing traffic
4195972f6Sopenharmony_ci *
5195972f6Sopenharmony_ci * The output functions of TCP.
6195972f6Sopenharmony_ci *
7195972f6Sopenharmony_ci * There are two distinct ways for TCP segments to get sent:
8195972f6Sopenharmony_ci * - queued data: these are segments transferring data or segments containing
9195972f6Sopenharmony_ci *   SYN or FIN (which both count as one sequence number). They are created as
10195972f6Sopenharmony_ci *   struct @ref pbuf together with a struct tcp_seg and enqueue to the
11195972f6Sopenharmony_ci *   unsent list of the pcb. They are sent by tcp_output:
12195972f6Sopenharmony_ci *   - @ref tcp_write : creates data segments
13195972f6Sopenharmony_ci *   - @ref tcp_split_unsent_seg : splits a data segment
14195972f6Sopenharmony_ci *   - @ref tcp_enqueue_flags : creates SYN-only or FIN-only segments
15195972f6Sopenharmony_ci *   - @ref tcp_output / tcp_output_segment : finalize the tcp header
16195972f6Sopenharmony_ci *      (e.g. sequence numbers, options, checksum) and output to IP
17195972f6Sopenharmony_ci *   - the various tcp_rexmit functions shuffle around segments between the
18195972f6Sopenharmony_ci *     unsent an unacked lists to retransmit them
19195972f6Sopenharmony_ci *   - tcp_create_segment and tcp_pbuf_prealloc allocate pbuf and
20195972f6Sopenharmony_ci *     segment for these functions
21195972f6Sopenharmony_ci * - direct send: these segments don't contain data but control the connection
22195972f6Sopenharmony_ci *   behaviour. They are created as pbuf only and sent directly without
23195972f6Sopenharmony_ci *   enqueueing them:
24195972f6Sopenharmony_ci *   - @ref tcp_send_empty_ack sends an ACK-only segment
25195972f6Sopenharmony_ci *   - @ref tcp_rst sends a RST segment
26195972f6Sopenharmony_ci *   - @ref tcp_keepalive sends a keepalive segment
27195972f6Sopenharmony_ci *   - @ref tcp_zero_window_probe sends a window probe segment
28195972f6Sopenharmony_ci *   - tcp_output_alloc_header allocates a header-only pbuf for these functions
29195972f6Sopenharmony_ci */
30195972f6Sopenharmony_ci
31195972f6Sopenharmony_ci/*
32195972f6Sopenharmony_ci * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
33195972f6Sopenharmony_ci * All rights reserved.
34195972f6Sopenharmony_ci *
35195972f6Sopenharmony_ci * Redistribution and use in source and binary forms, with or without modification,
36195972f6Sopenharmony_ci * are permitted provided that the following conditions are met:
37195972f6Sopenharmony_ci *
38195972f6Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright notice,
39195972f6Sopenharmony_ci *    this list of conditions and the following disclaimer.
40195972f6Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright notice,
41195972f6Sopenharmony_ci *    this list of conditions and the following disclaimer in the documentation
42195972f6Sopenharmony_ci *    and/or other materials provided with the distribution.
43195972f6Sopenharmony_ci * 3. The name of the author may not be used to endorse or promote products
44195972f6Sopenharmony_ci *    derived from this software without specific prior written permission.
45195972f6Sopenharmony_ci *
46195972f6Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
47195972f6Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
48195972f6Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
49195972f6Sopenharmony_ci * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
50195972f6Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
51195972f6Sopenharmony_ci * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
52195972f6Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
53195972f6Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
54195972f6Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
55195972f6Sopenharmony_ci * OF SUCH DAMAGE.
56195972f6Sopenharmony_ci *
57195972f6Sopenharmony_ci * This file is part of the lwIP TCP/IP stack.
58195972f6Sopenharmony_ci *
59195972f6Sopenharmony_ci * Author: Adam Dunkels <adam@sics.se>
60195972f6Sopenharmony_ci *
61195972f6Sopenharmony_ci */
62195972f6Sopenharmony_ci
63195972f6Sopenharmony_ci#include "lwip/opt.h"
64195972f6Sopenharmony_ci
65195972f6Sopenharmony_ci#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
66195972f6Sopenharmony_ci
67195972f6Sopenharmony_ci#include "lwip/priv/tcp_priv.h"
68195972f6Sopenharmony_ci#include "lwip/def.h"
69195972f6Sopenharmony_ci#include "lwip/mem.h"
70195972f6Sopenharmony_ci#include "lwip/memp.h"
71195972f6Sopenharmony_ci#include "lwip/ip_addr.h"
72195972f6Sopenharmony_ci#include "lwip/netif.h"
73195972f6Sopenharmony_ci#include "lwip/inet_chksum.h"
74195972f6Sopenharmony_ci#include "lwip/stats.h"
75195972f6Sopenharmony_ci#include "lwip/ip6.h"
76195972f6Sopenharmony_ci#include "lwip/ip6_addr.h"
77195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
78195972f6Sopenharmony_ci#include "lwip/sys.h"
79195972f6Sopenharmony_ci#endif
80195972f6Sopenharmony_ci
81195972f6Sopenharmony_ci#include <string.h>
82195972f6Sopenharmony_ci
83195972f6Sopenharmony_ci#ifdef LWIP_HOOK_FILENAME
84195972f6Sopenharmony_ci#include LWIP_HOOK_FILENAME
85195972f6Sopenharmony_ci#endif
86195972f6Sopenharmony_ci
87195972f6Sopenharmony_ci/* Allow to add custom TCP header options by defining this hook */
88195972f6Sopenharmony_ci#ifdef LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH
89195972f6Sopenharmony_ci#define LWIP_TCP_OPT_LENGTH_SEGMENT(flags, pcb) LWIP_HOOK_TCP_OUT_TCPOPT_LENGTH(pcb, LWIP_TCP_OPT_LENGTH(flags))
90195972f6Sopenharmony_ci#else
91195972f6Sopenharmony_ci#define LWIP_TCP_OPT_LENGTH_SEGMENT(flags, pcb) LWIP_TCP_OPT_LENGTH(flags)
92195972f6Sopenharmony_ci#endif
93195972f6Sopenharmony_ci
94195972f6Sopenharmony_ci/* Define some copy-macros for checksum-on-copy so that the code looks
95195972f6Sopenharmony_ci   nicer by preventing too many ifdef's. */
96195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
97195972f6Sopenharmony_ci#define TCP_DATA_COPY(dst, src, len, seg) do { \
98195972f6Sopenharmony_ci  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
99195972f6Sopenharmony_ci                     len, &seg->chksum, &seg->chksum_swapped); \
100195972f6Sopenharmony_ci  seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
101195972f6Sopenharmony_ci#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped)  \
102195972f6Sopenharmony_ci  tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
103195972f6Sopenharmony_ci#else /* TCP_CHECKSUM_ON_COPY*/
104195972f6Sopenharmony_ci#define TCP_DATA_COPY(dst, src, len, seg)                     MEMCPY(dst, src, len)
105195972f6Sopenharmony_ci#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
106195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY*/
107195972f6Sopenharmony_ci
108195972f6Sopenharmony_ci/** Define this to 1 for an extra check that the output checksum is valid
109195972f6Sopenharmony_ci * (usefule when the checksum is generated by the application, not the stack) */
110195972f6Sopenharmony_ci#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
111195972f6Sopenharmony_ci#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK   0
112195972f6Sopenharmony_ci#endif
113195972f6Sopenharmony_ci/* Allow to override the failure of sanity check from warning to e.g. hard failure */
114195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
115195972f6Sopenharmony_ci#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL
116195972f6Sopenharmony_ci#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg)
117195972f6Sopenharmony_ci#endif
118195972f6Sopenharmony_ci#endif
119195972f6Sopenharmony_ci
120195972f6Sopenharmony_ci#if TCP_OVERSIZE
121195972f6Sopenharmony_ci/** The size of segment pbufs created when TCP_OVERSIZE is enabled */
122195972f6Sopenharmony_ci#ifndef TCP_OVERSIZE_CALC_LENGTH
123195972f6Sopenharmony_ci#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE)
124195972f6Sopenharmony_ci#endif
125195972f6Sopenharmony_ci#endif
126195972f6Sopenharmony_ci
127195972f6Sopenharmony_ci/* Forward declarations.*/
128195972f6Sopenharmony_cistatic err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
129195972f6Sopenharmony_ci
130195972f6Sopenharmony_ci/* tcp_route: common code that returns a fixed bound netif or calls ip_route */
131195972f6Sopenharmony_cistatic struct netif *
132195972f6Sopenharmony_citcp_route(const struct tcp_pcb *pcb, const ip_addr_t *src, const ip_addr_t *dst)
133195972f6Sopenharmony_ci{
134195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(src); /* in case IPv4-only and source-based routing is disabled */
135195972f6Sopenharmony_ci#ifdef LOSCFG_NET_CONTAINER
136195972f6Sopenharmony_ci  struct net_group *group = get_net_group_from_tcp_pcb(pcb);
137195972f6Sopenharmony_ci  LWIP_ERROR("tcp_route: invalid net group", group != NULL, return NULL);
138195972f6Sopenharmony_ci#endif
139195972f6Sopenharmony_ci
140195972f6Sopenharmony_ci  if ((pcb != NULL) && (pcb->netif_idx != NETIF_NO_INDEX)) {
141195972f6Sopenharmony_ci#ifdef LOSCFG_NET_CONTAINER
142195972f6Sopenharmony_ci    return netif_get_by_index(pcb->netif_idx, group);
143195972f6Sopenharmony_ci#else
144195972f6Sopenharmony_ci    return netif_get_by_index(pcb->netif_idx);
145195972f6Sopenharmony_ci#endif
146195972f6Sopenharmony_ci  } else {
147195972f6Sopenharmony_ci#ifdef LOSCFG_NET_CONTAINER
148195972f6Sopenharmony_ci    return ip_route(src, dst, group);
149195972f6Sopenharmony_ci#else
150195972f6Sopenharmony_ci    return ip_route(src, dst);
151195972f6Sopenharmony_ci#endif
152195972f6Sopenharmony_ci  }
153195972f6Sopenharmony_ci}
154195972f6Sopenharmony_ci
155195972f6Sopenharmony_ci/**
156195972f6Sopenharmony_ci * Create a TCP segment with prefilled header.
157195972f6Sopenharmony_ci *
158195972f6Sopenharmony_ci * Called by @ref tcp_write, @ref tcp_enqueue_flags and @ref tcp_split_unsent_seg
159195972f6Sopenharmony_ci *
160195972f6Sopenharmony_ci * @param pcb Protocol control block for the TCP connection.
161195972f6Sopenharmony_ci * @param p pbuf that is used to hold the TCP header.
162195972f6Sopenharmony_ci * @param hdrflags TCP flags for header.
163195972f6Sopenharmony_ci * @param seqno TCP sequence number of this packet
164195972f6Sopenharmony_ci * @param optflags options to include in TCP header
165195972f6Sopenharmony_ci * @return a new tcp_seg pointing to p, or NULL.
166195972f6Sopenharmony_ci * The TCP header is filled in except ackno and wnd.
167195972f6Sopenharmony_ci * p is freed on failure.
168195972f6Sopenharmony_ci */
169195972f6Sopenharmony_cistatic struct tcp_seg *
170195972f6Sopenharmony_citcp_create_segment(const struct tcp_pcb *pcb, struct pbuf *p, u8_t hdrflags, u32_t seqno, u8_t optflags)
171195972f6Sopenharmony_ci{
172195972f6Sopenharmony_ci  struct tcp_seg *seg;
173195972f6Sopenharmony_ci  u8_t optlen;
174195972f6Sopenharmony_ci
175195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_create_segment: invalid pcb", pcb != NULL);
176195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_create_segment: invalid pbuf", p != NULL);
177195972f6Sopenharmony_ci
178195972f6Sopenharmony_ci  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
179195972f6Sopenharmony_ci
180195972f6Sopenharmony_ci  if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
181195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no memory.\n"));
182195972f6Sopenharmony_ci    pbuf_free(p);
183195972f6Sopenharmony_ci    return NULL;
184195972f6Sopenharmony_ci  }
185195972f6Sopenharmony_ci  seg->flags = optflags;
186195972f6Sopenharmony_ci  seg->next = NULL;
187195972f6Sopenharmony_ci  seg->p = p;
188195972f6Sopenharmony_ci  LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen);
189195972f6Sopenharmony_ci  seg->len = p->tot_len - optlen;
190195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
191195972f6Sopenharmony_ci  seg->oversize_left = 0;
192195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
193195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
194195972f6Sopenharmony_ci  seg->chksum = 0;
195195972f6Sopenharmony_ci  seg->chksum_swapped = 0;
196195972f6Sopenharmony_ci  /* check optflags */
197195972f6Sopenharmony_ci  LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
198195972f6Sopenharmony_ci              (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
199195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
200195972f6Sopenharmony_ci
201195972f6Sopenharmony_ci  /* build TCP header */
202195972f6Sopenharmony_ci  if (pbuf_add_header(p, TCP_HLEN)) {
203195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
204195972f6Sopenharmony_ci    TCP_STATS_INC(tcp.err);
205195972f6Sopenharmony_ci    tcp_seg_free(seg);
206195972f6Sopenharmony_ci    return NULL;
207195972f6Sopenharmony_ci  }
208195972f6Sopenharmony_ci  seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
209195972f6Sopenharmony_ci  seg->tcphdr->src = lwip_htons(pcb->local_port);
210195972f6Sopenharmony_ci  seg->tcphdr->dest = lwip_htons(pcb->remote_port);
211195972f6Sopenharmony_ci  seg->tcphdr->seqno = lwip_htonl(seqno);
212195972f6Sopenharmony_ci  /* ackno is set in tcp_output */
213195972f6Sopenharmony_ci  TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), hdrflags);
214195972f6Sopenharmony_ci  /* wnd and chksum are set in tcp_output */
215195972f6Sopenharmony_ci  seg->tcphdr->urgp = 0;
216195972f6Sopenharmony_ci  return seg;
217195972f6Sopenharmony_ci}
218195972f6Sopenharmony_ci
219195972f6Sopenharmony_ci/**
220195972f6Sopenharmony_ci * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
221195972f6Sopenharmony_ci *
222195972f6Sopenharmony_ci * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
223195972f6Sopenharmony_ci * there may be extra bytes available at the end.
224195972f6Sopenharmony_ci *
225195972f6Sopenharmony_ci * Called by @ref tcp_write
226195972f6Sopenharmony_ci *
227195972f6Sopenharmony_ci * @param layer flag to define header size.
228195972f6Sopenharmony_ci * @param length size of the pbuf's payload.
229195972f6Sopenharmony_ci * @param max_length maximum usable size of payload+oversize.
230195972f6Sopenharmony_ci * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
231195972f6Sopenharmony_ci * @param pcb The TCP connection that will enqueue the pbuf.
232195972f6Sopenharmony_ci * @param apiflags API flags given to tcp_write.
233195972f6Sopenharmony_ci * @param first_seg true when this pbuf will be used in the first enqueued segment.
234195972f6Sopenharmony_ci */
235195972f6Sopenharmony_ci#if TCP_OVERSIZE
236195972f6Sopenharmony_cistatic struct pbuf *
237195972f6Sopenharmony_citcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
238195972f6Sopenharmony_ci                  u16_t *oversize, const struct tcp_pcb *pcb, u8_t apiflags,
239195972f6Sopenharmony_ci                  u8_t first_seg)
240195972f6Sopenharmony_ci{
241195972f6Sopenharmony_ci  struct pbuf *p;
242195972f6Sopenharmony_ci  u16_t alloc = length;
243195972f6Sopenharmony_ci
244195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_pbuf_prealloc: invalid oversize", oversize != NULL);
245195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_pbuf_prealloc: invalid pcb", pcb != NULL);
246195972f6Sopenharmony_ci
247195972f6Sopenharmony_ci#if LWIP_NETIF_TX_SINGLE_PBUF
248195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(max_length);
249195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(pcb);
250195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(apiflags);
251195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(first_seg);
252195972f6Sopenharmony_ci  alloc = max_length;
253195972f6Sopenharmony_ci#else /* LWIP_NETIF_TX_SINGLE_PBUF */
254195972f6Sopenharmony_ci  if (length < max_length) {
255195972f6Sopenharmony_ci    /* Should we allocate an oversized pbuf, or just the minimum
256195972f6Sopenharmony_ci     * length required? If tcp_write is going to be called again
257195972f6Sopenharmony_ci     * before this segment is transmitted, we want the oversized
258195972f6Sopenharmony_ci     * buffer. If the segment will be transmitted immediately, we can
259195972f6Sopenharmony_ci     * save memory by allocating only length. We use a simple
260195972f6Sopenharmony_ci     * heuristic based on the following information:
261195972f6Sopenharmony_ci     *
262195972f6Sopenharmony_ci     * Did the user set TCP_WRITE_FLAG_MORE?
263195972f6Sopenharmony_ci     *
264195972f6Sopenharmony_ci     * Will the Nagle algorithm defer transmission of this segment?
265195972f6Sopenharmony_ci     */
266195972f6Sopenharmony_ci    if ((apiflags & TCP_WRITE_FLAG_MORE) ||
267195972f6Sopenharmony_ci        (!(pcb->flags & TF_NODELAY) &&
268195972f6Sopenharmony_ci         (!first_seg ||
269195972f6Sopenharmony_ci          pcb->unsent != NULL ||
270195972f6Sopenharmony_ci          pcb->unacked != NULL))) {
271195972f6Sopenharmony_ci      alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length)));
272195972f6Sopenharmony_ci    }
273195972f6Sopenharmony_ci  }
274195972f6Sopenharmony_ci#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
275195972f6Sopenharmony_ci  p = pbuf_alloc(layer, alloc, PBUF_RAM);
276195972f6Sopenharmony_ci  if (p == NULL) {
277195972f6Sopenharmony_ci    return NULL;
278195972f6Sopenharmony_ci  }
279195972f6Sopenharmony_ci  LWIP_ASSERT("need unchained pbuf", p->next == NULL);
280195972f6Sopenharmony_ci  *oversize = p->len - length;
281195972f6Sopenharmony_ci  /* trim p->len to the currently used size */
282195972f6Sopenharmony_ci  p->len = p->tot_len = length;
283195972f6Sopenharmony_ci  return p;
284195972f6Sopenharmony_ci}
285195972f6Sopenharmony_ci#else /* TCP_OVERSIZE */
286195972f6Sopenharmony_ci#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
287195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
288195972f6Sopenharmony_ci
289195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
290195972f6Sopenharmony_ci/** Add a checksum of newly added data to the segment.
291195972f6Sopenharmony_ci *
292195972f6Sopenharmony_ci * Called by tcp_write and tcp_split_unsent_seg.
293195972f6Sopenharmony_ci */
294195972f6Sopenharmony_cistatic void
295195972f6Sopenharmony_citcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
296195972f6Sopenharmony_ci                   u8_t *seg_chksum_swapped)
297195972f6Sopenharmony_ci{
298195972f6Sopenharmony_ci  u32_t helper;
299195972f6Sopenharmony_ci  /* add chksum to old chksum and fold to u16_t */
300195972f6Sopenharmony_ci  helper = chksum + *seg_chksum;
301195972f6Sopenharmony_ci  chksum = FOLD_U32T(helper);
302195972f6Sopenharmony_ci  if ((len & 1) != 0) {
303195972f6Sopenharmony_ci    *seg_chksum_swapped = 1 - *seg_chksum_swapped;
304195972f6Sopenharmony_ci    chksum = SWAP_BYTES_IN_WORD(chksum);
305195972f6Sopenharmony_ci  }
306195972f6Sopenharmony_ci  *seg_chksum = chksum;
307195972f6Sopenharmony_ci}
308195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
309195972f6Sopenharmony_ci
310195972f6Sopenharmony_ci/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
311195972f6Sopenharmony_ci *
312195972f6Sopenharmony_ci * @param pcb the tcp pcb to check for
313195972f6Sopenharmony_ci * @param len length of data to send (checked agains snd_buf)
314195972f6Sopenharmony_ci * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
315195972f6Sopenharmony_ci */
316195972f6Sopenharmony_cistatic err_t
317195972f6Sopenharmony_citcp_write_checks(struct tcp_pcb *pcb, u16_t len)
318195972f6Sopenharmony_ci{
319195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_write_checks: invalid pcb", pcb != NULL);
320195972f6Sopenharmony_ci
321195972f6Sopenharmony_ci  /* connection is in invalid state for data transmission? */
322195972f6Sopenharmony_ci  if ((pcb->state != ESTABLISHED) &&
323195972f6Sopenharmony_ci      (pcb->state != CLOSE_WAIT) &&
324195972f6Sopenharmony_ci      (pcb->state != SYN_SENT) &&
325195972f6Sopenharmony_ci      (pcb->state != SYN_RCVD)) {
326195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
327195972f6Sopenharmony_ci    return ERR_CONN;
328195972f6Sopenharmony_ci  } else if (len == 0) {
329195972f6Sopenharmony_ci    return ERR_OK;
330195972f6Sopenharmony_ci  }
331195972f6Sopenharmony_ci
332195972f6Sopenharmony_ci  /* fail on too much data */
333195972f6Sopenharmony_ci  if (len > pcb->snd_buf) {
334195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"TCPWNDSIZE_F")\n",
335195972f6Sopenharmony_ci                len, pcb->snd_buf));
336195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_NAGLEMEMERR);
337195972f6Sopenharmony_ci    return ERR_MEM;
338195972f6Sopenharmony_ci  }
339195972f6Sopenharmony_ci
340195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen));
341195972f6Sopenharmony_ci
342195972f6Sopenharmony_ci  /* If total number of pbufs on the unsent/unacked queues exceeds the
343195972f6Sopenharmony_ci   * configured maximum, return an error */
344195972f6Sopenharmony_ci  /* check for configured max queuelen and possible overflow */
345195972f6Sopenharmony_ci  if (pcb->snd_queuelen >= LWIP_MIN(TCP_SND_QUEUELEN, (TCP_SNDQUEUELEN_OVERFLOW + 1))) {
346195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
347195972f6Sopenharmony_ci                pcb->snd_queuelen, (u16_t)TCP_SND_QUEUELEN));
348195972f6Sopenharmony_ci    TCP_STATS_INC(tcp.memerr);
349195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_NAGLEMEMERR);
350195972f6Sopenharmony_ci    return ERR_MEM;
351195972f6Sopenharmony_ci  }
352195972f6Sopenharmony_ci  if (pcb->snd_queuelen != 0) {
353195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
354195972f6Sopenharmony_ci                pcb->unacked != NULL || pcb->unsent != NULL);
355195972f6Sopenharmony_ci  } else {
356195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
357195972f6Sopenharmony_ci                pcb->unacked == NULL && pcb->unsent == NULL);
358195972f6Sopenharmony_ci  }
359195972f6Sopenharmony_ci  return ERR_OK;
360195972f6Sopenharmony_ci}
361195972f6Sopenharmony_ci
362195972f6Sopenharmony_ci/**
363195972f6Sopenharmony_ci * @ingroup tcp_raw
364195972f6Sopenharmony_ci * Write data for sending (but does not send it immediately).
365195972f6Sopenharmony_ci *
366195972f6Sopenharmony_ci * It waits in the expectation of more data being sent soon (as
367195972f6Sopenharmony_ci * it can send them more efficiently by combining them together).
368195972f6Sopenharmony_ci * To prompt the system to send data now, call tcp_output() after
369195972f6Sopenharmony_ci * calling tcp_write().
370195972f6Sopenharmony_ci *
371195972f6Sopenharmony_ci * This function enqueues the data pointed to by the argument dataptr. The length of
372195972f6Sopenharmony_ci * the data is passed as the len parameter. The apiflags can be one or more of:
373195972f6Sopenharmony_ci * - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
374195972f6Sopenharmony_ci *   for the data to be copied into. If this flag is not given, no new memory
375195972f6Sopenharmony_ci *   should be allocated and the data should only be referenced by pointer. This
376195972f6Sopenharmony_ci *   also means that the memory behind dataptr must not change until the data is
377195972f6Sopenharmony_ci *   ACKed by the remote host
378195972f6Sopenharmony_ci * - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted,
379195972f6Sopenharmony_ci *   the PSH flag is set in the last segment created by this call to tcp_write.
380195972f6Sopenharmony_ci *   If this flag is given, the PSH flag is not set.
381195972f6Sopenharmony_ci *
382195972f6Sopenharmony_ci * The tcp_write() function will fail and return ERR_MEM if the length
383195972f6Sopenharmony_ci * of the data exceeds the current send buffer size or if the length of
384195972f6Sopenharmony_ci * the queue of outgoing segment is larger than the upper limit defined
385195972f6Sopenharmony_ci * in lwipopts.h. The number of bytes available in the output queue can
386195972f6Sopenharmony_ci * be retrieved with the tcp_sndbuf() function.
387195972f6Sopenharmony_ci *
388195972f6Sopenharmony_ci * The proper way to use this function is to call the function with at
389195972f6Sopenharmony_ci * most tcp_sndbuf() bytes of data. If the function returns ERR_MEM,
390195972f6Sopenharmony_ci * the application should wait until some of the currently enqueued
391195972f6Sopenharmony_ci * data has been successfully received by the other host and try again.
392195972f6Sopenharmony_ci *
393195972f6Sopenharmony_ci * @param pcb Protocol control block for the TCP connection to enqueue data for.
394195972f6Sopenharmony_ci * @param arg Pointer to the data to be enqueued for sending.
395195972f6Sopenharmony_ci * @param len Data length in bytes
396195972f6Sopenharmony_ci * @param apiflags combination of following flags :
397195972f6Sopenharmony_ci * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
398195972f6Sopenharmony_ci * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will not be set on last segment sent,
399195972f6Sopenharmony_ci * @return ERR_OK if enqueued, another err_t on error
400195972f6Sopenharmony_ci */
401195972f6Sopenharmony_cierr_t
402195972f6Sopenharmony_citcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
403195972f6Sopenharmony_ci{
404195972f6Sopenharmony_ci  struct pbuf *concat_p = NULL;
405195972f6Sopenharmony_ci  struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
406195972f6Sopenharmony_ci  u16_t pos = 0; /* position in 'arg' data */
407195972f6Sopenharmony_ci  u16_t queuelen;
408195972f6Sopenharmony_ci  u8_t optlen;
409195972f6Sopenharmony_ci  u8_t optflags = 0;
410195972f6Sopenharmony_ci#if TCP_OVERSIZE
411195972f6Sopenharmony_ci  u16_t oversize = 0;
412195972f6Sopenharmony_ci  u16_t oversize_used = 0;
413195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
414195972f6Sopenharmony_ci  u16_t oversize_add = 0;
415195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK*/
416195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
417195972f6Sopenharmony_ci  u16_t extendlen = 0;
418195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
419195972f6Sopenharmony_ci  u16_t concat_chksum = 0;
420195972f6Sopenharmony_ci  u8_t concat_chksum_swapped = 0;
421195972f6Sopenharmony_ci  u16_t concat_chksummed = 0;
422195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
423195972f6Sopenharmony_ci  err_t err;
424195972f6Sopenharmony_ci  u16_t mss_local;
425195972f6Sopenharmony_ci
426195972f6Sopenharmony_ci  LWIP_ERROR("tcp_write: invalid pcb", pcb != NULL, return ERR_ARG);
427195972f6Sopenharmony_ci
428195972f6Sopenharmony_ci  /* don't allocate segments bigger than half the maximum window we ever received */
429195972f6Sopenharmony_ci  mss_local = LWIP_MIN(pcb->mss, TCPWND_MIN16(pcb->snd_wnd_max / 2));
430195972f6Sopenharmony_ci  mss_local = mss_local ? mss_local : pcb->mss;
431195972f6Sopenharmony_ci
432195972f6Sopenharmony_ci  LWIP_ASSERT_CORE_LOCKED();
433195972f6Sopenharmony_ci
434195972f6Sopenharmony_ci#if LWIP_NETIF_TX_SINGLE_PBUF
435195972f6Sopenharmony_ci  /* Always copy to try to create single pbufs for TX */
436195972f6Sopenharmony_ci  apiflags |= TCP_WRITE_FLAG_COPY;
437195972f6Sopenharmony_ci#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
438195972f6Sopenharmony_ci
439195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
440195972f6Sopenharmony_ci                                 (void *)pcb, arg, len, (u16_t)apiflags));
441195972f6Sopenharmony_ci  LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
442195972f6Sopenharmony_ci             arg != NULL, return ERR_ARG;);
443195972f6Sopenharmony_ci
444195972f6Sopenharmony_ci  err = tcp_write_checks(pcb, len);
445195972f6Sopenharmony_ci  if (err != ERR_OK) {
446195972f6Sopenharmony_ci    return err;
447195972f6Sopenharmony_ci  }
448195972f6Sopenharmony_ci  queuelen = pcb->snd_queuelen;
449195972f6Sopenharmony_ci
450195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
451195972f6Sopenharmony_ci  if ((pcb->flags & TF_TIMESTAMP)) {
452195972f6Sopenharmony_ci    /* Make sure the timestamp option is only included in data segments if we
453195972f6Sopenharmony_ci       agreed about it with the remote host. */
454195972f6Sopenharmony_ci    optflags = TF_SEG_OPTS_TS;
455195972f6Sopenharmony_ci    optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(TF_SEG_OPTS_TS, pcb);
456195972f6Sopenharmony_ci    /* ensure that segments can hold at least one data byte... */
457195972f6Sopenharmony_ci    mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1);
458195972f6Sopenharmony_ci  } else
459195972f6Sopenharmony_ci#endif /* LWIP_TCP_TIMESTAMPS */
460195972f6Sopenharmony_ci  {
461195972f6Sopenharmony_ci    optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
462195972f6Sopenharmony_ci  }
463195972f6Sopenharmony_ci
464195972f6Sopenharmony_ci
465195972f6Sopenharmony_ci  /*
466195972f6Sopenharmony_ci   * TCP segmentation is done in three phases with increasing complexity:
467195972f6Sopenharmony_ci   *
468195972f6Sopenharmony_ci   * 1. Copy data directly into an oversized pbuf.
469195972f6Sopenharmony_ci   * 2. Chain a new pbuf to the end of pcb->unsent.
470195972f6Sopenharmony_ci   * 3. Create new segments.
471195972f6Sopenharmony_ci   *
472195972f6Sopenharmony_ci   * We may run out of memory at any point. In that case we must
473195972f6Sopenharmony_ci   * return ERR_MEM and not change anything in pcb. Therefore, all
474195972f6Sopenharmony_ci   * changes are recorded in local variables and committed at the end
475195972f6Sopenharmony_ci   * of the function. Some pcb fields are maintained in local copies:
476195972f6Sopenharmony_ci   *
477195972f6Sopenharmony_ci   * queuelen = pcb->snd_queuelen
478195972f6Sopenharmony_ci   * oversize = pcb->unsent_oversize
479195972f6Sopenharmony_ci   *
480195972f6Sopenharmony_ci   * These variables are set consistently by the phases:
481195972f6Sopenharmony_ci   *
482195972f6Sopenharmony_ci   * seg points to the last segment tampered with.
483195972f6Sopenharmony_ci   *
484195972f6Sopenharmony_ci   * pos records progress as data is segmented.
485195972f6Sopenharmony_ci   */
486195972f6Sopenharmony_ci
487195972f6Sopenharmony_ci  /* Find the tail of the unsent queue. */
488195972f6Sopenharmony_ci  if (pcb->unsent != NULL) {
489195972f6Sopenharmony_ci    u16_t space;
490195972f6Sopenharmony_ci    u16_t unsent_optlen;
491195972f6Sopenharmony_ci
492195972f6Sopenharmony_ci    /* @todo: this could be sped up by keeping last_unsent in the pcb */
493195972f6Sopenharmony_ci    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
494195972f6Sopenharmony_ci         last_unsent = last_unsent->next);
495195972f6Sopenharmony_ci
496195972f6Sopenharmony_ci    /* Usable space at the end of the last unsent segment */
497195972f6Sopenharmony_ci    unsent_optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(last_unsent->flags, pcb);
498195972f6Sopenharmony_ci    LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen);
499195972f6Sopenharmony_ci    space = mss_local - (last_unsent->len + unsent_optlen);
500195972f6Sopenharmony_ci
501195972f6Sopenharmony_ci    /*
502195972f6Sopenharmony_ci     * Phase 1: Copy data directly into an oversized pbuf.
503195972f6Sopenharmony_ci     *
504195972f6Sopenharmony_ci     * The number of bytes copied is recorded in the oversize_used
505195972f6Sopenharmony_ci     * variable. The actual copying is done at the bottom of the
506195972f6Sopenharmony_ci     * function.
507195972f6Sopenharmony_ci     */
508195972f6Sopenharmony_ci#if TCP_OVERSIZE
509195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
510195972f6Sopenharmony_ci    /* check that pcb->unsent_oversize matches last_unsent->oversize_left */
511195972f6Sopenharmony_ci    LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
512195972f6Sopenharmony_ci                pcb->unsent_oversize == last_unsent->oversize_left);
513195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
514195972f6Sopenharmony_ci    oversize = pcb->unsent_oversize;
515195972f6Sopenharmony_ci    if (oversize > 0) {
516195972f6Sopenharmony_ci      LWIP_ASSERT("inconsistent oversize vs. space", oversize <= space);
517195972f6Sopenharmony_ci      seg = last_unsent;
518195972f6Sopenharmony_ci      oversize_used = LWIP_MIN(space, LWIP_MIN(oversize, len));
519195972f6Sopenharmony_ci      pos += oversize_used;
520195972f6Sopenharmony_ci      oversize -= oversize_used;
521195972f6Sopenharmony_ci      space -= oversize_used;
522195972f6Sopenharmony_ci    }
523195972f6Sopenharmony_ci    /* now we are either finished or oversize is zero */
524195972f6Sopenharmony_ci    LWIP_ASSERT("inconsistent oversize vs. len", (oversize == 0) || (pos == len));
525195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
526195972f6Sopenharmony_ci
527195972f6Sopenharmony_ci#if !LWIP_NETIF_TX_SINGLE_PBUF
528195972f6Sopenharmony_ci    /*
529195972f6Sopenharmony_ci     * Phase 2: Chain a new pbuf to the end of pcb->unsent.
530195972f6Sopenharmony_ci     *
531195972f6Sopenharmony_ci     * As an exception when NOT copying the data, if the given data buffer
532195972f6Sopenharmony_ci     * directly follows the last unsent data buffer in memory, extend the last
533195972f6Sopenharmony_ci     * ROM pbuf reference to the buffer, thus saving a ROM pbuf allocation.
534195972f6Sopenharmony_ci     *
535195972f6Sopenharmony_ci     * We don't extend segments containing SYN/FIN flags or options
536195972f6Sopenharmony_ci     * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
537195972f6Sopenharmony_ci     * the end.
538195972f6Sopenharmony_ci     *
539195972f6Sopenharmony_ci     * This phase is skipped for LWIP_NETIF_TX_SINGLE_PBUF as we could only execute
540195972f6Sopenharmony_ci     * it after rexmit puts a segment from unacked to unsent and at this point,
541195972f6Sopenharmony_ci     * oversize info is lost.
542195972f6Sopenharmony_ci     */
543195972f6Sopenharmony_ci    if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
544195972f6Sopenharmony_ci      u16_t seglen = LWIP_MIN(space, len - pos);
545195972f6Sopenharmony_ci      seg = last_unsent;
546195972f6Sopenharmony_ci
547195972f6Sopenharmony_ci      /* Create a pbuf with a copy or reference to seglen bytes. We
548195972f6Sopenharmony_ci       * can use PBUF_RAW here since the data appears in the middle of
549195972f6Sopenharmony_ci       * a segment. A header will never be prepended. */
550195972f6Sopenharmony_ci      if (apiflags & TCP_WRITE_FLAG_COPY) {
551195972f6Sopenharmony_ci        /* Data is copied */
552195972f6Sopenharmony_ci        if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
553195972f6Sopenharmony_ci          LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
554195972f6Sopenharmony_ci                      ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
555195972f6Sopenharmony_ci                       seglen));
556195972f6Sopenharmony_ci          goto memerr;
557195972f6Sopenharmony_ci        }
558195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
559195972f6Sopenharmony_ci        oversize_add = oversize;
560195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
561195972f6Sopenharmony_ci        TCP_DATA_COPY2(concat_p->payload, (const u8_t *)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
562195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
563195972f6Sopenharmony_ci        concat_chksummed += seglen;
564195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
565195972f6Sopenharmony_ci        queuelen += pbuf_clen(concat_p);
566195972f6Sopenharmony_ci      } else {
567195972f6Sopenharmony_ci        /* Data is not copied */
568195972f6Sopenharmony_ci        /* If the last unsent pbuf is of type PBUF_ROM, try to extend it. */
569195972f6Sopenharmony_ci        struct pbuf *p;
570195972f6Sopenharmony_ci        for (p = last_unsent->p; p->next != NULL; p = p->next);
571195972f6Sopenharmony_ci        if (((p->type_internal & (PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_DATA_VOLATILE)) == 0) &&
572195972f6Sopenharmony_ci            (const u8_t *)p->payload + p->len == (const u8_t *)arg) {
573195972f6Sopenharmony_ci          LWIP_ASSERT("tcp_write: ROM pbufs cannot be oversized", pos == 0);
574195972f6Sopenharmony_ci          extendlen = seglen;
575195972f6Sopenharmony_ci        } else {
576195972f6Sopenharmony_ci          if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
577195972f6Sopenharmony_ci            LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
578195972f6Sopenharmony_ci                        ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
579195972f6Sopenharmony_ci            goto memerr;
580195972f6Sopenharmony_ci          }
581195972f6Sopenharmony_ci          /* reference the non-volatile payload data */
582195972f6Sopenharmony_ci          ((struct pbuf_rom *)concat_p)->payload = (const u8_t *)arg + pos;
583195972f6Sopenharmony_ci          queuelen += pbuf_clen(concat_p);
584195972f6Sopenharmony_ci        }
585195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
586195972f6Sopenharmony_ci        /* calculate the checksum of nocopy-data */
587195972f6Sopenharmony_ci        tcp_seg_add_chksum(~inet_chksum((const u8_t *)arg + pos, seglen), seglen,
588195972f6Sopenharmony_ci                           &concat_chksum, &concat_chksum_swapped);
589195972f6Sopenharmony_ci        concat_chksummed += seglen;
590195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
591195972f6Sopenharmony_ci      }
592195972f6Sopenharmony_ci
593195972f6Sopenharmony_ci      pos += seglen;
594195972f6Sopenharmony_ci    }
595195972f6Sopenharmony_ci#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
596195972f6Sopenharmony_ci  } else {
597195972f6Sopenharmony_ci#if TCP_OVERSIZE
598195972f6Sopenharmony_ci    LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
599195972f6Sopenharmony_ci                pcb->unsent_oversize == 0);
600195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
601195972f6Sopenharmony_ci  }
602195972f6Sopenharmony_ci
603195972f6Sopenharmony_ci  /*
604195972f6Sopenharmony_ci   * Phase 3: Create new segments.
605195972f6Sopenharmony_ci   *
606195972f6Sopenharmony_ci   * The new segments are chained together in the local 'queue'
607195972f6Sopenharmony_ci   * variable, ready to be appended to pcb->unsent.
608195972f6Sopenharmony_ci   */
609195972f6Sopenharmony_ci  while (pos < len) {
610195972f6Sopenharmony_ci    struct pbuf *p;
611195972f6Sopenharmony_ci    u16_t left = len - pos;
612195972f6Sopenharmony_ci    u16_t max_len = mss_local - optlen;
613195972f6Sopenharmony_ci    u16_t seglen = LWIP_MIN(left, max_len);
614195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
615195972f6Sopenharmony_ci    u16_t chksum = 0;
616195972f6Sopenharmony_ci    u8_t chksum_swapped = 0;
617195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
618195972f6Sopenharmony_ci
619195972f6Sopenharmony_ci    if (apiflags & TCP_WRITE_FLAG_COPY) {
620195972f6Sopenharmony_ci      /* If copy is set, memory should be allocated and data copied
621195972f6Sopenharmony_ci       * into pbuf */
622195972f6Sopenharmony_ci      if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
623195972f6Sopenharmony_ci        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
624195972f6Sopenharmony_ci        goto memerr;
625195972f6Sopenharmony_ci      }
626195972f6Sopenharmony_ci      LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
627195972f6Sopenharmony_ci                  (p->len >= seglen));
628195972f6Sopenharmony_ci      TCP_DATA_COPY2((char *)p->payload + optlen, (const u8_t *)arg + pos, seglen, &chksum, &chksum_swapped);
629195972f6Sopenharmony_ci    } else {
630195972f6Sopenharmony_ci      /* Copy is not set: First allocate a pbuf for holding the data.
631195972f6Sopenharmony_ci       * Since the referenced data is available at least until it is
632195972f6Sopenharmony_ci       * sent out on the link (as it has to be ACKed by the remote
633195972f6Sopenharmony_ci       * party) we can safely use PBUF_ROM instead of PBUF_REF here.
634195972f6Sopenharmony_ci       */
635195972f6Sopenharmony_ci      struct pbuf *p2;
636195972f6Sopenharmony_ci#if TCP_OVERSIZE
637195972f6Sopenharmony_ci      LWIP_ASSERT("oversize == 0", oversize == 0);
638195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
639195972f6Sopenharmony_ci      if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
640195972f6Sopenharmony_ci        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
641195972f6Sopenharmony_ci        goto memerr;
642195972f6Sopenharmony_ci      }
643195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
644195972f6Sopenharmony_ci      /* calculate the checksum of nocopy-data */
645195972f6Sopenharmony_ci      chksum = ~inet_chksum((const u8_t *)arg + pos, seglen);
646195972f6Sopenharmony_ci      if (seglen & 1) {
647195972f6Sopenharmony_ci        chksum_swapped = 1;
648195972f6Sopenharmony_ci        chksum = SWAP_BYTES_IN_WORD(chksum);
649195972f6Sopenharmony_ci      }
650195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
651195972f6Sopenharmony_ci      /* reference the non-volatile payload data */
652195972f6Sopenharmony_ci      ((struct pbuf_rom *)p2)->payload = (const u8_t *)arg + pos;
653195972f6Sopenharmony_ci
654195972f6Sopenharmony_ci      /* Second, allocate a pbuf for the headers. */
655195972f6Sopenharmony_ci      if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
656195972f6Sopenharmony_ci        /* If allocation fails, we have to deallocate the data pbuf as
657195972f6Sopenharmony_ci         * well. */
658195972f6Sopenharmony_ci        pbuf_free(p2);
659195972f6Sopenharmony_ci        LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: could not allocate memory for header pbuf\n"));
660195972f6Sopenharmony_ci        goto memerr;
661195972f6Sopenharmony_ci      }
662195972f6Sopenharmony_ci      /* Concatenate the headers and data pbufs together. */
663195972f6Sopenharmony_ci      pbuf_cat(p/*header*/, p2/*data*/);
664195972f6Sopenharmony_ci    }
665195972f6Sopenharmony_ci
666195972f6Sopenharmony_ci    queuelen += pbuf_clen(p);
667195972f6Sopenharmony_ci
668195972f6Sopenharmony_ci    /* Now that there are more segments queued, we check again if the
669195972f6Sopenharmony_ci     * length of the queue exceeds the configured maximum or
670195972f6Sopenharmony_ci     * overflows. */
671195972f6Sopenharmony_ci    if (queuelen > LWIP_MIN(TCP_SND_QUEUELEN, TCP_SNDQUEUELEN_OVERFLOW)) {
672195972f6Sopenharmony_ci      LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_write: queue too long %"U16_F" (%d)\n",
673195972f6Sopenharmony_ci                  queuelen, (int)TCP_SND_QUEUELEN));
674195972f6Sopenharmony_ci      pbuf_free(p);
675195972f6Sopenharmony_ci      goto memerr;
676195972f6Sopenharmony_ci    }
677195972f6Sopenharmony_ci
678195972f6Sopenharmony_ci    if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
679195972f6Sopenharmony_ci      goto memerr;
680195972f6Sopenharmony_ci    }
681195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
682195972f6Sopenharmony_ci    seg->oversize_left = oversize;
683195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
684195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
685195972f6Sopenharmony_ci    seg->chksum = chksum;
686195972f6Sopenharmony_ci    seg->chksum_swapped = chksum_swapped;
687195972f6Sopenharmony_ci    seg->flags |= TF_SEG_DATA_CHECKSUMMED;
688195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
689195972f6Sopenharmony_ci
690195972f6Sopenharmony_ci    /* first segment of to-be-queued data? */
691195972f6Sopenharmony_ci    if (queue == NULL) {
692195972f6Sopenharmony_ci      queue = seg;
693195972f6Sopenharmony_ci    } else {
694195972f6Sopenharmony_ci      /* Attach the segment to the end of the queued segments */
695195972f6Sopenharmony_ci      LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
696195972f6Sopenharmony_ci      prev_seg->next = seg;
697195972f6Sopenharmony_ci    }
698195972f6Sopenharmony_ci    /* remember last segment of to-be-queued data for next iteration */
699195972f6Sopenharmony_ci    prev_seg = seg;
700195972f6Sopenharmony_ci
701195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
702195972f6Sopenharmony_ci                lwip_ntohl(seg->tcphdr->seqno),
703195972f6Sopenharmony_ci                lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
704195972f6Sopenharmony_ci
705195972f6Sopenharmony_ci    pos += seglen;
706195972f6Sopenharmony_ci  }
707195972f6Sopenharmony_ci
708195972f6Sopenharmony_ci  /*
709195972f6Sopenharmony_ci   * All three segmentation phases were successful. We can commit the
710195972f6Sopenharmony_ci   * transaction.
711195972f6Sopenharmony_ci   */
712195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
713195972f6Sopenharmony_ci  if ((last_unsent != NULL) && (oversize_add != 0)) {
714195972f6Sopenharmony_ci    last_unsent->oversize_left += oversize_add;
715195972f6Sopenharmony_ci  }
716195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
717195972f6Sopenharmony_ci
718195972f6Sopenharmony_ci  /*
719195972f6Sopenharmony_ci   * Phase 1: If data has been added to the preallocated tail of
720195972f6Sopenharmony_ci   * last_unsent, we update the length fields of the pbuf chain.
721195972f6Sopenharmony_ci   */
722195972f6Sopenharmony_ci#if TCP_OVERSIZE
723195972f6Sopenharmony_ci  if (oversize_used > 0) {
724195972f6Sopenharmony_ci    struct pbuf *p;
725195972f6Sopenharmony_ci    /* Bump tot_len of whole chain, len of tail */
726195972f6Sopenharmony_ci    for (p = last_unsent->p; p; p = p->next) {
727195972f6Sopenharmony_ci      p->tot_len += oversize_used;
728195972f6Sopenharmony_ci      if (p->next == NULL) {
729195972f6Sopenharmony_ci        TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
730195972f6Sopenharmony_ci        p->len += oversize_used;
731195972f6Sopenharmony_ci      }
732195972f6Sopenharmony_ci    }
733195972f6Sopenharmony_ci    last_unsent->len += oversize_used;
734195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
735195972f6Sopenharmony_ci    LWIP_ASSERT("last_unsent->oversize_left >= oversize_used",
736195972f6Sopenharmony_ci                last_unsent->oversize_left >= oversize_used);
737195972f6Sopenharmony_ci    last_unsent->oversize_left -= oversize_used;
738195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
739195972f6Sopenharmony_ci  }
740195972f6Sopenharmony_ci  pcb->unsent_oversize = oversize;
741195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
742195972f6Sopenharmony_ci
743195972f6Sopenharmony_ci  /*
744195972f6Sopenharmony_ci   * Phase 2: concat_p can be concatenated onto last_unsent->p, unless we
745195972f6Sopenharmony_ci   * determined that the last ROM pbuf can be extended to include the new data.
746195972f6Sopenharmony_ci   */
747195972f6Sopenharmony_ci  if (concat_p != NULL) {
748195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
749195972f6Sopenharmony_ci                (last_unsent != NULL));
750195972f6Sopenharmony_ci    pbuf_cat(last_unsent->p, concat_p);
751195972f6Sopenharmony_ci    last_unsent->len += concat_p->tot_len;
752195972f6Sopenharmony_ci  } else if (extendlen > 0) {
753195972f6Sopenharmony_ci    struct pbuf *p;
754195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: extension of reference requires reference",
755195972f6Sopenharmony_ci                last_unsent != NULL && last_unsent->p != NULL);
756195972f6Sopenharmony_ci    for (p = last_unsent->p; p->next != NULL; p = p->next) {
757195972f6Sopenharmony_ci      p->tot_len += extendlen;
758195972f6Sopenharmony_ci    }
759195972f6Sopenharmony_ci    p->tot_len += extendlen;
760195972f6Sopenharmony_ci    p->len += extendlen;
761195972f6Sopenharmony_ci    last_unsent->len += extendlen;
762195972f6Sopenharmony_ci  }
763195972f6Sopenharmony_ci
764195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
765195972f6Sopenharmony_ci  if (concat_chksummed) {
766195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: concat checksum needs concatenated data",
767195972f6Sopenharmony_ci                concat_p != NULL || extendlen > 0);
768195972f6Sopenharmony_ci    /*if concat checksumm swapped - swap it back */
769195972f6Sopenharmony_ci    if (concat_chksum_swapped) {
770195972f6Sopenharmony_ci      concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum);
771195972f6Sopenharmony_ci    }
772195972f6Sopenharmony_ci    tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
773195972f6Sopenharmony_ci                       &last_unsent->chksum_swapped);
774195972f6Sopenharmony_ci    last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
775195972f6Sopenharmony_ci  }
776195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
777195972f6Sopenharmony_ci
778195972f6Sopenharmony_ci  /*
779195972f6Sopenharmony_ci   * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
780195972f6Sopenharmony_ci   * is harmless
781195972f6Sopenharmony_ci   */
782195972f6Sopenharmony_ci  if (last_unsent == NULL) {
783195972f6Sopenharmony_ci    pcb->unsent = queue;
784195972f6Sopenharmony_ci  } else {
785195972f6Sopenharmony_ci    last_unsent->next = queue;
786195972f6Sopenharmony_ci  }
787195972f6Sopenharmony_ci
788195972f6Sopenharmony_ci  /*
789195972f6Sopenharmony_ci   * Finally update the pcb state.
790195972f6Sopenharmony_ci   */
791195972f6Sopenharmony_ci  pcb->snd_lbb += len;
792195972f6Sopenharmony_ci  pcb->snd_buf -= len;
793195972f6Sopenharmony_ci  pcb->snd_queuelen = queuelen;
794195972f6Sopenharmony_ci
795195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
796195972f6Sopenharmony_ci                               pcb->snd_queuelen));
797195972f6Sopenharmony_ci  if (pcb->snd_queuelen != 0) {
798195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: valid queue length",
799195972f6Sopenharmony_ci                pcb->unacked != NULL || pcb->unsent != NULL);
800195972f6Sopenharmony_ci  }
801195972f6Sopenharmony_ci
802195972f6Sopenharmony_ci  /* Set the PSH flag in the last segment that we enqueued. */
803195972f6Sopenharmony_ci  if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE) == 0)) {
804195972f6Sopenharmony_ci    TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
805195972f6Sopenharmony_ci  }
806195972f6Sopenharmony_ci
807195972f6Sopenharmony_ci  return ERR_OK;
808195972f6Sopenharmony_cimemerr:
809195972f6Sopenharmony_ci  tcp_set_flags(pcb, TF_NAGLEMEMERR);
810195972f6Sopenharmony_ci  TCP_STATS_INC(tcp.memerr);
811195972f6Sopenharmony_ci
812195972f6Sopenharmony_ci  if (concat_p != NULL) {
813195972f6Sopenharmony_ci    pbuf_free(concat_p);
814195972f6Sopenharmony_ci  }
815195972f6Sopenharmony_ci  if (queue != NULL) {
816195972f6Sopenharmony_ci    tcp_segs_free(queue);
817195972f6Sopenharmony_ci  }
818195972f6Sopenharmony_ci  if (pcb->snd_queuelen != 0) {
819195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
820195972f6Sopenharmony_ci                pcb->unsent != NULL);
821195972f6Sopenharmony_ci  }
822195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
823195972f6Sopenharmony_ci  return ERR_MEM;
824195972f6Sopenharmony_ci}
825195972f6Sopenharmony_ci
826195972f6Sopenharmony_ci/**
827195972f6Sopenharmony_ci * Split segment on the head of the unsent queue.  If return is not
828195972f6Sopenharmony_ci * ERR_OK, existing head remains intact
829195972f6Sopenharmony_ci *
830195972f6Sopenharmony_ci * The split is accomplished by creating a new TCP segment and pbuf
831195972f6Sopenharmony_ci * which holds the remainder payload after the split.  The original
832195972f6Sopenharmony_ci * pbuf is trimmed to new length.  This allows splitting of read-only
833195972f6Sopenharmony_ci * pbufs
834195972f6Sopenharmony_ci *
835195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to split the unsent head
836195972f6Sopenharmony_ci * @param split the amount of payload to remain in the head
837195972f6Sopenharmony_ci */
838195972f6Sopenharmony_cierr_t
839195972f6Sopenharmony_citcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split)
840195972f6Sopenharmony_ci{
841195972f6Sopenharmony_ci  struct tcp_seg *seg = NULL, *useg = NULL;
842195972f6Sopenharmony_ci  struct pbuf *p = NULL;
843195972f6Sopenharmony_ci  u8_t optlen;
844195972f6Sopenharmony_ci  u8_t optflags;
845195972f6Sopenharmony_ci  u8_t split_flags;
846195972f6Sopenharmony_ci  u8_t remainder_flags;
847195972f6Sopenharmony_ci  u16_t remainder;
848195972f6Sopenharmony_ci  u16_t offset;
849195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
850195972f6Sopenharmony_ci  u16_t chksum = 0;
851195972f6Sopenharmony_ci  u8_t chksum_swapped = 0;
852195972f6Sopenharmony_ci  struct pbuf *q;
853195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
854195972f6Sopenharmony_ci
855195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_split_unsent_seg: invalid pcb", pcb != NULL);
856195972f6Sopenharmony_ci
857195972f6Sopenharmony_ci  useg = pcb->unsent;
858195972f6Sopenharmony_ci  if (useg == NULL) {
859195972f6Sopenharmony_ci    return ERR_MEM;
860195972f6Sopenharmony_ci  }
861195972f6Sopenharmony_ci
862195972f6Sopenharmony_ci  if (split == 0) {
863195972f6Sopenharmony_ci    LWIP_ASSERT("Can't split segment into length 0", 0);
864195972f6Sopenharmony_ci    return ERR_VAL;
865195972f6Sopenharmony_ci  }
866195972f6Sopenharmony_ci
867195972f6Sopenharmony_ci  if (useg->len <= split) {
868195972f6Sopenharmony_ci    return ERR_OK;
869195972f6Sopenharmony_ci  }
870195972f6Sopenharmony_ci
871195972f6Sopenharmony_ci  LWIP_ASSERT("split <= mss", split <= pcb->mss);
872195972f6Sopenharmony_ci  LWIP_ASSERT("useg->len > 0", useg->len > 0);
873195972f6Sopenharmony_ci
874195972f6Sopenharmony_ci  /* We should check that we don't exceed TCP_SND_QUEUELEN but we need
875195972f6Sopenharmony_ci   * to split this packet so we may actually exceed the max value by
876195972f6Sopenharmony_ci   * one!
877195972f6Sopenharmony_ci   */
878195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: split_unsent_seg: %u\n", (unsigned int)pcb->snd_queuelen));
879195972f6Sopenharmony_ci
880195972f6Sopenharmony_ci  optflags = useg->flags;
881195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
882195972f6Sopenharmony_ci  /* Remove since checksum is not stored until after tcp_create_segment() */
883195972f6Sopenharmony_ci  optflags &= ~TF_SEG_DATA_CHECKSUMMED;
884195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
885195972f6Sopenharmony_ci  optlen = LWIP_TCP_OPT_LENGTH(optflags);
886195972f6Sopenharmony_ci  remainder = useg->len - split;
887195972f6Sopenharmony_ci
888195972f6Sopenharmony_ci  /* Create new pbuf for the remainder of the split */
889195972f6Sopenharmony_ci  p = pbuf_alloc(PBUF_TRANSPORT, remainder + optlen, PBUF_RAM);
890195972f6Sopenharmony_ci  if (p == NULL) {
891195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
892195972f6Sopenharmony_ci                ("tcp_split_unsent_seg: could not allocate memory for pbuf remainder %u\n", remainder));
893195972f6Sopenharmony_ci    goto memerr;
894195972f6Sopenharmony_ci  }
895195972f6Sopenharmony_ci
896195972f6Sopenharmony_ci  /* Offset into the original pbuf is past TCP/IP headers, options, and split amount */
897195972f6Sopenharmony_ci  offset = useg->p->tot_len - useg->len + split;
898195972f6Sopenharmony_ci  /* Copy remainder into new pbuf, headers and options will not be filled out */
899195972f6Sopenharmony_ci  if (pbuf_copy_partial(useg->p, (u8_t *)p->payload + optlen, remainder, offset ) != remainder) {
900195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
901195972f6Sopenharmony_ci                ("tcp_split_unsent_seg: could not copy pbuf remainder %u\n", remainder));
902195972f6Sopenharmony_ci    goto memerr;
903195972f6Sopenharmony_ci  }
904195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
905195972f6Sopenharmony_ci  /* calculate the checksum on remainder data */
906195972f6Sopenharmony_ci  tcp_seg_add_chksum(~inet_chksum((const u8_t *)p->payload + optlen, remainder), remainder,
907195972f6Sopenharmony_ci                     &chksum, &chksum_swapped);
908195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
909195972f6Sopenharmony_ci
910195972f6Sopenharmony_ci  /* Options are created when calling tcp_output() */
911195972f6Sopenharmony_ci
912195972f6Sopenharmony_ci  /* Migrate flags from original segment */
913195972f6Sopenharmony_ci  split_flags = TCPH_FLAGS(useg->tcphdr);
914195972f6Sopenharmony_ci  remainder_flags = 0; /* ACK added in tcp_output() */
915195972f6Sopenharmony_ci
916195972f6Sopenharmony_ci  if (split_flags & TCP_PSH) {
917195972f6Sopenharmony_ci    split_flags &= ~TCP_PSH;
918195972f6Sopenharmony_ci    remainder_flags |= TCP_PSH;
919195972f6Sopenharmony_ci  }
920195972f6Sopenharmony_ci  if (split_flags & TCP_FIN) {
921195972f6Sopenharmony_ci    split_flags &= ~TCP_FIN;
922195972f6Sopenharmony_ci    remainder_flags |= TCP_FIN;
923195972f6Sopenharmony_ci  }
924195972f6Sopenharmony_ci  /* SYN should be left on split, RST should not be present with data */
925195972f6Sopenharmony_ci
926195972f6Sopenharmony_ci  seg = tcp_create_segment(pcb, p, remainder_flags, lwip_ntohl(useg->tcphdr->seqno) + split, optflags);
927195972f6Sopenharmony_ci  if (seg == NULL) {
928195972f6Sopenharmony_ci    p = NULL; /* Freed by tcp_create_segment */
929195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
930195972f6Sopenharmony_ci                ("tcp_split_unsent_seg: could not create new TCP segment\n"));
931195972f6Sopenharmony_ci    goto memerr;
932195972f6Sopenharmony_ci  }
933195972f6Sopenharmony_ci
934195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
935195972f6Sopenharmony_ci  seg->chksum = chksum;
936195972f6Sopenharmony_ci  seg->chksum_swapped = chksum_swapped;
937195972f6Sopenharmony_ci  seg->flags |= TF_SEG_DATA_CHECKSUMMED;
938195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
939195972f6Sopenharmony_ci
940195972f6Sopenharmony_ci  /* Remove this segment from the queue since trimming it may free pbufs */
941195972f6Sopenharmony_ci  pcb->snd_queuelen -= pbuf_clen(useg->p);
942195972f6Sopenharmony_ci
943195972f6Sopenharmony_ci  /* Trim the original pbuf into our split size.  At this point our remainder segment must be setup
944195972f6Sopenharmony_ci  successfully because we are modifying the original segment */
945195972f6Sopenharmony_ci  pbuf_realloc(useg->p, useg->p->tot_len - remainder);
946195972f6Sopenharmony_ci  useg->len -= remainder;
947195972f6Sopenharmony_ci  TCPH_SET_FLAG(useg->tcphdr, split_flags);
948195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
949195972f6Sopenharmony_ci  /* By trimming, realloc may have actually shrunk the pbuf, so clear oversize_left */
950195972f6Sopenharmony_ci  useg->oversize_left = 0;
951195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
952195972f6Sopenharmony_ci
953195972f6Sopenharmony_ci  /* Add back to the queue with new trimmed pbuf */
954195972f6Sopenharmony_ci  pcb->snd_queuelen += pbuf_clen(useg->p);
955195972f6Sopenharmony_ci
956195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
957195972f6Sopenharmony_ci  /* The checksum on the split segment is now incorrect. We need to re-run it over the split */
958195972f6Sopenharmony_ci  useg->chksum = 0;
959195972f6Sopenharmony_ci  useg->chksum_swapped = 0;
960195972f6Sopenharmony_ci  q = useg->p;
961195972f6Sopenharmony_ci  offset = q->tot_len - useg->len; /* Offset due to exposed headers */
962195972f6Sopenharmony_ci
963195972f6Sopenharmony_ci  /* Advance to the pbuf where the offset ends */
964195972f6Sopenharmony_ci  while (q != NULL && offset > q->len) {
965195972f6Sopenharmony_ci    offset -= q->len;
966195972f6Sopenharmony_ci    q = q->next;
967195972f6Sopenharmony_ci  }
968195972f6Sopenharmony_ci  LWIP_ASSERT("Found start of payload pbuf", q != NULL);
969195972f6Sopenharmony_ci  /* Checksum the first payload pbuf accounting for offset, then other pbufs are all payload */
970195972f6Sopenharmony_ci  for (; q != NULL; offset = 0, q = q->next) {
971195972f6Sopenharmony_ci    tcp_seg_add_chksum(~inet_chksum((const u8_t *)q->payload + offset, q->len - offset), q->len - offset,
972195972f6Sopenharmony_ci                       &useg->chksum, &useg->chksum_swapped);
973195972f6Sopenharmony_ci  }
974195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
975195972f6Sopenharmony_ci
976195972f6Sopenharmony_ci  /* Update number of segments on the queues. Note that length now may
977195972f6Sopenharmony_ci   * exceed TCP_SND_QUEUELEN! We don't have to touch pcb->snd_buf
978195972f6Sopenharmony_ci   * because the total amount of data is constant when packet is split */
979195972f6Sopenharmony_ci  pcb->snd_queuelen += pbuf_clen(seg->p);
980195972f6Sopenharmony_ci
981195972f6Sopenharmony_ci  /* Finally insert remainder into queue after split (which stays head) */
982195972f6Sopenharmony_ci  seg->next = useg->next;
983195972f6Sopenharmony_ci  useg->next = seg;
984195972f6Sopenharmony_ci
985195972f6Sopenharmony_ci#if TCP_OVERSIZE
986195972f6Sopenharmony_ci  /* If remainder is last segment on the unsent, ensure we clear the oversize amount
987195972f6Sopenharmony_ci   * because the remainder is always sized to the exact remaining amount */
988195972f6Sopenharmony_ci  if (seg->next == NULL) {
989195972f6Sopenharmony_ci    pcb->unsent_oversize = 0;
990195972f6Sopenharmony_ci  }
991195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
992195972f6Sopenharmony_ci
993195972f6Sopenharmony_ci  return ERR_OK;
994195972f6Sopenharmony_cimemerr:
995195972f6Sopenharmony_ci  TCP_STATS_INC(tcp.memerr);
996195972f6Sopenharmony_ci
997195972f6Sopenharmony_ci  LWIP_ASSERT("seg == NULL", seg == NULL);
998195972f6Sopenharmony_ci  if (p != NULL) {
999195972f6Sopenharmony_ci    pbuf_free(p);
1000195972f6Sopenharmony_ci  }
1001195972f6Sopenharmony_ci
1002195972f6Sopenharmony_ci  return ERR_MEM;
1003195972f6Sopenharmony_ci}
1004195972f6Sopenharmony_ci
1005195972f6Sopenharmony_ci/**
1006195972f6Sopenharmony_ci * Called by tcp_close() to send a segment including FIN flag but not data.
1007195972f6Sopenharmony_ci * This FIN may be added to an existing segment or a new, otherwise empty
1008195972f6Sopenharmony_ci * segment is enqueued.
1009195972f6Sopenharmony_ci *
1010195972f6Sopenharmony_ci * @param pcb the tcp_pcb over which to send a segment
1011195972f6Sopenharmony_ci * @return ERR_OK if sent, another err_t otherwise
1012195972f6Sopenharmony_ci */
1013195972f6Sopenharmony_cierr_t
1014195972f6Sopenharmony_citcp_send_fin(struct tcp_pcb *pcb)
1015195972f6Sopenharmony_ci{
1016195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_send_fin: invalid pcb", pcb != NULL);
1017195972f6Sopenharmony_ci
1018195972f6Sopenharmony_ci  /* first, try to add the fin to the last unsent segment */
1019195972f6Sopenharmony_ci  if (pcb->unsent != NULL) {
1020195972f6Sopenharmony_ci    struct tcp_seg *last_unsent;
1021195972f6Sopenharmony_ci    for (last_unsent = pcb->unsent; last_unsent->next != NULL;
1022195972f6Sopenharmony_ci         last_unsent = last_unsent->next);
1023195972f6Sopenharmony_ci
1024195972f6Sopenharmony_ci    if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
1025195972f6Sopenharmony_ci      /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
1026195972f6Sopenharmony_ci      TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
1027195972f6Sopenharmony_ci      tcp_set_flags(pcb, TF_FIN);
1028195972f6Sopenharmony_ci      return ERR_OK;
1029195972f6Sopenharmony_ci    }
1030195972f6Sopenharmony_ci  }
1031195972f6Sopenharmony_ci  /* no data, no length, flags, copy=1, no optdata */
1032195972f6Sopenharmony_ci  return tcp_enqueue_flags(pcb, TCP_FIN);
1033195972f6Sopenharmony_ci}
1034195972f6Sopenharmony_ci
1035195972f6Sopenharmony_ci/**
1036195972f6Sopenharmony_ci * Enqueue SYN or FIN for transmission.
1037195972f6Sopenharmony_ci *
1038195972f6Sopenharmony_ci * Called by @ref tcp_connect, tcp_listen_input, and @ref tcp_close
1039195972f6Sopenharmony_ci * (via @ref tcp_send_fin)
1040195972f6Sopenharmony_ci *
1041195972f6Sopenharmony_ci * @param pcb Protocol control block for the TCP connection.
1042195972f6Sopenharmony_ci * @param flags TCP header flags to set in the outgoing segment.
1043195972f6Sopenharmony_ci */
1044195972f6Sopenharmony_cierr_t
1045195972f6Sopenharmony_citcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
1046195972f6Sopenharmony_ci{
1047195972f6Sopenharmony_ci  struct pbuf *p;
1048195972f6Sopenharmony_ci  struct tcp_seg *seg;
1049195972f6Sopenharmony_ci  u8_t optflags = 0;
1050195972f6Sopenharmony_ci  u8_t optlen = 0;
1051195972f6Sopenharmony_ci
1052195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
1053195972f6Sopenharmony_ci
1054195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
1055195972f6Sopenharmony_ci              (flags & (TCP_SYN | TCP_FIN)) != 0);
1056195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_enqueue_flags: invalid pcb", pcb != NULL);
1057195972f6Sopenharmony_ci
1058195972f6Sopenharmony_ci  /* No need to check pcb->snd_queuelen if only SYN or FIN are allowed! */
1059195972f6Sopenharmony_ci
1060195972f6Sopenharmony_ci  /* Get options for this segment. This is a special case since this is the
1061195972f6Sopenharmony_ci     only place where a SYN can be sent. */
1062195972f6Sopenharmony_ci  if (flags & TCP_SYN) {
1063195972f6Sopenharmony_ci    optflags = TF_SEG_OPTS_MSS;
1064195972f6Sopenharmony_ci#if LWIP_WND_SCALE
1065195972f6Sopenharmony_ci    if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) {
1066195972f6Sopenharmony_ci      /* In a <SYN,ACK> (sent in state SYN_RCVD), the window scale option may only
1067195972f6Sopenharmony_ci         be sent if we received a window scale option from the remote host. */
1068195972f6Sopenharmony_ci      optflags |= TF_SEG_OPTS_WND_SCALE;
1069195972f6Sopenharmony_ci    }
1070195972f6Sopenharmony_ci#endif /* LWIP_WND_SCALE */
1071195972f6Sopenharmony_ci#if LWIP_TCP_SACK_OUT
1072195972f6Sopenharmony_ci    if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_SACK)) {
1073195972f6Sopenharmony_ci      /* In a <SYN,ACK> (sent in state SYN_RCVD), the SACK_PERM option may only
1074195972f6Sopenharmony_ci         be sent if we received a SACK_PERM option from the remote host. */
1075195972f6Sopenharmony_ci      optflags |= TF_SEG_OPTS_SACK_PERM;
1076195972f6Sopenharmony_ci    }
1077195972f6Sopenharmony_ci#endif /* LWIP_TCP_SACK_OUT */
1078195972f6Sopenharmony_ci  }
1079195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
1080195972f6Sopenharmony_ci  if ((pcb->flags & TF_TIMESTAMP) || ((flags & TCP_SYN) && (pcb->state != SYN_RCVD))) {
1081195972f6Sopenharmony_ci    /* Make sure the timestamp option is only included in data segments if we
1082195972f6Sopenharmony_ci       agreed about it with the remote host (and in active open SYN segments). */
1083195972f6Sopenharmony_ci    optflags |= TF_SEG_OPTS_TS;
1084195972f6Sopenharmony_ci  }
1085195972f6Sopenharmony_ci#endif /* LWIP_TCP_TIMESTAMPS */
1086195972f6Sopenharmony_ci  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
1087195972f6Sopenharmony_ci
1088195972f6Sopenharmony_ci  /* Allocate pbuf with room for TCP header + options */
1089195972f6Sopenharmony_ci  if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
1090195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_NAGLEMEMERR);
1091195972f6Sopenharmony_ci    TCP_STATS_INC(tcp.memerr);
1092195972f6Sopenharmony_ci    return ERR_MEM;
1093195972f6Sopenharmony_ci  }
1094195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
1095195972f6Sopenharmony_ci              (p->len >= optlen));
1096195972f6Sopenharmony_ci
1097195972f6Sopenharmony_ci  /* Allocate memory for tcp_seg, and fill in fields. */
1098195972f6Sopenharmony_ci  if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
1099195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_NAGLEMEMERR);
1100195972f6Sopenharmony_ci    TCP_STATS_INC(tcp.memerr);
1101195972f6Sopenharmony_ci    return ERR_MEM;
1102195972f6Sopenharmony_ci  }
1103195972f6Sopenharmony_ci  LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % LWIP_MIN(MEM_ALIGNMENT, 4)) == 0);
1104195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg->len == 0);
1105195972f6Sopenharmony_ci
1106195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE,
1107195972f6Sopenharmony_ci              ("tcp_enqueue_flags: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n",
1108195972f6Sopenharmony_ci               lwip_ntohl(seg->tcphdr->seqno),
1109195972f6Sopenharmony_ci               lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
1110195972f6Sopenharmony_ci               (u16_t)flags));
1111195972f6Sopenharmony_ci
1112195972f6Sopenharmony_ci  /* Now append seg to pcb->unsent queue */
1113195972f6Sopenharmony_ci  if (pcb->unsent == NULL) {
1114195972f6Sopenharmony_ci    pcb->unsent = seg;
1115195972f6Sopenharmony_ci  } else {
1116195972f6Sopenharmony_ci    struct tcp_seg *useg;
1117195972f6Sopenharmony_ci    for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
1118195972f6Sopenharmony_ci    useg->next = seg;
1119195972f6Sopenharmony_ci  }
1120195972f6Sopenharmony_ci#if TCP_OVERSIZE
1121195972f6Sopenharmony_ci  /* The new unsent tail has no space */
1122195972f6Sopenharmony_ci  pcb->unsent_oversize = 0;
1123195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
1124195972f6Sopenharmony_ci
1125195972f6Sopenharmony_ci  /* SYN and FIN bump the sequence number */
1126195972f6Sopenharmony_ci  if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
1127195972f6Sopenharmony_ci    pcb->snd_lbb++;
1128195972f6Sopenharmony_ci    /* optlen does not influence snd_buf */
1129195972f6Sopenharmony_ci  }
1130195972f6Sopenharmony_ci  if (flags & TCP_FIN) {
1131195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_FIN);
1132195972f6Sopenharmony_ci  }
1133195972f6Sopenharmony_ci
1134195972f6Sopenharmony_ci  /* update number of segments on the queues */
1135195972f6Sopenharmony_ci  pcb->snd_queuelen += pbuf_clen(seg->p);
1136195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
1137195972f6Sopenharmony_ci  if (pcb->snd_queuelen != 0) {
1138195972f6Sopenharmony_ci    LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
1139195972f6Sopenharmony_ci                pcb->unacked != NULL || pcb->unsent != NULL);
1140195972f6Sopenharmony_ci  }
1141195972f6Sopenharmony_ci
1142195972f6Sopenharmony_ci  return ERR_OK;
1143195972f6Sopenharmony_ci}
1144195972f6Sopenharmony_ci
1145195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
1146195972f6Sopenharmony_ci/* Build a timestamp option (12 bytes long) at the specified options pointer)
1147195972f6Sopenharmony_ci *
1148195972f6Sopenharmony_ci * @param pcb tcp_pcb
1149195972f6Sopenharmony_ci * @param opts option pointer where to store the timestamp option
1150195972f6Sopenharmony_ci */
1151195972f6Sopenharmony_cistatic void
1152195972f6Sopenharmony_citcp_build_timestamp_option(const struct tcp_pcb *pcb, u32_t *opts)
1153195972f6Sopenharmony_ci{
1154195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_build_timestamp_option: invalid pcb", pcb != NULL);
1155195972f6Sopenharmony_ci
1156195972f6Sopenharmony_ci  /* Pad with two NOP options to make everything nicely aligned */
1157195972f6Sopenharmony_ci  opts[0] = PP_HTONL(0x0101080A);
1158195972f6Sopenharmony_ci  opts[1] = lwip_htonl(sys_now());
1159195972f6Sopenharmony_ci  opts[2] = lwip_htonl(pcb->ts_recent);
1160195972f6Sopenharmony_ci}
1161195972f6Sopenharmony_ci#endif
1162195972f6Sopenharmony_ci
1163195972f6Sopenharmony_ci#if LWIP_TCP_SACK_OUT
1164195972f6Sopenharmony_ci/**
1165195972f6Sopenharmony_ci * Calculates the number of SACK entries that should be generated.
1166195972f6Sopenharmony_ci * It takes into account whether TF_SACK flag is set,
1167195972f6Sopenharmony_ci * the number of SACK entries in tcp_pcb that are valid,
1168195972f6Sopenharmony_ci * as well as the available options size.
1169195972f6Sopenharmony_ci *
1170195972f6Sopenharmony_ci * @param pcb tcp_pcb
1171195972f6Sopenharmony_ci * @param optlen the length of other TCP options (in bytes)
1172195972f6Sopenharmony_ci * @return the number of SACK ranges that can be used
1173195972f6Sopenharmony_ci */
1174195972f6Sopenharmony_cistatic u8_t
1175195972f6Sopenharmony_citcp_get_num_sacks(const struct tcp_pcb *pcb, u8_t optlen)
1176195972f6Sopenharmony_ci{
1177195972f6Sopenharmony_ci  u8_t num_sacks = 0;
1178195972f6Sopenharmony_ci
1179195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_get_num_sacks: invalid pcb", pcb != NULL);
1180195972f6Sopenharmony_ci
1181195972f6Sopenharmony_ci  if (pcb->flags & TF_SACK) {
1182195972f6Sopenharmony_ci    u8_t i;
1183195972f6Sopenharmony_ci
1184195972f6Sopenharmony_ci    /* The first SACK takes up 12 bytes (it includes SACK header and two NOP options),
1185195972f6Sopenharmony_ci       each additional one - 8 bytes. */
1186195972f6Sopenharmony_ci    optlen += 12;
1187195972f6Sopenharmony_ci
1188195972f6Sopenharmony_ci    /* Max options size = 40, number of SACK array entries = LWIP_TCP_MAX_SACK_NUM */
1189195972f6Sopenharmony_ci    for (i = 0; (i < LWIP_TCP_MAX_SACK_NUM) && (optlen <= TCP_MAX_OPTION_BYTES) &&
1190195972f6Sopenharmony_ci         LWIP_TCP_SACK_VALID(pcb, i); ++i) {
1191195972f6Sopenharmony_ci      ++num_sacks;
1192195972f6Sopenharmony_ci      optlen += 8;
1193195972f6Sopenharmony_ci    }
1194195972f6Sopenharmony_ci  }
1195195972f6Sopenharmony_ci
1196195972f6Sopenharmony_ci  return num_sacks;
1197195972f6Sopenharmony_ci}
1198195972f6Sopenharmony_ci
1199195972f6Sopenharmony_ci/** Build a SACK option (12 or more bytes long) at the specified options pointer)
1200195972f6Sopenharmony_ci *
1201195972f6Sopenharmony_ci * @param pcb tcp_pcb
1202195972f6Sopenharmony_ci * @param opts option pointer where to store the SACK option
1203195972f6Sopenharmony_ci * @param num_sacks the number of SACKs to store
1204195972f6Sopenharmony_ci */
1205195972f6Sopenharmony_cistatic void
1206195972f6Sopenharmony_citcp_build_sack_option(const struct tcp_pcb *pcb, u32_t *opts, u8_t num_sacks)
1207195972f6Sopenharmony_ci{
1208195972f6Sopenharmony_ci  u8_t i;
1209195972f6Sopenharmony_ci
1210195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_build_sack_option: invalid pcb", pcb != NULL);
1211195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_build_sack_option: invalid opts", opts != NULL);
1212195972f6Sopenharmony_ci
1213195972f6Sopenharmony_ci  /* Pad with two NOP options to make everything nicely aligned.
1214195972f6Sopenharmony_ci     We add the length (of just the SACK option, not the NOPs in front of it),
1215195972f6Sopenharmony_ci     which is 2B of header, plus 8B for each SACK. */
1216195972f6Sopenharmony_ci  *(opts++) = PP_HTONL(0x01010500 + 2 + num_sacks * 8);
1217195972f6Sopenharmony_ci
1218195972f6Sopenharmony_ci  for (i = 0; i < num_sacks; ++i) {
1219195972f6Sopenharmony_ci    *(opts++) = lwip_htonl(pcb->rcv_sacks[i].left);
1220195972f6Sopenharmony_ci    *(opts++) = lwip_htonl(pcb->rcv_sacks[i].right);
1221195972f6Sopenharmony_ci  }
1222195972f6Sopenharmony_ci}
1223195972f6Sopenharmony_ci
1224195972f6Sopenharmony_ci#endif
1225195972f6Sopenharmony_ci
1226195972f6Sopenharmony_ci#if LWIP_WND_SCALE
1227195972f6Sopenharmony_ci/** Build a window scale option (3 bytes long) at the specified options pointer)
1228195972f6Sopenharmony_ci *
1229195972f6Sopenharmony_ci * @param opts option pointer where to store the window scale option
1230195972f6Sopenharmony_ci */
1231195972f6Sopenharmony_cistatic void
1232195972f6Sopenharmony_citcp_build_wnd_scale_option(u32_t *opts)
1233195972f6Sopenharmony_ci{
1234195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_build_wnd_scale_option: invalid opts", opts != NULL);
1235195972f6Sopenharmony_ci
1236195972f6Sopenharmony_ci  /* Pad with one NOP option to make everything nicely aligned */
1237195972f6Sopenharmony_ci  opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE);
1238195972f6Sopenharmony_ci}
1239195972f6Sopenharmony_ci#endif
1240195972f6Sopenharmony_ci
1241195972f6Sopenharmony_ci/**
1242195972f6Sopenharmony_ci * @ingroup tcp_raw
1243195972f6Sopenharmony_ci * Find out what we can send and send it
1244195972f6Sopenharmony_ci *
1245195972f6Sopenharmony_ci * @param pcb Protocol control block for the TCP connection to send data
1246195972f6Sopenharmony_ci * @return ERR_OK if data has been sent or nothing to send
1247195972f6Sopenharmony_ci *         another err_t on error
1248195972f6Sopenharmony_ci */
1249195972f6Sopenharmony_cierr_t
1250195972f6Sopenharmony_citcp_output(struct tcp_pcb *pcb)
1251195972f6Sopenharmony_ci{
1252195972f6Sopenharmony_ci  struct tcp_seg *seg, *useg;
1253195972f6Sopenharmony_ci  u32_t wnd, snd_nxt;
1254195972f6Sopenharmony_ci  err_t err;
1255195972f6Sopenharmony_ci  struct netif *netif;
1256195972f6Sopenharmony_ci#if TCP_CWND_DEBUG
1257195972f6Sopenharmony_ci  s16_t i = 0;
1258195972f6Sopenharmony_ci#endif /* TCP_CWND_DEBUG */
1259195972f6Sopenharmony_ci
1260195972f6Sopenharmony_ci  LWIP_ASSERT_CORE_LOCKED();
1261195972f6Sopenharmony_ci
1262195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output: invalid pcb", pcb != NULL);
1263195972f6Sopenharmony_ci  /* pcb->state LISTEN not allowed here */
1264195972f6Sopenharmony_ci  LWIP_ASSERT("don't call tcp_output for listen-pcbs",
1265195972f6Sopenharmony_ci              pcb->state != LISTEN);
1266195972f6Sopenharmony_ci
1267195972f6Sopenharmony_ci  /* First, check if we are invoked by the TCP input processing
1268195972f6Sopenharmony_ci     code. If so, we do not output anything. Instead, we rely on the
1269195972f6Sopenharmony_ci     input processing code to call us when input processing is done
1270195972f6Sopenharmony_ci     with. */
1271195972f6Sopenharmony_ci  if (tcp_input_pcb == pcb) {
1272195972f6Sopenharmony_ci    return ERR_OK;
1273195972f6Sopenharmony_ci  }
1274195972f6Sopenharmony_ci
1275195972f6Sopenharmony_ci  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
1276195972f6Sopenharmony_ci
1277195972f6Sopenharmony_ci  seg = pcb->unsent;
1278195972f6Sopenharmony_ci
1279195972f6Sopenharmony_ci  if (seg == NULL) {
1280195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
1281195972f6Sopenharmony_ci                                   (void *)pcb->unsent));
1282195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F
1283195972f6Sopenharmony_ci                                 ", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
1284195972f6Sopenharmony_ci                                 ", seg == NULL, ack %"U32_F"\n",
1285195972f6Sopenharmony_ci                                 pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
1286195972f6Sopenharmony_ci
1287195972f6Sopenharmony_ci    /* If the TF_ACK_NOW flag is set and the ->unsent queue is empty, construct
1288195972f6Sopenharmony_ci     * an empty ACK segment and send it. */
1289195972f6Sopenharmony_ci    if (pcb->flags & TF_ACK_NOW) {
1290195972f6Sopenharmony_ci      return tcp_send_empty_ack(pcb);
1291195972f6Sopenharmony_ci    }
1292195972f6Sopenharmony_ci    /* nothing to send: shortcut out of here */
1293195972f6Sopenharmony_ci    goto output_done;
1294195972f6Sopenharmony_ci  } else {
1295195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_CWND_DEBUG,
1296195972f6Sopenharmony_ci                ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F
1297195972f6Sopenharmony_ci                 ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
1298195972f6Sopenharmony_ci                 pcb->snd_wnd, pcb->cwnd, wnd,
1299195972f6Sopenharmony_ci                 lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
1300195972f6Sopenharmony_ci                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
1301195972f6Sopenharmony_ci  }
1302195972f6Sopenharmony_ci
1303195972f6Sopenharmony_ci  netif = tcp_route(pcb, &pcb->local_ip, &pcb->remote_ip);
1304195972f6Sopenharmony_ci  if (netif == NULL) {
1305195972f6Sopenharmony_ci    return ERR_RTE;
1306195972f6Sopenharmony_ci  }
1307195972f6Sopenharmony_ci
1308195972f6Sopenharmony_ci  /* If we don't have a local IP address, we get one from netif */
1309195972f6Sopenharmony_ci  if (ip_addr_isany(&pcb->local_ip)) {
1310195972f6Sopenharmony_ci    const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, &pcb->remote_ip);
1311195972f6Sopenharmony_ci    if (local_ip == NULL) {
1312195972f6Sopenharmony_ci      return ERR_RTE;
1313195972f6Sopenharmony_ci    }
1314195972f6Sopenharmony_ci    ip_addr_copy(pcb->local_ip, *local_ip);
1315195972f6Sopenharmony_ci  }
1316195972f6Sopenharmony_ci
1317195972f6Sopenharmony_ci  /* Handle the current segment not fitting within the window */
1318195972f6Sopenharmony_ci  if (lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd) {
1319195972f6Sopenharmony_ci    /* We need to start the persistent timer when the next unsent segment does not fit
1320195972f6Sopenharmony_ci     * within the remaining (could be 0) send window and RTO timer is not running (we
1321195972f6Sopenharmony_ci     * have no in-flight data). If window is still too small after persist timer fires,
1322195972f6Sopenharmony_ci     * then we split the segment. We don't consider the congestion window since a cwnd
1323195972f6Sopenharmony_ci     * smaller than 1 SMSS implies in-flight data
1324195972f6Sopenharmony_ci     */
1325195972f6Sopenharmony_ci    if (wnd == pcb->snd_wnd && pcb->unacked == NULL && pcb->persist_backoff == 0) {
1326195972f6Sopenharmony_ci      pcb->persist_cnt = 0;
1327195972f6Sopenharmony_ci      pcb->persist_backoff = 1;
1328195972f6Sopenharmony_ci      pcb->persist_probe = 0;
1329195972f6Sopenharmony_ci    }
1330195972f6Sopenharmony_ci    /* We need an ACK, but can't send data now, so send an empty ACK */
1331195972f6Sopenharmony_ci    if (pcb->flags & TF_ACK_NOW) {
1332195972f6Sopenharmony_ci      return tcp_send_empty_ack(pcb);
1333195972f6Sopenharmony_ci    }
1334195972f6Sopenharmony_ci    goto output_done;
1335195972f6Sopenharmony_ci  }
1336195972f6Sopenharmony_ci  /* Stop persist timer, above conditions are not active */
1337195972f6Sopenharmony_ci  pcb->persist_backoff = 0;
1338195972f6Sopenharmony_ci
1339195972f6Sopenharmony_ci  /* useg should point to last segment on unacked queue */
1340195972f6Sopenharmony_ci  useg = pcb->unacked;
1341195972f6Sopenharmony_ci  if (useg != NULL) {
1342195972f6Sopenharmony_ci    for (; useg->next != NULL; useg = useg->next);
1343195972f6Sopenharmony_ci  }
1344195972f6Sopenharmony_ci  /* data available and window allows it to be sent? */
1345195972f6Sopenharmony_ci  while (seg != NULL &&
1346195972f6Sopenharmony_ci         lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
1347195972f6Sopenharmony_ci    LWIP_ASSERT("RST not expected here!",
1348195972f6Sopenharmony_ci                (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
1349195972f6Sopenharmony_ci    /* Stop sending if the nagle algorithm would prevent it
1350195972f6Sopenharmony_ci     * Don't stop:
1351195972f6Sopenharmony_ci     * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
1352195972f6Sopenharmony_ci     * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
1353195972f6Sopenharmony_ci     *   either seg->next != NULL or pcb->unacked == NULL;
1354195972f6Sopenharmony_ci     *   RST is no sent using tcp_write/tcp_output.
1355195972f6Sopenharmony_ci     */
1356195972f6Sopenharmony_ci    if ((tcp_do_output_nagle(pcb) == 0) &&
1357195972f6Sopenharmony_ci        ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
1358195972f6Sopenharmony_ci      break;
1359195972f6Sopenharmony_ci    }
1360195972f6Sopenharmony_ci#if TCP_CWND_DEBUG
1361195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
1362195972f6Sopenharmony_ci                                 pcb->snd_wnd, pcb->cwnd, wnd,
1363195972f6Sopenharmony_ci                                 lwip_ntohl(seg->tcphdr->seqno) + seg->len -
1364195972f6Sopenharmony_ci                                 pcb->lastack,
1365195972f6Sopenharmony_ci                                 lwip_ntohl(seg->tcphdr->seqno), pcb->lastack, i));
1366195972f6Sopenharmony_ci    ++i;
1367195972f6Sopenharmony_ci#endif /* TCP_CWND_DEBUG */
1368195972f6Sopenharmony_ci
1369195972f6Sopenharmony_ci    if (pcb->state != SYN_SENT) {
1370195972f6Sopenharmony_ci      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
1371195972f6Sopenharmony_ci    }
1372195972f6Sopenharmony_ci
1373195972f6Sopenharmony_ci    err = tcp_output_segment(seg, pcb, netif);
1374195972f6Sopenharmony_ci    if (err != ERR_OK) {
1375195972f6Sopenharmony_ci      /* segment could not be sent, for whatever reason */
1376195972f6Sopenharmony_ci      tcp_set_flags(pcb, TF_NAGLEMEMERR);
1377195972f6Sopenharmony_ci      return err;
1378195972f6Sopenharmony_ci    }
1379195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
1380195972f6Sopenharmony_ci    seg->oversize_left = 0;
1381195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
1382195972f6Sopenharmony_ci    pcb->unsent = seg->next;
1383195972f6Sopenharmony_ci    if (pcb->state != SYN_SENT) {
1384195972f6Sopenharmony_ci      tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
1385195972f6Sopenharmony_ci    }
1386195972f6Sopenharmony_ci    snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
1387195972f6Sopenharmony_ci    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
1388195972f6Sopenharmony_ci      pcb->snd_nxt = snd_nxt;
1389195972f6Sopenharmony_ci    }
1390195972f6Sopenharmony_ci    /* put segment on unacknowledged list if length > 0 */
1391195972f6Sopenharmony_ci    if (TCP_TCPLEN(seg) > 0) {
1392195972f6Sopenharmony_ci      seg->next = NULL;
1393195972f6Sopenharmony_ci      /* unacked list is empty? */
1394195972f6Sopenharmony_ci      if (pcb->unacked == NULL) {
1395195972f6Sopenharmony_ci        pcb->unacked = seg;
1396195972f6Sopenharmony_ci        useg = seg;
1397195972f6Sopenharmony_ci        /* unacked list is not empty? */
1398195972f6Sopenharmony_ci      } else {
1399195972f6Sopenharmony_ci        /* In the case of fast retransmit, the packet should not go to the tail
1400195972f6Sopenharmony_ci         * of the unacked queue, but rather somewhere before it. We need to check for
1401195972f6Sopenharmony_ci         * this case. -STJ Jul 27, 2004 */
1402195972f6Sopenharmony_ci        if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno), lwip_ntohl(useg->tcphdr->seqno))) {
1403195972f6Sopenharmony_ci          /* add segment to before tail of unacked list, keeping the list sorted */
1404195972f6Sopenharmony_ci          struct tcp_seg **cur_seg = &(pcb->unacked);
1405195972f6Sopenharmony_ci          while (*cur_seg &&
1406195972f6Sopenharmony_ci                 TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
1407195972f6Sopenharmony_ci            cur_seg = &((*cur_seg)->next );
1408195972f6Sopenharmony_ci          }
1409195972f6Sopenharmony_ci          seg->next = (*cur_seg);
1410195972f6Sopenharmony_ci          (*cur_seg) = seg;
1411195972f6Sopenharmony_ci        } else {
1412195972f6Sopenharmony_ci          /* add segment to tail of unacked list */
1413195972f6Sopenharmony_ci          useg->next = seg;
1414195972f6Sopenharmony_ci          useg = useg->next;
1415195972f6Sopenharmony_ci        }
1416195972f6Sopenharmony_ci      }
1417195972f6Sopenharmony_ci      /* do not queue empty segments on the unacked list */
1418195972f6Sopenharmony_ci    } else {
1419195972f6Sopenharmony_ci      tcp_seg_free(seg);
1420195972f6Sopenharmony_ci    }
1421195972f6Sopenharmony_ci    seg = pcb->unsent;
1422195972f6Sopenharmony_ci  }
1423195972f6Sopenharmony_ci#if TCP_OVERSIZE
1424195972f6Sopenharmony_ci  if (pcb->unsent == NULL) {
1425195972f6Sopenharmony_ci    /* last unsent has been removed, reset unsent_oversize */
1426195972f6Sopenharmony_ci    pcb->unsent_oversize = 0;
1427195972f6Sopenharmony_ci  }
1428195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
1429195972f6Sopenharmony_ci
1430195972f6Sopenharmony_cioutput_done:
1431195972f6Sopenharmony_ci  tcp_clear_flags(pcb, TF_NAGLEMEMERR);
1432195972f6Sopenharmony_ci  return ERR_OK;
1433195972f6Sopenharmony_ci}
1434195972f6Sopenharmony_ci
1435195972f6Sopenharmony_ci/** Check if a segment's pbufs are used by someone else than TCP.
1436195972f6Sopenharmony_ci * This can happen on retransmission if the pbuf of this segment is still
1437195972f6Sopenharmony_ci * referenced by the netif driver due to deferred transmission.
1438195972f6Sopenharmony_ci * This is the case (only!) if someone down the TX call path called
1439195972f6Sopenharmony_ci * pbuf_ref() on one of the pbufs!
1440195972f6Sopenharmony_ci *
1441195972f6Sopenharmony_ci * @arg seg the tcp segment to check
1442195972f6Sopenharmony_ci * @return 1 if ref != 1, 0 if ref == 1
1443195972f6Sopenharmony_ci */
1444195972f6Sopenharmony_cistatic int
1445195972f6Sopenharmony_citcp_output_segment_busy(const struct tcp_seg *seg)
1446195972f6Sopenharmony_ci{
1447195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_segment_busy: invalid seg", seg != NULL);
1448195972f6Sopenharmony_ci
1449195972f6Sopenharmony_ci  /* We only need to check the first pbuf here:
1450195972f6Sopenharmony_ci     If a pbuf is queued for transmission, a driver calls pbuf_ref(),
1451195972f6Sopenharmony_ci     which only changes the ref count of the first pbuf */
1452195972f6Sopenharmony_ci  if (seg->p->ref != 1) {
1453195972f6Sopenharmony_ci    /* other reference found */
1454195972f6Sopenharmony_ci    return 1;
1455195972f6Sopenharmony_ci  }
1456195972f6Sopenharmony_ci  /* no other references found */
1457195972f6Sopenharmony_ci  return 0;
1458195972f6Sopenharmony_ci}
1459195972f6Sopenharmony_ci
1460195972f6Sopenharmony_ci/**
1461195972f6Sopenharmony_ci * Called by tcp_output() to actually send a TCP segment over IP.
1462195972f6Sopenharmony_ci *
1463195972f6Sopenharmony_ci * @param seg the tcp_seg to send
1464195972f6Sopenharmony_ci * @param pcb the tcp_pcb for the TCP connection used to send the segment
1465195972f6Sopenharmony_ci * @param netif the netif used to send the segment
1466195972f6Sopenharmony_ci */
1467195972f6Sopenharmony_cistatic err_t
1468195972f6Sopenharmony_citcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif)
1469195972f6Sopenharmony_ci{
1470195972f6Sopenharmony_ci  err_t err;
1471195972f6Sopenharmony_ci  u16_t len;
1472195972f6Sopenharmony_ci  u32_t *opts;
1473195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
1474195972f6Sopenharmony_ci  int seg_chksum_was_swapped = 0;
1475195972f6Sopenharmony_ci#endif
1476195972f6Sopenharmony_ci
1477195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_segment: invalid seg", seg != NULL);
1478195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_segment: invalid pcb", pcb != NULL);
1479195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_segment: invalid netif", netif != NULL);
1480195972f6Sopenharmony_ci
1481195972f6Sopenharmony_ci  if (tcp_output_segment_busy(seg)) {
1482195972f6Sopenharmony_ci    /* This should not happen: rexmit functions should have checked this.
1483195972f6Sopenharmony_ci       However, since this function modifies p->len, we must not continue in this case. */
1484195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_RTO_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_output_segment: segment busy\n"));
1485195972f6Sopenharmony_ci    return ERR_OK;
1486195972f6Sopenharmony_ci  }
1487195972f6Sopenharmony_ci
1488195972f6Sopenharmony_ci  /* The TCP header has already been constructed, but the ackno and
1489195972f6Sopenharmony_ci   wnd fields remain. */
1490195972f6Sopenharmony_ci  seg->tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
1491195972f6Sopenharmony_ci
1492195972f6Sopenharmony_ci  /* advertise our receive window size in this TCP segment */
1493195972f6Sopenharmony_ci#if LWIP_WND_SCALE
1494195972f6Sopenharmony_ci  if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
1495195972f6Sopenharmony_ci    /* The Window field in a SYN segment itself (the only type where we send
1496195972f6Sopenharmony_ci       the window scale option) is never scaled. */
1497195972f6Sopenharmony_ci    seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(pcb->rcv_ann_wnd));
1498195972f6Sopenharmony_ci  } else
1499195972f6Sopenharmony_ci#endif /* LWIP_WND_SCALE */
1500195972f6Sopenharmony_ci  {
1501195972f6Sopenharmony_ci    seg->tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
1502195972f6Sopenharmony_ci  }
1503195972f6Sopenharmony_ci
1504195972f6Sopenharmony_ci  pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
1505195972f6Sopenharmony_ci
1506195972f6Sopenharmony_ci  /* Add any requested options.  NB MSS option is only set on SYN
1507195972f6Sopenharmony_ci     packets, so ignore it here */
1508195972f6Sopenharmony_ci  /* cast through void* to get rid of alignment warnings */
1509195972f6Sopenharmony_ci  opts = (u32_t *)(void *)(seg->tcphdr + 1);
1510195972f6Sopenharmony_ci  if (seg->flags & TF_SEG_OPTS_MSS) {
1511195972f6Sopenharmony_ci    u16_t mss;
1512195972f6Sopenharmony_ci#if TCP_CALCULATE_EFF_SEND_MSS
1513195972f6Sopenharmony_ci    mss = tcp_eff_send_mss_netif(TCP_MSS, netif, &pcb->remote_ip);
1514195972f6Sopenharmony_ci#else /* TCP_CALCULATE_EFF_SEND_MSS */
1515195972f6Sopenharmony_ci    mss = TCP_MSS;
1516195972f6Sopenharmony_ci#endif /* TCP_CALCULATE_EFF_SEND_MSS */
1517195972f6Sopenharmony_ci    *opts = TCP_BUILD_MSS_OPTION(mss);
1518195972f6Sopenharmony_ci    opts += 1;
1519195972f6Sopenharmony_ci  }
1520195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
1521195972f6Sopenharmony_ci  pcb->ts_lastacksent = pcb->rcv_nxt;
1522195972f6Sopenharmony_ci
1523195972f6Sopenharmony_ci  if (seg->flags & TF_SEG_OPTS_TS) {
1524195972f6Sopenharmony_ci    tcp_build_timestamp_option(pcb, opts);
1525195972f6Sopenharmony_ci    opts += 3;
1526195972f6Sopenharmony_ci  }
1527195972f6Sopenharmony_ci#endif
1528195972f6Sopenharmony_ci#if LWIP_WND_SCALE
1529195972f6Sopenharmony_ci  if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
1530195972f6Sopenharmony_ci    tcp_build_wnd_scale_option(opts);
1531195972f6Sopenharmony_ci    opts += 1;
1532195972f6Sopenharmony_ci  }
1533195972f6Sopenharmony_ci#endif
1534195972f6Sopenharmony_ci#if LWIP_TCP_SACK_OUT
1535195972f6Sopenharmony_ci  if (seg->flags & TF_SEG_OPTS_SACK_PERM) {
1536195972f6Sopenharmony_ci    /* Pad with two NOP options to make everything nicely aligned
1537195972f6Sopenharmony_ci     * NOTE: When we send both timestamp and SACK_PERM options,
1538195972f6Sopenharmony_ci     * we could use the first two NOPs before the timestamp to store SACK_PERM option,
1539195972f6Sopenharmony_ci     * but that would complicate the code.
1540195972f6Sopenharmony_ci     */
1541195972f6Sopenharmony_ci    *(opts++) = PP_HTONL(0x01010402);
1542195972f6Sopenharmony_ci  }
1543195972f6Sopenharmony_ci#endif
1544195972f6Sopenharmony_ci
1545195972f6Sopenharmony_ci  /* Set retransmission timer running if it is not currently enabled
1546195972f6Sopenharmony_ci     This must be set before checking the route. */
1547195972f6Sopenharmony_ci  if (pcb->rtime < 0) {
1548195972f6Sopenharmony_ci    pcb->rtime = 0;
1549195972f6Sopenharmony_ci  }
1550195972f6Sopenharmony_ci
1551195972f6Sopenharmony_ci  if (pcb->rttest == 0) {
1552195972f6Sopenharmony_ci    pcb->rttest = tcp_ticks;
1553195972f6Sopenharmony_ci    pcb->rtseq = lwip_ntohl(seg->tcphdr->seqno);
1554195972f6Sopenharmony_ci
1555195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
1556195972f6Sopenharmony_ci  }
1557195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
1558195972f6Sopenharmony_ci                                 lwip_htonl(seg->tcphdr->seqno), lwip_htonl(seg->tcphdr->seqno) +
1559195972f6Sopenharmony_ci                                 seg->len));
1560195972f6Sopenharmony_ci
1561195972f6Sopenharmony_ci  len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
1562195972f6Sopenharmony_ci  if (len == 0) {
1563195972f6Sopenharmony_ci    /** Exclude retransmitted segments from this count. */
1564195972f6Sopenharmony_ci    MIB2_STATS_INC(mib2.tcpoutsegs);
1565195972f6Sopenharmony_ci  }
1566195972f6Sopenharmony_ci
1567195972f6Sopenharmony_ci  seg->p->len -= len;
1568195972f6Sopenharmony_ci  seg->p->tot_len -= len;
1569195972f6Sopenharmony_ci
1570195972f6Sopenharmony_ci  seg->p->payload = seg->tcphdr;
1571195972f6Sopenharmony_ci
1572195972f6Sopenharmony_ci  seg->tcphdr->chksum = 0;
1573195972f6Sopenharmony_ci
1574195972f6Sopenharmony_ci#ifdef LWIP_HOOK_TCP_OUT_ADD_TCPOPTS
1575195972f6Sopenharmony_ci  opts = LWIP_HOOK_TCP_OUT_ADD_TCPOPTS(seg->p, seg->tcphdr, pcb, opts);
1576195972f6Sopenharmony_ci#endif
1577195972f6Sopenharmony_ci  LWIP_ASSERT("options not filled", (u8_t *)opts == ((u8_t *)(seg->tcphdr + 1)) + LWIP_TCP_OPT_LENGTH_SEGMENT(seg->flags, pcb));
1578195972f6Sopenharmony_ci
1579195972f6Sopenharmony_ci#if CHECKSUM_GEN_TCP
1580195972f6Sopenharmony_ci  IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
1581195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
1582195972f6Sopenharmony_ci    u32_t acc;
1583195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
1584195972f6Sopenharmony_ci    u16_t chksum_slow = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
1585195972f6Sopenharmony_ci                                         seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
1586195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
1587195972f6Sopenharmony_ci    if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
1588195972f6Sopenharmony_ci      LWIP_ASSERT("data included but not checksummed",
1589195972f6Sopenharmony_ci                  seg->p->tot_len == TCPH_HDRLEN_BYTES(seg->tcphdr));
1590195972f6Sopenharmony_ci    }
1591195972f6Sopenharmony_ci
1592195972f6Sopenharmony_ci    /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
1593195972f6Sopenharmony_ci    acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP,
1594195972f6Sopenharmony_ci                                   seg->p->tot_len, TCPH_HDRLEN_BYTES(seg->tcphdr), &pcb->local_ip, &pcb->remote_ip);
1595195972f6Sopenharmony_ci    /* add payload checksum */
1596195972f6Sopenharmony_ci    if (seg->chksum_swapped) {
1597195972f6Sopenharmony_ci      seg_chksum_was_swapped = 1;
1598195972f6Sopenharmony_ci      seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
1599195972f6Sopenharmony_ci      seg->chksum_swapped = 0;
1600195972f6Sopenharmony_ci    }
1601195972f6Sopenharmony_ci    acc = (u16_t)~acc + seg->chksum;
1602195972f6Sopenharmony_ci    seg->tcphdr->chksum = (u16_t)~FOLD_U32T(acc);
1603195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
1604195972f6Sopenharmony_ci    if (chksum_slow != seg->tcphdr->chksum) {
1605195972f6Sopenharmony_ci      TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(
1606195972f6Sopenharmony_ci        ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
1607195972f6Sopenharmony_ci         seg->tcphdr->chksum, chksum_slow));
1608195972f6Sopenharmony_ci      seg->tcphdr->chksum = chksum_slow;
1609195972f6Sopenharmony_ci    }
1610195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
1611195972f6Sopenharmony_ci#else /* TCP_CHECKSUM_ON_COPY */
1612195972f6Sopenharmony_ci    seg->tcphdr->chksum = ip_chksum_pseudo(seg->p, IP_PROTO_TCP,
1613195972f6Sopenharmony_ci                                           seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
1614195972f6Sopenharmony_ci#endif /* TCP_CHECKSUM_ON_COPY */
1615195972f6Sopenharmony_ci  }
1616195972f6Sopenharmony_ci#endif /* CHECKSUM_GEN_TCP */
1617195972f6Sopenharmony_ci  TCP_STATS_INC(tcp.xmit);
1618195972f6Sopenharmony_ci
1619195972f6Sopenharmony_ci  NETIF_SET_HINTS(netif, &(pcb->netif_hints));
1620195972f6Sopenharmony_ci  err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
1621195972f6Sopenharmony_ci                     pcb->tos, IP_PROTO_TCP, netif);
1622195972f6Sopenharmony_ci  NETIF_RESET_HINTS(netif);
1623195972f6Sopenharmony_ci
1624195972f6Sopenharmony_ci#if TCP_CHECKSUM_ON_COPY
1625195972f6Sopenharmony_ci  if (seg_chksum_was_swapped) {
1626195972f6Sopenharmony_ci    /* if data is added to this segment later, chksum needs to be swapped,
1627195972f6Sopenharmony_ci       so restore this now */
1628195972f6Sopenharmony_ci    seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
1629195972f6Sopenharmony_ci    seg->chksum_swapped = 1;
1630195972f6Sopenharmony_ci  }
1631195972f6Sopenharmony_ci#endif
1632195972f6Sopenharmony_ci
1633195972f6Sopenharmony_ci  return err;
1634195972f6Sopenharmony_ci}
1635195972f6Sopenharmony_ci
1636195972f6Sopenharmony_ci/**
1637195972f6Sopenharmony_ci * Requeue all unacked segments for retransmission
1638195972f6Sopenharmony_ci *
1639195972f6Sopenharmony_ci * Called by tcp_slowtmr() for slow retransmission.
1640195972f6Sopenharmony_ci *
1641195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
1642195972f6Sopenharmony_ci */
1643195972f6Sopenharmony_cierr_t
1644195972f6Sopenharmony_citcp_rexmit_rto_prepare(struct tcp_pcb *pcb)
1645195972f6Sopenharmony_ci{
1646195972f6Sopenharmony_ci  struct tcp_seg *seg;
1647195972f6Sopenharmony_ci
1648195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rexmit_rto_prepare: invalid pcb", pcb != NULL);
1649195972f6Sopenharmony_ci
1650195972f6Sopenharmony_ci  if (pcb->unacked == NULL) {
1651195972f6Sopenharmony_ci    return ERR_VAL;
1652195972f6Sopenharmony_ci  }
1653195972f6Sopenharmony_ci
1654195972f6Sopenharmony_ci  /* Move all unacked segments to the head of the unsent queue.
1655195972f6Sopenharmony_ci     However, give up if any of the unsent pbufs are still referenced by the
1656195972f6Sopenharmony_ci     netif driver due to deferred transmission. No point loading the link further
1657195972f6Sopenharmony_ci     if it is struggling to flush its buffered writes. */
1658195972f6Sopenharmony_ci  for (seg = pcb->unacked; seg->next != NULL; seg = seg->next) {
1659195972f6Sopenharmony_ci    if (tcp_output_segment_busy(seg)) {
1660195972f6Sopenharmony_ci      LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit_rto: segment busy\n"));
1661195972f6Sopenharmony_ci      return ERR_VAL;
1662195972f6Sopenharmony_ci    }
1663195972f6Sopenharmony_ci  }
1664195972f6Sopenharmony_ci  if (tcp_output_segment_busy(seg)) {
1665195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit_rto: segment busy\n"));
1666195972f6Sopenharmony_ci    return ERR_VAL;
1667195972f6Sopenharmony_ci  }
1668195972f6Sopenharmony_ci  /* concatenate unsent queue after unacked queue */
1669195972f6Sopenharmony_ci  seg->next = pcb->unsent;
1670195972f6Sopenharmony_ci#if TCP_OVERSIZE_DBGCHECK
1671195972f6Sopenharmony_ci  /* if last unsent changed, we need to update unsent_oversize */
1672195972f6Sopenharmony_ci  if (pcb->unsent == NULL) {
1673195972f6Sopenharmony_ci    pcb->unsent_oversize = seg->oversize_left;
1674195972f6Sopenharmony_ci  }
1675195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE_DBGCHECK */
1676195972f6Sopenharmony_ci  /* unsent queue is the concatenated queue (of unacked, unsent) */
1677195972f6Sopenharmony_ci  pcb->unsent = pcb->unacked;
1678195972f6Sopenharmony_ci  /* unacked queue is now empty */
1679195972f6Sopenharmony_ci  pcb->unacked = NULL;
1680195972f6Sopenharmony_ci
1681195972f6Sopenharmony_ci  /* Mark RTO in-progress */
1682195972f6Sopenharmony_ci  tcp_set_flags(pcb, TF_RTO);
1683195972f6Sopenharmony_ci  /* Record the next byte following retransmit */
1684195972f6Sopenharmony_ci  pcb->rto_end = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
1685195972f6Sopenharmony_ci  /* Don't take any RTT measurements after retransmitting. */
1686195972f6Sopenharmony_ci  pcb->rttest = 0;
1687195972f6Sopenharmony_ci
1688195972f6Sopenharmony_ci  return ERR_OK;
1689195972f6Sopenharmony_ci}
1690195972f6Sopenharmony_ci
1691195972f6Sopenharmony_ci/**
1692195972f6Sopenharmony_ci * Requeue all unacked segments for retransmission
1693195972f6Sopenharmony_ci *
1694195972f6Sopenharmony_ci * Called by tcp_slowtmr() for slow retransmission.
1695195972f6Sopenharmony_ci *
1696195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
1697195972f6Sopenharmony_ci */
1698195972f6Sopenharmony_civoid
1699195972f6Sopenharmony_citcp_rexmit_rto_commit(struct tcp_pcb *pcb)
1700195972f6Sopenharmony_ci{
1701195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rexmit_rto_commit: invalid pcb", pcb != NULL);
1702195972f6Sopenharmony_ci
1703195972f6Sopenharmony_ci  /* increment number of retransmissions */
1704195972f6Sopenharmony_ci  if (pcb->nrtx < 0xFF) {
1705195972f6Sopenharmony_ci    ++pcb->nrtx;
1706195972f6Sopenharmony_ci  }
1707195972f6Sopenharmony_ci  /* Do the actual retransmission */
1708195972f6Sopenharmony_ci  tcp_output(pcb);
1709195972f6Sopenharmony_ci}
1710195972f6Sopenharmony_ci
1711195972f6Sopenharmony_ci/**
1712195972f6Sopenharmony_ci * Requeue all unacked segments for retransmission
1713195972f6Sopenharmony_ci *
1714195972f6Sopenharmony_ci * Called by tcp_process() only, tcp_slowtmr() needs to do some things between
1715195972f6Sopenharmony_ci * "prepare" and "commit".
1716195972f6Sopenharmony_ci *
1717195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
1718195972f6Sopenharmony_ci */
1719195972f6Sopenharmony_civoid
1720195972f6Sopenharmony_citcp_rexmit_rto(struct tcp_pcb *pcb)
1721195972f6Sopenharmony_ci{
1722195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rexmit_rto: invalid pcb", pcb != NULL);
1723195972f6Sopenharmony_ci
1724195972f6Sopenharmony_ci  if (tcp_rexmit_rto_prepare(pcb) == ERR_OK) {
1725195972f6Sopenharmony_ci    tcp_rexmit_rto_commit(pcb);
1726195972f6Sopenharmony_ci  }
1727195972f6Sopenharmony_ci}
1728195972f6Sopenharmony_ci
1729195972f6Sopenharmony_ci/**
1730195972f6Sopenharmony_ci * Requeue the first unacked segment for retransmission
1731195972f6Sopenharmony_ci *
1732195972f6Sopenharmony_ci * Called by tcp_receive() for fast retransmit.
1733195972f6Sopenharmony_ci *
1734195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to retransmit the first unacked segment
1735195972f6Sopenharmony_ci */
1736195972f6Sopenharmony_cierr_t
1737195972f6Sopenharmony_citcp_rexmit(struct tcp_pcb *pcb)
1738195972f6Sopenharmony_ci{
1739195972f6Sopenharmony_ci  struct tcp_seg *seg;
1740195972f6Sopenharmony_ci  struct tcp_seg **cur_seg;
1741195972f6Sopenharmony_ci
1742195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rexmit: invalid pcb", pcb != NULL);
1743195972f6Sopenharmony_ci
1744195972f6Sopenharmony_ci  if (pcb->unacked == NULL) {
1745195972f6Sopenharmony_ci    return ERR_VAL;
1746195972f6Sopenharmony_ci  }
1747195972f6Sopenharmony_ci
1748195972f6Sopenharmony_ci  seg = pcb->unacked;
1749195972f6Sopenharmony_ci
1750195972f6Sopenharmony_ci  /* Give up if the segment is still referenced by the netif driver
1751195972f6Sopenharmony_ci     due to deferred transmission. */
1752195972f6Sopenharmony_ci  if (tcp_output_segment_busy(seg)) {
1753195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit busy\n"));
1754195972f6Sopenharmony_ci    return ERR_VAL;
1755195972f6Sopenharmony_ci  }
1756195972f6Sopenharmony_ci
1757195972f6Sopenharmony_ci  /* Move the first unacked segment to the unsent queue */
1758195972f6Sopenharmony_ci  /* Keep the unsent queue sorted. */
1759195972f6Sopenharmony_ci  pcb->unacked = seg->next;
1760195972f6Sopenharmony_ci
1761195972f6Sopenharmony_ci  cur_seg = &(pcb->unsent);
1762195972f6Sopenharmony_ci  while (*cur_seg &&
1763195972f6Sopenharmony_ci         TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
1764195972f6Sopenharmony_ci    cur_seg = &((*cur_seg)->next );
1765195972f6Sopenharmony_ci  }
1766195972f6Sopenharmony_ci  seg->next = *cur_seg;
1767195972f6Sopenharmony_ci  *cur_seg = seg;
1768195972f6Sopenharmony_ci#if TCP_OVERSIZE
1769195972f6Sopenharmony_ci  if (seg->next == NULL) {
1770195972f6Sopenharmony_ci    /* the retransmitted segment is last in unsent, so reset unsent_oversize */
1771195972f6Sopenharmony_ci    pcb->unsent_oversize = 0;
1772195972f6Sopenharmony_ci  }
1773195972f6Sopenharmony_ci#endif /* TCP_OVERSIZE */
1774195972f6Sopenharmony_ci
1775195972f6Sopenharmony_ci  if (pcb->nrtx < 0xFF) {
1776195972f6Sopenharmony_ci    ++pcb->nrtx;
1777195972f6Sopenharmony_ci  }
1778195972f6Sopenharmony_ci
1779195972f6Sopenharmony_ci  /* Don't take any rtt measurements after retransmitting. */
1780195972f6Sopenharmony_ci  pcb->rttest = 0;
1781195972f6Sopenharmony_ci
1782195972f6Sopenharmony_ci  /* Do the actual retransmission. */
1783195972f6Sopenharmony_ci  MIB2_STATS_INC(mib2.tcpretranssegs);
1784195972f6Sopenharmony_ci  /* No need to call tcp_output: we are always called from tcp_input()
1785195972f6Sopenharmony_ci     and thus tcp_output directly returns. */
1786195972f6Sopenharmony_ci  return ERR_OK;
1787195972f6Sopenharmony_ci}
1788195972f6Sopenharmony_ci
1789195972f6Sopenharmony_ci
1790195972f6Sopenharmony_ci/**
1791195972f6Sopenharmony_ci * Handle retransmission after three dupacks received
1792195972f6Sopenharmony_ci *
1793195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to retransmit the first unacked segment
1794195972f6Sopenharmony_ci */
1795195972f6Sopenharmony_civoid
1796195972f6Sopenharmony_citcp_rexmit_fast(struct tcp_pcb *pcb)
1797195972f6Sopenharmony_ci{
1798195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rexmit_fast: invalid pcb", pcb != NULL);
1799195972f6Sopenharmony_ci
1800195972f6Sopenharmony_ci  if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
1801195972f6Sopenharmony_ci    /* This is fast retransmit. Retransmit the first unacked segment. */
1802195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_FR_DEBUG,
1803195972f6Sopenharmony_ci                ("tcp_receive: dupacks %"U16_F" (%"U32_F
1804195972f6Sopenharmony_ci                 "), fast retransmit %"U32_F"\n",
1805195972f6Sopenharmony_ci                 (u16_t)pcb->dupacks, pcb->lastack,
1806195972f6Sopenharmony_ci                 lwip_ntohl(pcb->unacked->tcphdr->seqno)));
1807195972f6Sopenharmony_ci    if (tcp_rexmit(pcb) == ERR_OK) {
1808195972f6Sopenharmony_ci      /* Set ssthresh to half of the minimum of the current
1809195972f6Sopenharmony_ci       * cwnd and the advertised window */
1810195972f6Sopenharmony_ci      pcb->ssthresh = LWIP_MIN(pcb->cwnd, pcb->snd_wnd) / 2;
1811195972f6Sopenharmony_ci
1812195972f6Sopenharmony_ci      /* The minimum value for ssthresh should be 2 MSS */
1813195972f6Sopenharmony_ci      if (pcb->ssthresh < (2U * pcb->mss)) {
1814195972f6Sopenharmony_ci        LWIP_DEBUGF(TCP_FR_DEBUG,
1815195972f6Sopenharmony_ci                    ("tcp_receive: The minimum value for ssthresh %"TCPWNDSIZE_F
1816195972f6Sopenharmony_ci                     " should be min 2 mss %"U16_F"...\n",
1817195972f6Sopenharmony_ci                     pcb->ssthresh, (u16_t)(2 * pcb->mss)));
1818195972f6Sopenharmony_ci        pcb->ssthresh = 2 * pcb->mss;
1819195972f6Sopenharmony_ci      }
1820195972f6Sopenharmony_ci
1821195972f6Sopenharmony_ci      pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
1822195972f6Sopenharmony_ci      tcp_set_flags(pcb, TF_INFR);
1823195972f6Sopenharmony_ci
1824195972f6Sopenharmony_ci      /* Reset the retransmission timer to prevent immediate rto retransmissions */
1825195972f6Sopenharmony_ci      pcb->rtime = 0;
1826195972f6Sopenharmony_ci    }
1827195972f6Sopenharmony_ci  }
1828195972f6Sopenharmony_ci}
1829195972f6Sopenharmony_ci
1830195972f6Sopenharmony_cistatic struct pbuf *
1831195972f6Sopenharmony_citcp_output_alloc_header_common(u32_t ackno, u16_t optlen, u16_t datalen,
1832195972f6Sopenharmony_ci                        u32_t seqno_be /* already in network byte order */,
1833195972f6Sopenharmony_ci                        u16_t src_port, u16_t dst_port, u8_t flags, u16_t wnd)
1834195972f6Sopenharmony_ci{
1835195972f6Sopenharmony_ci  struct tcp_hdr *tcphdr;
1836195972f6Sopenharmony_ci  struct pbuf *p;
1837195972f6Sopenharmony_ci
1838195972f6Sopenharmony_ci  p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
1839195972f6Sopenharmony_ci  if (p != NULL) {
1840195972f6Sopenharmony_ci    LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
1841195972f6Sopenharmony_ci                (p->len >= TCP_HLEN + optlen));
1842195972f6Sopenharmony_ci    tcphdr = (struct tcp_hdr *)p->payload;
1843195972f6Sopenharmony_ci    tcphdr->src = lwip_htons(src_port);
1844195972f6Sopenharmony_ci    tcphdr->dest = lwip_htons(dst_port);
1845195972f6Sopenharmony_ci    tcphdr->seqno = seqno_be;
1846195972f6Sopenharmony_ci    tcphdr->ackno = lwip_htonl(ackno);
1847195972f6Sopenharmony_ci    TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), flags);
1848195972f6Sopenharmony_ci    tcphdr->wnd = lwip_htons(wnd);
1849195972f6Sopenharmony_ci    tcphdr->chksum = 0;
1850195972f6Sopenharmony_ci    tcphdr->urgp = 0;
1851195972f6Sopenharmony_ci  }
1852195972f6Sopenharmony_ci  return p;
1853195972f6Sopenharmony_ci}
1854195972f6Sopenharmony_ci
1855195972f6Sopenharmony_ci/** Allocate a pbuf and create a tcphdr at p->payload, used for output
1856195972f6Sopenharmony_ci * functions other than the default tcp_output -> tcp_output_segment
1857195972f6Sopenharmony_ci * (e.g. tcp_send_empty_ack, etc.)
1858195972f6Sopenharmony_ci *
1859195972f6Sopenharmony_ci * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
1860195972f6Sopenharmony_ci * @param optlen length of header-options
1861195972f6Sopenharmony_ci * @param datalen length of tcp data to reserve in pbuf
1862195972f6Sopenharmony_ci * @param seqno_be seqno in network byte order (big-endian)
1863195972f6Sopenharmony_ci * @return pbuf with p->payload being the tcp_hdr
1864195972f6Sopenharmony_ci */
1865195972f6Sopenharmony_cistatic struct pbuf *
1866195972f6Sopenharmony_citcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
1867195972f6Sopenharmony_ci                        u32_t seqno_be /* already in network byte order */)
1868195972f6Sopenharmony_ci{
1869195972f6Sopenharmony_ci  struct pbuf *p;
1870195972f6Sopenharmony_ci
1871195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_alloc_header: invalid pcb", pcb != NULL);
1872195972f6Sopenharmony_ci
1873195972f6Sopenharmony_ci  p = tcp_output_alloc_header_common(pcb->rcv_nxt, optlen, datalen,
1874195972f6Sopenharmony_ci    seqno_be, pcb->local_port, pcb->remote_port, TCP_ACK,
1875195972f6Sopenharmony_ci    TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
1876195972f6Sopenharmony_ci  if (p != NULL) {
1877195972f6Sopenharmony_ci    /* If we're sending a packet, update the announced right window edge */
1878195972f6Sopenharmony_ci    pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
1879195972f6Sopenharmony_ci  }
1880195972f6Sopenharmony_ci  return p;
1881195972f6Sopenharmony_ci}
1882195972f6Sopenharmony_ci
1883195972f6Sopenharmony_ci/* Fill in options for control segments */
1884195972f6Sopenharmony_cistatic void
1885195972f6Sopenharmony_citcp_output_fill_options(const struct tcp_pcb *pcb, struct pbuf *p, u8_t optflags, u8_t num_sacks)
1886195972f6Sopenharmony_ci{
1887195972f6Sopenharmony_ci  struct tcp_hdr *tcphdr;
1888195972f6Sopenharmony_ci  u32_t *opts;
1889195972f6Sopenharmony_ci  u16_t sacks_len = 0;
1890195972f6Sopenharmony_ci
1891195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_fill_options: invalid pbuf", p != NULL);
1892195972f6Sopenharmony_ci
1893195972f6Sopenharmony_ci  tcphdr = (struct tcp_hdr *)p->payload;
1894195972f6Sopenharmony_ci  opts = (u32_t *)(void *)(tcphdr + 1);
1895195972f6Sopenharmony_ci
1896195972f6Sopenharmony_ci  /* NB. MSS and window scale options are only sent on SYNs, so ignore them here */
1897195972f6Sopenharmony_ci
1898195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
1899195972f6Sopenharmony_ci  if (optflags & TF_SEG_OPTS_TS) {
1900195972f6Sopenharmony_ci    tcp_build_timestamp_option(pcb, opts);
1901195972f6Sopenharmony_ci    opts += 3;
1902195972f6Sopenharmony_ci  }
1903195972f6Sopenharmony_ci#endif
1904195972f6Sopenharmony_ci
1905195972f6Sopenharmony_ci#if LWIP_TCP_SACK_OUT
1906195972f6Sopenharmony_ci  if (pcb && (num_sacks > 0)) {
1907195972f6Sopenharmony_ci    tcp_build_sack_option(pcb, opts, num_sacks);
1908195972f6Sopenharmony_ci    /* 1 word for SACKs header (including 2xNOP), and 2 words for each SACK */
1909195972f6Sopenharmony_ci    sacks_len = 1 + num_sacks * 2;
1910195972f6Sopenharmony_ci    opts += sacks_len;
1911195972f6Sopenharmony_ci  }
1912195972f6Sopenharmony_ci#else
1913195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(num_sacks);
1914195972f6Sopenharmony_ci#endif
1915195972f6Sopenharmony_ci
1916195972f6Sopenharmony_ci#ifdef LWIP_HOOK_TCP_OUT_ADD_TCPOPTS
1917195972f6Sopenharmony_ci  opts = LWIP_HOOK_TCP_OUT_ADD_TCPOPTS(p, tcphdr, pcb, opts);
1918195972f6Sopenharmony_ci#endif
1919195972f6Sopenharmony_ci
1920195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(pcb);
1921195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(sacks_len);
1922195972f6Sopenharmony_ci  LWIP_ASSERT("options not filled", (u8_t *)opts == ((u8_t *)(tcphdr + 1)) + sacks_len * 4 + LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb));
1923195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(optflags); /* for LWIP_NOASSERT */
1924195972f6Sopenharmony_ci  LWIP_UNUSED_ARG(opts); /* for LWIP_NOASSERT */
1925195972f6Sopenharmony_ci}
1926195972f6Sopenharmony_ci
1927195972f6Sopenharmony_ci/** Output a control segment pbuf to IP.
1928195972f6Sopenharmony_ci *
1929195972f6Sopenharmony_ci * Called from tcp_rst, tcp_send_empty_ack, tcp_keepalive and tcp_zero_window_probe,
1930195972f6Sopenharmony_ci * this function combines selecting a netif for transmission, generating the tcp
1931195972f6Sopenharmony_ci * header checksum and calling ip_output_if while handling netif hints and stats.
1932195972f6Sopenharmony_ci */
1933195972f6Sopenharmony_cistatic err_t
1934195972f6Sopenharmony_citcp_output_control_segment(const struct tcp_pcb *pcb, struct pbuf *p,
1935195972f6Sopenharmony_ci                           const ip_addr_t *src, const ip_addr_t *dst)
1936195972f6Sopenharmony_ci{
1937195972f6Sopenharmony_ci  err_t err;
1938195972f6Sopenharmony_ci  struct netif *netif;
1939195972f6Sopenharmony_ci
1940195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_output_control_segment: invalid pbuf", p != NULL);
1941195972f6Sopenharmony_ci
1942195972f6Sopenharmony_ci  netif = tcp_route(pcb, src, dst);
1943195972f6Sopenharmony_ci  if (netif == NULL) {
1944195972f6Sopenharmony_ci    err = ERR_RTE;
1945195972f6Sopenharmony_ci  } else {
1946195972f6Sopenharmony_ci    u8_t ttl, tos;
1947195972f6Sopenharmony_ci#if CHECKSUM_GEN_TCP
1948195972f6Sopenharmony_ci    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_TCP) {
1949195972f6Sopenharmony_ci      struct tcp_hdr *tcphdr = (struct tcp_hdr *)p->payload;
1950195972f6Sopenharmony_ci      tcphdr->chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
1951195972f6Sopenharmony_ci                                        src, dst);
1952195972f6Sopenharmony_ci    }
1953195972f6Sopenharmony_ci#endif
1954195972f6Sopenharmony_ci    if (pcb != NULL) {
1955195972f6Sopenharmony_ci      NETIF_SET_HINTS(netif, LWIP_CONST_CAST(struct netif_hint*, &(pcb->netif_hints)));
1956195972f6Sopenharmony_ci      ttl = pcb->ttl;
1957195972f6Sopenharmony_ci      tos = pcb->tos;
1958195972f6Sopenharmony_ci    } else {
1959195972f6Sopenharmony_ci      /* Send output with hardcoded TTL/HL since we have no access to the pcb */
1960195972f6Sopenharmony_ci      ttl = TCP_TTL;
1961195972f6Sopenharmony_ci      tos = 0;
1962195972f6Sopenharmony_ci    }
1963195972f6Sopenharmony_ci    TCP_STATS_INC(tcp.xmit);
1964195972f6Sopenharmony_ci    err = ip_output_if(p, src, dst, ttl, tos, IP_PROTO_TCP, netif);
1965195972f6Sopenharmony_ci    NETIF_RESET_HINTS(netif);
1966195972f6Sopenharmony_ci  }
1967195972f6Sopenharmony_ci  pbuf_free(p);
1968195972f6Sopenharmony_ci  return err;
1969195972f6Sopenharmony_ci}
1970195972f6Sopenharmony_ci
1971195972f6Sopenharmony_ci/**
1972195972f6Sopenharmony_ci * Send a TCP RESET packet (empty segment with RST flag set) either to
1973195972f6Sopenharmony_ci * abort a connection or to show that there is no matching local connection
1974195972f6Sopenharmony_ci * for a received segment.
1975195972f6Sopenharmony_ci *
1976195972f6Sopenharmony_ci * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
1977195972f6Sopenharmony_ci * matching local pcb was found), tcp_listen_input() (if incoming segment
1978195972f6Sopenharmony_ci * has ACK flag set) and tcp_process() (received segment in the wrong state)
1979195972f6Sopenharmony_ci *
1980195972f6Sopenharmony_ci * Since a RST segment is in most cases not sent for an active connection,
1981195972f6Sopenharmony_ci * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
1982195972f6Sopenharmony_ci * most other segment output functions.
1983195972f6Sopenharmony_ci *
1984195972f6Sopenharmony_ci * @param pcb TCP pcb (may be NULL if no pcb is available)
1985195972f6Sopenharmony_ci * @param seqno the sequence number to use for the outgoing segment
1986195972f6Sopenharmony_ci * @param ackno the acknowledge number to use for the outgoing segment
1987195972f6Sopenharmony_ci * @param local_ip the local IP address to send the segment from
1988195972f6Sopenharmony_ci * @param remote_ip the remote IP address to send the segment to
1989195972f6Sopenharmony_ci * @param local_port the local TCP port to send the segment from
1990195972f6Sopenharmony_ci * @param remote_port the remote TCP port to send the segment to
1991195972f6Sopenharmony_ci */
1992195972f6Sopenharmony_civoid
1993195972f6Sopenharmony_citcp_rst(const struct tcp_pcb *pcb, u32_t seqno, u32_t ackno,
1994195972f6Sopenharmony_ci        const ip_addr_t *local_ip, const ip_addr_t *remote_ip,
1995195972f6Sopenharmony_ci        u16_t local_port, u16_t remote_port)
1996195972f6Sopenharmony_ci{
1997195972f6Sopenharmony_ci  struct pbuf *p;
1998195972f6Sopenharmony_ci  u16_t wnd;
1999195972f6Sopenharmony_ci  u8_t optlen;
2000195972f6Sopenharmony_ci
2001195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rst: invalid local_ip", local_ip != NULL);
2002195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_rst: invalid remote_ip", remote_ip != NULL);
2003195972f6Sopenharmony_ci
2004195972f6Sopenharmony_ci  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
2005195972f6Sopenharmony_ci
2006195972f6Sopenharmony_ci#if LWIP_WND_SCALE
2007195972f6Sopenharmony_ci  wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF));
2008195972f6Sopenharmony_ci#else
2009195972f6Sopenharmony_ci  wnd = PP_HTONS(TCP_WND);
2010195972f6Sopenharmony_ci#endif
2011195972f6Sopenharmony_ci
2012195972f6Sopenharmony_ci  p = tcp_output_alloc_header_common(ackno, optlen, 0, lwip_htonl(seqno), local_port,
2013195972f6Sopenharmony_ci    remote_port, TCP_RST | TCP_ACK, wnd);
2014195972f6Sopenharmony_ci  if (p == NULL) {
2015195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
2016195972f6Sopenharmony_ci    return;
2017195972f6Sopenharmony_ci  }
2018195972f6Sopenharmony_ci  tcp_output_fill_options(pcb, p, 0, 0);
2019195972f6Sopenharmony_ci
2020195972f6Sopenharmony_ci  MIB2_STATS_INC(mib2.tcpoutrsts);
2021195972f6Sopenharmony_ci
2022195972f6Sopenharmony_ci  tcp_output_control_segment(pcb, p, local_ip, remote_ip);
2023195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
2024195972f6Sopenharmony_ci}
2025195972f6Sopenharmony_ci
2026195972f6Sopenharmony_ci/**
2027195972f6Sopenharmony_ci * Send an ACK without data.
2028195972f6Sopenharmony_ci *
2029195972f6Sopenharmony_ci * @param pcb Protocol control block for the TCP connection to send the ACK
2030195972f6Sopenharmony_ci */
2031195972f6Sopenharmony_cierr_t
2032195972f6Sopenharmony_citcp_send_empty_ack(struct tcp_pcb *pcb)
2033195972f6Sopenharmony_ci{
2034195972f6Sopenharmony_ci  err_t err;
2035195972f6Sopenharmony_ci  struct pbuf *p;
2036195972f6Sopenharmony_ci  u8_t optlen, optflags = 0;
2037195972f6Sopenharmony_ci  u8_t num_sacks = 0;
2038195972f6Sopenharmony_ci
2039195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_send_empty_ack: invalid pcb", pcb != NULL);
2040195972f6Sopenharmony_ci
2041195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
2042195972f6Sopenharmony_ci  if (pcb->flags & TF_TIMESTAMP) {
2043195972f6Sopenharmony_ci    optflags = TF_SEG_OPTS_TS;
2044195972f6Sopenharmony_ci  }
2045195972f6Sopenharmony_ci#endif
2046195972f6Sopenharmony_ci  optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(optflags, pcb);
2047195972f6Sopenharmony_ci
2048195972f6Sopenharmony_ci#if LWIP_TCP_SACK_OUT
2049195972f6Sopenharmony_ci  /* For now, SACKs are only sent with empty ACKs */
2050195972f6Sopenharmony_ci  if ((num_sacks = tcp_get_num_sacks(pcb, optlen)) > 0) {
2051195972f6Sopenharmony_ci    optlen += 4 + num_sacks * 8; /* 4 bytes for header (including 2*NOP), plus 8B for each SACK */
2052195972f6Sopenharmony_ci  }
2053195972f6Sopenharmony_ci#endif
2054195972f6Sopenharmony_ci
2055195972f6Sopenharmony_ci  p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt));
2056195972f6Sopenharmony_ci  if (p == NULL) {
2057195972f6Sopenharmony_ci    /* let tcp_fasttmr retry sending this ACK */
2058195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
2059195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
2060195972f6Sopenharmony_ci    return ERR_BUF;
2061195972f6Sopenharmony_ci  }
2062195972f6Sopenharmony_ci  tcp_output_fill_options(pcb, p, optflags, num_sacks);
2063195972f6Sopenharmony_ci
2064195972f6Sopenharmony_ci#if LWIP_TCP_TIMESTAMPS
2065195972f6Sopenharmony_ci  pcb->ts_lastacksent = pcb->rcv_nxt;
2066195972f6Sopenharmony_ci#endif
2067195972f6Sopenharmony_ci
2068195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
2069195972f6Sopenharmony_ci              ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
2070195972f6Sopenharmony_ci  err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
2071195972f6Sopenharmony_ci  if (err != ERR_OK) {
2072195972f6Sopenharmony_ci    /* let tcp_fasttmr retry sending this ACK */
2073195972f6Sopenharmony_ci    tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
2074195972f6Sopenharmony_ci  } else {
2075195972f6Sopenharmony_ci    /* remove ACK flags from the PCB, as we sent an empty ACK now */
2076195972f6Sopenharmony_ci    tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
2077195972f6Sopenharmony_ci  }
2078195972f6Sopenharmony_ci
2079195972f6Sopenharmony_ci  return err;
2080195972f6Sopenharmony_ci}
2081195972f6Sopenharmony_ci
2082195972f6Sopenharmony_ci/**
2083195972f6Sopenharmony_ci * Send keepalive packets to keep a connection active although
2084195972f6Sopenharmony_ci * no data is sent over it.
2085195972f6Sopenharmony_ci *
2086195972f6Sopenharmony_ci * Called by tcp_slowtmr()
2087195972f6Sopenharmony_ci *
2088195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to send a keepalive packet
2089195972f6Sopenharmony_ci */
2090195972f6Sopenharmony_cierr_t
2091195972f6Sopenharmony_citcp_keepalive(struct tcp_pcb *pcb)
2092195972f6Sopenharmony_ci{
2093195972f6Sopenharmony_ci  err_t err;
2094195972f6Sopenharmony_ci  struct pbuf *p;
2095195972f6Sopenharmony_ci  u8_t optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
2096195972f6Sopenharmony_ci
2097195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_keepalive: invalid pcb", pcb != NULL);
2098195972f6Sopenharmony_ci
2099195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
2100195972f6Sopenharmony_ci  ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
2101195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
2102195972f6Sopenharmony_ci
2103195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F"   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
2104195972f6Sopenharmony_ci                          tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
2105195972f6Sopenharmony_ci
2106195972f6Sopenharmony_ci  p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt - 1));
2107195972f6Sopenharmony_ci  if (p == NULL) {
2108195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_DEBUG,
2109195972f6Sopenharmony_ci                ("tcp_keepalive: could not allocate memory for pbuf\n"));
2110195972f6Sopenharmony_ci    return ERR_MEM;
2111195972f6Sopenharmony_ci  }
2112195972f6Sopenharmony_ci  tcp_output_fill_options(pcb, p, 0, 0);
2113195972f6Sopenharmony_ci  err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
2114195972f6Sopenharmony_ci
2115195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F" err %d.\n",
2116195972f6Sopenharmony_ci                          pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
2117195972f6Sopenharmony_ci  return err;
2118195972f6Sopenharmony_ci}
2119195972f6Sopenharmony_ci
2120195972f6Sopenharmony_ci/**
2121195972f6Sopenharmony_ci * Send persist timer zero-window probes to keep a connection active
2122195972f6Sopenharmony_ci * when a window update is lost.
2123195972f6Sopenharmony_ci *
2124195972f6Sopenharmony_ci * Called by tcp_slowtmr()
2125195972f6Sopenharmony_ci *
2126195972f6Sopenharmony_ci * @param pcb the tcp_pcb for which to send a zero-window probe packet
2127195972f6Sopenharmony_ci */
2128195972f6Sopenharmony_cierr_t
2129195972f6Sopenharmony_citcp_zero_window_probe(struct tcp_pcb *pcb)
2130195972f6Sopenharmony_ci{
2131195972f6Sopenharmony_ci  err_t err;
2132195972f6Sopenharmony_ci  struct pbuf *p;
2133195972f6Sopenharmony_ci  struct tcp_hdr *tcphdr;
2134195972f6Sopenharmony_ci  struct tcp_seg *seg;
2135195972f6Sopenharmony_ci  u16_t len;
2136195972f6Sopenharmony_ci  u8_t is_fin;
2137195972f6Sopenharmony_ci  u32_t snd_nxt;
2138195972f6Sopenharmony_ci  u8_t optlen = LWIP_TCP_OPT_LENGTH_SEGMENT(0, pcb);
2139195972f6Sopenharmony_ci
2140195972f6Sopenharmony_ci  LWIP_ASSERT("tcp_zero_window_probe: invalid pcb", pcb != NULL);
2141195972f6Sopenharmony_ci
2142195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to "));
2143195972f6Sopenharmony_ci  ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
2144195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("\n"));
2145195972f6Sopenharmony_ci
2146195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG,
2147195972f6Sopenharmony_ci              ("tcp_zero_window_probe: tcp_ticks %"U32_F
2148195972f6Sopenharmony_ci               "   pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
2149195972f6Sopenharmony_ci               tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
2150195972f6Sopenharmony_ci
2151195972f6Sopenharmony_ci  /* Only consider unsent, persist timer should be off when there is data in-flight */
2152195972f6Sopenharmony_ci  seg = pcb->unsent;
2153195972f6Sopenharmony_ci  if (seg == NULL) {
2154195972f6Sopenharmony_ci    /* Not expected, persist timer should be off when the send buffer is empty */
2155195972f6Sopenharmony_ci    return ERR_OK;
2156195972f6Sopenharmony_ci  }
2157195972f6Sopenharmony_ci
2158195972f6Sopenharmony_ci  /* increment probe count. NOTE: we record probe even if it fails
2159195972f6Sopenharmony_ci     to actually transmit due to an error. This ensures memory exhaustion/
2160195972f6Sopenharmony_ci     routing problem doesn't leave a zero-window pcb as an indefinite zombie.
2161195972f6Sopenharmony_ci     RTO mechanism has similar behavior, see pcb->nrtx */
2162195972f6Sopenharmony_ci  if (pcb->persist_probe < 0xFF) {
2163195972f6Sopenharmony_ci    ++pcb->persist_probe;
2164195972f6Sopenharmony_ci  }
2165195972f6Sopenharmony_ci
2166195972f6Sopenharmony_ci  is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
2167195972f6Sopenharmony_ci  /* we want to send one seqno: either FIN or data (no options) */
2168195972f6Sopenharmony_ci  len = is_fin ? 0 : 1;
2169195972f6Sopenharmony_ci
2170195972f6Sopenharmony_ci  p = tcp_output_alloc_header(pcb, optlen, len, seg->tcphdr->seqno);
2171195972f6Sopenharmony_ci  if (p == NULL) {
2172195972f6Sopenharmony_ci    LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
2173195972f6Sopenharmony_ci    return ERR_MEM;
2174195972f6Sopenharmony_ci  }
2175195972f6Sopenharmony_ci  tcphdr = (struct tcp_hdr *)p->payload;
2176195972f6Sopenharmony_ci
2177195972f6Sopenharmony_ci  if (is_fin) {
2178195972f6Sopenharmony_ci    /* FIN segment, no data */
2179195972f6Sopenharmony_ci    TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
2180195972f6Sopenharmony_ci  } else {
2181195972f6Sopenharmony_ci    /* Data segment, copy in one byte from the head of the unacked queue */
2182195972f6Sopenharmony_ci    char *d = ((char *)p->payload + TCP_HLEN);
2183195972f6Sopenharmony_ci    /* Depending on whether the segment has already been sent (unacked) or not
2184195972f6Sopenharmony_ci       (unsent), seg->p->payload points to the IP header or TCP header.
2185195972f6Sopenharmony_ci       Ensure we copy the first TCP data byte: */
2186195972f6Sopenharmony_ci    pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
2187195972f6Sopenharmony_ci  }
2188195972f6Sopenharmony_ci
2189195972f6Sopenharmony_ci  /* The byte may be acknowledged without the window being opened. */
2190195972f6Sopenharmony_ci  snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + 1;
2191195972f6Sopenharmony_ci  if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
2192195972f6Sopenharmony_ci    pcb->snd_nxt = snd_nxt;
2193195972f6Sopenharmony_ci  }
2194195972f6Sopenharmony_ci  tcp_output_fill_options(pcb, p, 0, 0);
2195195972f6Sopenharmony_ci
2196195972f6Sopenharmony_ci  err = tcp_output_control_segment(pcb, p, &pcb->local_ip, &pcb->remote_ip);
2197195972f6Sopenharmony_ci
2198195972f6Sopenharmony_ci  LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
2199195972f6Sopenharmony_ci                          " ackno %"U32_F" err %d.\n",
2200195972f6Sopenharmony_ci                          pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
2201195972f6Sopenharmony_ci  return err;
2202195972f6Sopenharmony_ci}
2203195972f6Sopenharmony_ci#endif /* LWIP_TCP */
2204