📄 tcp_out.c
字号:
/** * @file * Transmission Control Protocol, outgoing traffic * * The output functions of TCP. * *//* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels <adam@sics.se> * */#include "lwip/opt.h"#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */#include "lwip/tcp_impl.h"#include "lwip/def.h"#include "lwip/mem.h"#include "lwip/memp.h"#include "lwip/sys.h"#include "lwip/ip_addr.h"#include "lwip/netif.h"#include "lwip/inet_chksum.h"#include "lwip/stats.h"#include "lwip/snmp.h"#include <string.h>/* Define some copy-macros for checksum-on-copy so that the code looks nicer by preventing too many ifdef's. */#if TCP_CHECKSUM_ON_COPY#define TCP_DATA_COPY(dst, src, len, seg) do { \ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \ len, &seg->chksum, &seg->chksum_swapped); \ seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);#else /* TCP_CHECKSUM_ON_COPY*/#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)#endif /* TCP_CHECKSUM_ON_COPY*//** Define this to 1 for an extra check that the output checksum is valid * (usefule when the checksum is generated by the application, not the stack) */#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0#endif/* Forward declarations.*/static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);/** Allocate a pbuf and create a tcphdr at p->payload, used for output * functions other than the default tcp_output -> tcp_output_segment * (e.g. tcp_send_empty_ack, etc.) * * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr) * @param optlen length of header-options * @param datalen length of tcp data to reserve in pbuf * @param seqno_be seqno in network byte order (big-endian) * @return pbuf with p->payload being the tcp_hdr */static struct pbuf *tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen, u32_t seqno_be /* already in network byte order */){ struct tcp_hdr *tcphdr; struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM); if (p != NULL) { LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", (p->len >= TCP_HLEN + optlen)); tcphdr = (struct tcp_hdr *)p->payload; tcphdr->src = htons(pcb->local_port); tcphdr->dest = htons(pcb->remote_port); tcphdr->seqno = seqno_be; tcphdr->ackno = htonl(pcb->rcv_nxt); TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); tcphdr->wnd = htons(pcb->rcv_ann_wnd); tcphdr->chksum = 0; tcphdr->urgp = 0; /* If we're sending a packet, update the announced right window edge */ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; } return p;}/** * Called by tcp_close() to send a segment including FIN flag but not data. * * @param pcb the tcp_pcb over which to send a segment * @return ERR_OK if sent, another err_t otherwise */err_ttcp_send_fin(struct tcp_pcb *pcb){ /* first, try to add the fin to the last unsent segment */ if (pcb->unsent != NULL) { struct tcp_seg *last_unsent; for (last_unsent = pcb->unsent; last_unsent->next != NULL; last_unsent = last_unsent->next); if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) { /* no SYN/FIN/RST flag in the header, we can add the FIN flag */ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN); return ERR_OK; } } /* no data, no length, flags, copy=1, no optdata */ return tcp_enqueue_flags(pcb, TCP_FIN);}/** * Create a TCP segment with prefilled header. * * Called by tcp_write and tcp_enqueue_flags. * * @param pcb Protocol control block for the TCP connection. * @param p pbuf that is used to hold the TCP header. * @param flags TCP flags for header. * @param seqno TCP sequence number of this packet * @param optflags options to include in TCP header * @return a new tcp_seg pointing to p, or NULL. * The TCP header is filled in except ackno and wnd. * p is freed on failure. */static struct tcp_seg *tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags){ struct tcp_seg *seg; u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags); if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n")); pbuf_free(p); return NULL; } seg->flags = optflags; seg->next = NULL; seg->p = p; seg->len = p->tot_len - optlen;#if TCP_OVERSIZE_DBGCHECK seg->oversize_left = 0;#endif /* TCP_OVERSIZE_DBGCHECK */#if TCP_CHECKSUM_ON_COPY seg->chksum = 0; seg->chksum_swapped = 0; /* check optflags */ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED", (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);#endif /* TCP_CHECKSUM_ON_COPY */ /* build TCP header */ if (pbuf_header(p, TCP_HLEN)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n")); TCP_STATS_INC(tcp.err); tcp_seg_free(seg); return NULL; } seg->tcphdr = (struct tcp_hdr *)seg->p->payload; seg->tcphdr->src = htons(pcb->local_port); seg->tcphdr->dest = htons(pcb->remote_port); seg->tcphdr->seqno = htonl(seqno); /* ackno is set in tcp_output */ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags); /* wnd and chksum are set in tcp_output */ seg->tcphdr->urgp = 0; return seg;} /** * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end. * * This function is like pbuf_alloc(layer, length, PBUF_RAM) except * there may be extra bytes available at the end. * * @param layer flag to define header size. * @param length size of the pbuf's payload. * @param max_length maximum usable size of payload+oversize. * @param oversize pointer to a u16_t that will receive the number of usable tail bytes. * @param pcb The TCP connection that willo enqueue the pbuf. * @param apiflags API flags given to tcp_write. * @param first_seg true when this pbuf will be used in the first enqueued segment. * @param */#if TCP_OVERSIZEstatic struct pbuf *tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length, u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags, u8_t first_seg){ struct pbuf *p; u16_t alloc = length;#if LWIP_NETIF_TX_SINGLE_PBUF LWIP_UNUSED_ARG(max_length); LWIP_UNUSED_ARG(pcb); LWIP_UNUSED_ARG(apiflags); LWIP_UNUSED_ARG(first_seg); /* always create MSS-sized pbufs */ alloc = TCP_MSS;#else /* LWIP_NETIF_TX_SINGLE_PBUF */ if (length < max_length) { /* Should we allocate an oversized pbuf, or just the minimum * length required? If tcp_write is going to be called again * before this segment is transmitted, we want the oversized * buffer. If the segment will be transmitted immediately, we can * save memory by allocating only length. We use a simple * heuristic based on the following information: * * Did the user set TCP_WRITE_FLAG_MORE? * * Will the Nagle algorithm defer transmission of this segment? */ if ((apiflags & TCP_WRITE_FLAG_MORE) || (!(pcb->flags & TF_NODELAY) && (!first_seg || pcb->unsent != NULL || pcb->unacked != NULL))) { alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE)); } }#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ p = pbuf_alloc(layer, alloc, PBUF_RAM); if (p == NULL) { return NULL; } LWIP_ASSERT("need unchained pbuf", p->next == NULL); *oversize = p->len - length; /* trim p->len to the currently used size */ p->len = p->tot_len = length; return p;}#else /* TCP_OVERSIZE */#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)#endif /* TCP_OVERSIZE */#if TCP_CHECKSUM_ON_COPY/** Add a checksum of newly added data to the segment */static voidtcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum, u8_t *seg_chksum_swapped){ u32_t helper; /* add chksum to old chksum and fold to u16_t */ helper = chksum + *seg_chksum; chksum = FOLD_U32T(helper); if ((len & 1) != 0) { *seg_chksum_swapped = 1 - *seg_chksum_swapped; chksum = SWAP_BYTES_IN_WORD(chksum); } *seg_chksum = chksum;}#endif /* TCP_CHECKSUM_ON_COPY *//** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen). * * @param pcb the tcp pcb to check for * @param len length of data to send (checked agains snd_buf) * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise */static err_ttcp_write_checks(struct tcp_pcb *pcb, u16_t len){ /* connection is in invalid state for data transmission? */ if ((pcb->state != ESTABLISHED) && (pcb->state != CLOSE_WAIT) && (pcb->state != SYN_SENT) && (pcb->state != SYN_RCVD)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); return ERR_CONN; } else if (len == 0) { return ERR_OK; } /* fail on too much data */ if (len > pcb->snd_buf) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); /* If total number of pbufs on the unsent/unacked queues exceeds the * configured maximum, return an error */ /* check for configured max queuelen and possible overflow */ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n", pcb->snd_queuelen, TCP_SND_QUEUELEN)); TCP_STATS_INC(tcp.memerr); pcb->flags |= TF_NAGLEMEMERR; return ERR_MEM; } if (pcb->snd_queuelen != 0) { LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty", pcb->unacked != NULL || pcb->unsent != NULL); } else { LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty", pcb->unacked == NULL && pcb->unsent == NULL); } return ERR_OK;}/** * Write data for sending (but does not send it immediately). * * It waits in the expectation of more data being sent soon (as * it can send them more efficiently by combining them together). * To prompt the system to send data now, call tcp_output() after * calling tcp_write(). * * @param pcb Protocol control block for the TCP connection to enqueue data for. * @param arg Pointer to the data to be enqueued for sending. * @param len Data length in bytes * @param apiflags combination of following flags : * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent, * @return ERR_OK if enqueued, another err_t on error */err_ttcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags){ struct pbuf *concat_p = NULL; struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL; u16_t pos = 0; /* position in 'arg' data */ u16_t queuelen; u8_t optlen = 0; u8_t optflags = 0;#if TCP_OVERSIZE u16_t oversize = 0; u16_t oversize_used = 0;#endif /* TCP_OVERSIZE */#if TCP_CHECKSUM_ON_COPY u16_t concat_chksum = 0; u8_t concat_chksum_swapped = 0; u16_t concat_chksummed = 0;#endif /* TCP_CHECKSUM_ON_COPY */ err_t err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -