/** 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 = tcp_tx_pbuf_alloc(pcb, optlen + datalen, PBUF_RAM); if (p != NULL) { pbuf_header(p, TCP_HLEN); 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 = RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd); // Which means: htons(pcb->rcv_ann_wnd >> pcb->rcv_scale); 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; }
/** 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->connid1 = htonl(pcb->conn_id.connid1); tcphdr->connid2 = htonl(pcb->conn_id.connid2); tcphdr->seqno = seqno_be; tcphdr->ackno = htonl(pcb->rcv_nxt); TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK); tcphdr->wnd = htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd))); /* 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_output() to actually send a TCP segment over IP. * * @param seg the tcp_seg to send * @param pcb the tcp_pcb for the TCP connection used to send the segment */ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) { u16_t len; u32_t *opts; /* The TCP header has already been constructed, but the ackno and wnd fields remain. */ seg->tcphdr->ackno = htonl(pcb->rcv_nxt); /* advertise our receive window size in this TCP segment */ seg->tcphdr->wnd = RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd); // Which means: htons(pcb->rcv_ann_wnd >> pcb->rcv_scale); pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd; /* Add any requested options. NB MSS option is only set on SYN packets, so ignore it here */ LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)(seg->tcphdr + 1) % 4) == 0); opts = (u32_t *)(void *)(seg->tcphdr + 1); if (seg->flags & TF_SEG_OPTS_MSS) { TCP_BUILD_MSS_OPTION(*opts, pcb->advtsd_mss); opts += 1; // Move to the next line (meaning next 32 bit) as this option is 4 bytes long } /* If RCV_SCALE is set then prepare segment for window scaling option */ if (seg->flags & TF_SEG_OPTS_WNDSCALE) { TCP_BUILD_WNDSCALE_OPTION(*opts, pcb->rcv_scale); opts += 1; // Move to the next line (meaning next 32 bit) as this option is 3 bytes long + we added 1 byte NOOP padding => total 4 bytes } #if LWIP_TCP_TIMESTAMPS pcb->ts_lastacksent = pcb->rcv_nxt; if (seg->flags & TF_SEG_OPTS_TS) { tcp_build_timestamp_option(pcb, opts); opts += 3; // Move to the next line (meaning next 32 bit) as this option is 10 bytes long, 12 with padding (so jump 3 lines) } #endif /* If we don't have a local IP address, we get one by calling ip_route(). */ if (ip_addr_isany(&(pcb->local_ip))) { LWIP_ASSERT("tcp_output_segment: need to find route to host", 0); } /* Set retransmission timer running if it is not currently enabled */ if(pcb->rtime == -1) { pcb->rtime = 0; } if (pcb->rttest == 0) { pcb->rttest = tcp_ticks; pcb->rtseq = seg->seqno; LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); } LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + seg->len)); len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); seg->p->len -= len; seg->p->tot_len -= len; seg->p->payload = seg->tcphdr; seg->tcphdr->chksum = 0; TCP_STATS_INC(tcp.xmit); #if LWIP_NETIF_HWADDRHINT ip_output_hinted(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, IP_PROTO_TCP, &(pcb->addr_hint)); #elif LWIP_3RD_PARTY_L3 pcb->ip_output(seg->p, pcb, seg->seqno < pcb->snd_nxt); #else /* LWIP_NETIF_HWADDRHINT*/ ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, IP_PROTO_TCP); #endif /* LWIP_NETIF_HWADDRHINT*/ }