| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*
- * Copyright (C) 2001-2005 by egnite Software GmbH
- * Copyright (c) 1993 by Digital Equipment Corporation
- * Copyright (c) 1983, 1993 by The Regents of the University of California
- *
- * 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. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``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
- * COPYRIGHT OWNER OR CONTRIBUTORS 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.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*!
- * \file net/tcpout.c
- * \brief TCP output functions.
- *
- * \verbatim
- * $Id: tcpout.c 4473 2012-08-20 15:12:45Z haraldkipp $
- * \endverbatim
- */
- #include <cfg/os.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/heap.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/icmp.h>
- #include <netinet/ip_icmp.h>
- #include <netinet/ipcsum.h>
- #include <sys/socket.h>
- #include <netinet/tcp.h>
- #include <sys/thread.h>
- #ifdef NUTDEBUG
- #include <net/netdebug.h>
- #endif
- /*!
- * \addtogroup xgTCP
- */
- /*!
- * \brief Initiate TCP segment transmission.
- *
- * Check the TCP socket status and send any segment waiting
- * for transmission.
- *
- * The function will not return until the data has been stored in the
- * network device hardware for transmission. If the device is not ready
- * for transmitting a new packet, the calling thread will be suspended
- * until the device becomes ready again.
- *
- * If the target host is connected through an Ethernet network and if
- * the hardware address of that host is currently unknown, an ARP
- * request is sent out and the function will block until a response
- * is received or an ARP timeout occurs.
- *
- * Segments containing data or SYN and FIN flags are added to a special
- * queue for unacknowledged segments and will be retransmitted by the
- * TCP timer thread, if not acknowledged by the remote within a specific
- * time. The state machine will remove these segments from the queue
- * as soon as they are acknowledged.
- *
- * \note This function is mainly used by the TCP state machine.
- * Applications typically do not call this function but
- * use NutTcpSend(), which is part of the TCP socket interface.
- *
- * \param sock Socket descriptor. This pointer must have been retrieved
- * by calling NutTcpCreateSocket().
- * \param data Pointer to TCP segment contents.
- * \param size TCP segment length.
- *
- * \return 0 on success, -1 otherwise. Returning 0 does not imply that
- * the data has been successfully delivered, because flow control
- * and retransmission is still handled in the background.
- */
- int NutTcpOutput(TCPSOCKET * sock, const uint8_t * data, uint16_t size)
- {
- NETBUF *nb;
- NETBUF *nb_clone = 0;
- TCPHDR *th;
- uint16_t csum;
- uint8_t hlen;
- /*
- * Check if anything to send at all.
- */
- if (size == 0
- && (sock->so_tx_flags & (SO_SYN | SO_FIN | SO_FORCE)) == 0)
- return 0;
- /*
- * Build TCP header. Add room for MAXSEG option if this is a
- * SYN segment.
- */
- hlen = sizeof(TCPHDR);
- if (sock->so_tx_flags & SO_SYN)
- hlen += 4;
- if ((nb = NutNetBufAlloc(0, NBAF_TRANSPORT, hlen)) == 0) {
- sock->so_last_error = ENOBUFS;
- return -1;
- }
- th = (TCPHDR *) nb->nb_tp.vp;
- th->th_sport = sock->so_local_port;
- th->th_dport = sock->so_remote_port;
- th->th_x2 = 0;
- th->th_off = hlen >> 2;
- sock->so_tx_flags &= ~SO_FORCE;
- /*
- * Process ACK flag.
- */
- th->th_seq = htonl(sock->so_tx_nxt);
- if (sock->so_tx_flags & SO_ACK) {
- th->th_flags = TH_ACK;
- sock->so_tx_flags &= ~SO_ACK;
- th->th_ack = htonl(sock->so_rx_nxt);
- } else {
- th->th_flags = 0;
- th->th_ack = 0;
- }
- /*
- * Any SYN is sent first. Add options too. We rely on the caller
- * not to send a SYN segment with data, because this may break
- * some old stacks.
- */
- if (sock->so_tx_flags & SO_SYN) {
- uint8_t *cp;
- uint16_t n_mss = htons(sock->so_mss);
- th->th_flags |= TH_SYN;
- sock->so_tx_flags &= ~SO_SYN;
- sock->so_tx_nxt++;
- cp = (uint8_t *) (th + 1);
- *cp++ = TCPOPT_MAXSEG;
- *cp++ = TCPOLEN_MAXSEG;
- *cp++ = *(uint8_t *)&n_mss;
- *cp = *((uint8_t *)(&n_mss) + 1);
- }
- /*
- * Next preference is sending data. Set PUSH flag.
- */
- else if (size) {
- if ((nb = NutNetBufAlloc(nb, NBAF_APPLICATION, size)) == 0) {
- sock->so_last_error = ENOBUFS;
- return -1;
- }
- memcpy(nb->nb_ap.vp, (void *)data, size);
- sock->so_tx_nxt += size;
- th->th_flags |= TH_PUSH;
- }
- /*
- * If all data sent, transmit any waiting FIN.
- */
- else if (sock->so_tx_flags & SO_FIN) {
- th->th_flags |= TH_FIN;
- sock->so_tx_flags &= ~SO_FIN;
- sock->so_tx_nxt++;
- //@@@printf ("[%04X]TcpOutput: sending FIN\n", (u_short) sock);
- }
- /*
- * We close our receiver window, if it is
- * below the maximum segment size.
- */
- if (sock->so_rx_win < sock->so_mss)
- th->th_win = 0;
- else
- th->th_win = htons(sock->so_rx_win);
- th->th_sum = 0;
- th->th_urp = 0;
- /*
- * Calculate TCP checksum.
- */
- csum =
- NutIpPseudoChkSumPartial(sock->so_local_addr, sock->so_remote_addr,
- IPPROTO_TCP,
- htons(nb->nb_tp.sz + nb->nb_ap.sz));
- csum = NutIpChkSumPartial(csum, th, nb->nb_tp.sz);
- th->th_sum = NutIpChkSum(csum, nb->nb_ap.vp, nb->nb_ap.sz);
- #ifdef NUTDEBUG
- if (__tcp_trf)
- NutDumpTcpHeader(__tcp_trs, "OUT", sock, nb);
- #endif
- /*
- * To avoid a race condition in tcp state machine, the segment is first
- * appended to the transmission que, and then sent to the network.
- */
- /*
- * Append the segment to our transmission queue.
- */
- //@@@printf ("[%04X]TcpOutput: size: %u, flags: %u\n", (u_short) sock, size, th->th_flags);
- if (size || ((th->th_flags & (TH_FIN | TH_SYN)))) {
- //@@@printf ("[%04X]TcpOutput: appending nb to queue\n", (u_short) sock);
- NETBUF *nbp;
- nb->nb_next = 0;
- if ((nbp = sock->so_tx_nbq) == 0) {
- sock->so_tx_nbq = nb;
- /*
- * First entry, so initialize our retransmission timer.
- * It is also set at various places in the state machine,
- * but here is the best central point to do it. We may
- * carefully check later, if we can remove some in the
- * state machine.
- */
- sock->so_retran_time = (uint16_t) NutGetMillis() | 1;
- }
- else {
- while (nbp->nb_next)
- nbp = nbp->nb_next;
- nbp->nb_next = nb;
- }
- if (sock->so_rtt_seq == 0)
- sock->so_rtt_seq = ntohl (th->th_seq);
- nb_clone = NutNetBufClonePart(nb, 0);
- if (nb_clone == NULL) {
- sock->so_last_error = ENOBUFS;
- return -1;
- }
- }
- else
- nb_clone = nb;
- /*
- * IP output might fail because of routing, ARP or network device
- * problems or because the system ran out of memory.
- */
- if (NutIpOutput(IPPROTO_TCP, sock->so_remote_addr, nb_clone))
- return -1;
- NutNetBufFree (nb_clone);
- return 0;
- }
- /*!
- * \brief Reject an incoming segment.
- *
- * Send RST in response to an incoming segment, which should
- * be rejected.
- *
- * The function avoids to send out a RST segment in response to
- * an incoming RST segment.
- *
- * \note This function is mainly used by the TCP state machine.
- * Applications typically do not call this function.
- *
- * \param nb Network buffer structure of the incoming segment.
- * Will be released within this function.
- *
- * \return 0 on success, -1 otherwise.
- */
- int NutTcpReject(NETBUF * nb)
- {
- uint16_t csum;
- IPHDR *ih = (IPHDR *) nb->nb_nw.vp;
- TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;
- /*
- * Never send RST in response to RST.
- */
- if (th->th_flags & TH_RST) {
- NutNetBufFree(nb);
- return 0;
- }
- /*
- * Remove any application data and TCP header options.
- */
- nb->nb_ap.sz = 0;
- nb->nb_tp.sz = sizeof(TCPHDR);
- /*
- * Swap ports.
- */
- csum = th->th_sport;
- th->th_sport = th->th_dport;
- th->th_dport = csum;
- /*
- * If the incoming segment has an ACK field, the reset
- * takes its sequence number from the ACK field of the
- * segment, otherwise the reset has sequence number zero
- * and the ACK field is set to the sum of the sequence
- * number and segment length of the incoming segment.
- */
- if (th->th_flags & TH_ACK) {
- th->th_flags = TH_RST;
- th->th_seq = th->th_ack;
- th->th_ack = 0;
- } else {
- if (th->th_flags & TH_SYN)
- th->th_ack = htonl(ntohl(th->th_seq) + 1);
- else
- th->th_ack = th->th_seq;
- th->th_seq = 0;
- th->th_flags = TH_RST | TH_ACK;
- }
- th->th_x2 = 0;
- th->th_off = sizeof(TCPHDR) / 4;
- th->th_win = 0;
- th->th_urp = 0;
- /*
- * Calculate TCP checksum without application data.
- */
- th->th_sum = 0;
- csum =
- NutIpPseudoChkSumPartial(ih->ip_dst, ih->ip_src, IPPROTO_TCP,
- htons(nb->nb_tp.sz));
- th->th_sum = NutIpChkSum(csum, th, nb->nb_tp.sz);
- /*
- * Sent segment back to the source.
- */
- #ifdef NUTDEBUG
- if (__tcp_trf)
- NutDumpTcpHeader(__tcp_trs, "REJ", 0, nb);
- #endif
- if(NutIpOutput(IPPROTO_TCP, ih->ip_src, nb) == 0)
- NutNetBufFree(nb);
- return 0;
- }
|