| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914 |
- /*
- * Copyright (C) 2001-2007 by egnite Software GmbH
- * Copyright (C) 2009 by egnite 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/tcpsm.c
- * \brief TCP state machine.
- *
- * \verbatim
- * $Id: tcpsm.c 5691 2014-05-02 15:23:32Z haraldkipp $
- * \endverbatim
- */
- #include <cfg/os.h>
- #include <cfg/tcp.h>
- #include <sys/thread.h>
- #include <sys/heap.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <errno.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <net/route.h>
- #include <sys/socket.h>
- #include <netinet/tcputil.h>
- #include <netinet/tcp.h>
- #ifdef NUTDEBUG
- #include <net/netdebug.h>
- #endif
- #ifndef NUT_THREAD_TCPSMSTACK
- #if defined(__AVR__)
- #if defined(__GNUC__)
- /* avr-gcc size optimized code used 148 bytes. */
- #define NUT_THREAD_TCPSMSTACK 256
- #else
- /* icc-avr v7.19 used 312 bytes. */
- #define NUT_THREAD_TCPSMSTACK 512
- #endif
- #elif defined(__AVR32__)
- #define NUT_THREAD_TCPSMSTACK 1024
- #else
- /* arm-elf-gcc used 260 bytes with size optimized, 644 bytes with debug code. */
- #define NUT_THREAD_TCPSMSTACK 384
- #endif
- #endif
- #ifndef TCP_RETRIES_MAX
- #define TCP_RETRIES_MAX 7
- #endif
- extern TCPSOCKET *tcpSocketList;
- /*!
- * \addtogroup xgTCP
- */
- /*@{*/
- HANDLE tcp_in_rdy;
- NETBUF *volatile tcp_in_nbq;
- static uint16_t tcp_in_cnt;
- static HANDLE tcpThread = 0;
- #ifndef TCP_COLLECT_INADV
- #define TCP_COLLECT_INADV 8
- #endif
- #ifndef TCP_COLLECT_SLIMIT
- #define TCP_COLLECT_SLIMIT 256
- #endif
- #ifndef TCP_BACKLOG_MAX
- #define TCP_BACKLOG_MAX 8
- #endif
- #if TCP_BACKLOG_MAX
- #ifndef TCP_BACKLOG_TIME
- #define TCP_BACKLOG_TIME 5
- #endif
- static NETBUF *tcp_backlog[TCP_BACKLOG_MAX];
- static uint_fast8_t tcp_backlog_time[TCP_BACKLOG_MAX];
- #endif
- static size_t tcp_adv_cnt;
- static size_t tcp_adv_max = TCP_WINSIZE;
- static int tcp_run_gc = 0;
- static void NutTcpStateProcess(TCPSOCKET * sock, NETBUF * nb);
- /* ================================================================
- * Helper functions
- * ================================================================
- */
- #if TCP_BACKLOG_MAX
- static int NutTcpBacklogAdd(NETBUF *nb)
- {
- uint_fast8_t i;
- uint_fast8_t n = TCP_BACKLOG_MAX;
- IPHDR *ih = (IPHDR *) nb->nb_nw.vp;
- TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;
- /* Process SYN segments only. */
- if ((th->th_flags & (TH_SYN | TH_ACK | TH_RST)) == TH_SYN) {
- for (i = 0; i < TCP_BACKLOG_MAX; i++) {
- if (tcp_backlog[i] == NULL) {
- /* Remember the first free entry. */
- if (n == TCP_BACKLOG_MAX) {
- n = i;
- }
- }
- else if (((IPHDR *) tcp_backlog[i]->nb_nw.vp)->ip_src == ih->ip_src &&
- ((TCPHDR *) tcp_backlog[i]->nb_tp.vp)->th_sport == th->th_sport) {
- /* Already received a SYN. Either the remote is too impatient
- or we are too busy. Kill this entry and reject the SYN. */
- NutNetBufFree(tcp_backlog[i]);
- tcp_backlog[i] = NULL;
- return -1;
- }
- }
- /* First SYN from the remote for this port.
- If there is a free entry left, then use it. */
- if (n != TCP_BACKLOG_MAX) {
- tcp_backlog[n] = nb;
- tcp_backlog_time[n] = 0;
- return 0;
- }
- }
- return -1;
- }
- static NETBUF *NutTcpBacklogCheck(uint16_t port)
- {
- NETBUF *nb;
- uint_fast8_t i;
- uint_fast8_t n = TCP_BACKLOG_MAX;
- for (i = 0; i < TCP_BACKLOG_MAX; i++) {
- if (tcp_backlog[i]) {
- if (((TCPHDR *) tcp_backlog[i]->nb_tp.vp)->th_dport == port) {
- if (n == TCP_BACKLOG_MAX || tcp_backlog_time[i] > tcp_backlog_time[n]) {
- n = i;
- }
- }
- }
- }
- if (n == TCP_BACKLOG_MAX) {
- nb = NULL;
- } else {
- nb = tcp_backlog[n];
- tcp_backlog[n] = NULL;
- }
- return nb;
- }
- static NETBUF *NutTcpBacklogTimer(void)
- {
- NETBUF *nb;
- uint_fast8_t i;
- uint_fast8_t n = TCP_BACKLOG_MAX;
- for (i = 0; i < TCP_BACKLOG_MAX; i++) {
- if (tcp_backlog[i]) {
- if (tcp_backlog_time[i] < TCP_BACKLOG_TIME) {
- tcp_backlog_time[i]++;
- } else {
- n = i;
- }
- }
- }
- if (n == TCP_BACKLOG_MAX) {
- nb = NULL;
- } else {
- nb = tcp_backlog[n];
- tcp_backlog[n] = NULL;
- }
- return nb;
- }
- #endif /* TCP_BACKLOG_MAX */
- /*!
- * \brief Reads TCP option fields if any, and writes the data to
- * the socket descriptor if important for us.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpInputOptions(TCPSOCKET * sock, NETBUF * nb)
- {
- uint8_t *cp;
- uint16_t s;
- /* any options there? */
- if (nb->nb_tp.sz <= sizeof(TCPHDR)) {
- return;
- }
- /* loop through available options */
- for (cp = ((uint8_t *) nb->nb_tp.vp) + sizeof(TCPHDR); (*cp != TCPOPT_EOL)
- && (cp - (uint8_t *) nb->nb_tp.vp < (int) nb->nb_tp.sz);) {
- switch (*cp) {
- /* On NOP just proceed to next option */
- case TCPOPT_NOP:
- cp++;
- continue;
- /* Read MAXSEG option */
- case TCPOPT_MAXSEG:
- /* Network uses big endian. */
- s = cp[2];
- s <<= 8;
- s |= cp[3];
- if (s < sock->so_mss) {
- sock->so_mss = s;
- }
- cp += TCPOLEN_MAXSEG;
- break;
- /* Ignore any other options */
- default:
- cp += *(uint8_t *) (cp + 1);
- break;
- }
- }
- }
- /*!
- * \brief Move application data in sync from the network buffer
- * structure to the socket's receive buffer.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpProcessAppData(TCPSOCKET * sock, NETBUF * nb)
- {
- /*
- * Add the NETBUF to the socket's input buffer.
- */
- if (sock->so_rx_buf) {
- NETBUF *nbp = sock->so_rx_buf;
- while (nbp->nb_next) {
- nbp = nbp->nb_next;
- }
- nbp->nb_next = nb;
- } else {
- sock->so_rx_buf = nb;
- }
- /*
- * Update the number of bytes available in the socket's input buffer
- * and the sequence number we expect next.
- */
- sock->so_rx_cnt += nb->nb_ap.sz;
- sock->so_rx_nxt += nb->nb_ap.sz;
- /*
- * Reduce our TCP window size.
- */
- if (nb->nb_ap.sz >= sock->so_rx_win) {
- sock->so_rx_win = 0;
- } else {
- sock->so_rx_win -= nb->nb_ap.sz;
- }
- /*
- * Set the socket's ACK flag. This will enable ACK transmission in
- * the next outgoing segment. If no more NETBUFs are queued, we
- * force immediate transmission of the ACK.
- */
- sock->so_tx_flags |= SO_ACK;
- if (nb->nb_next) {
- nb->nb_next = NULL;
- } else {
- sock->so_tx_flags |= SO_FORCE;
- }
- if (++sock->so_rx_apc > TCP_COLLECT_INADV) {
- NETBUF *nbq;
- int_fast8_t apc = sock->so_rx_apc;
- int cnt = sock->so_rx_cnt;
- for (nbq = sock->so_rx_buf; nbq; nbq = nbq->nb_next) {
- if (nbq->nb_ap.sz < TCP_COLLECT_SLIMIT) {
- sock->so_rx_apc -= NutNetBufCollect(nbq, cnt);
- break;
- }
- if (--apc < 8) {
- break;
- }
- cnt -= nbq->nb_ap.sz;
- }
- }
- NutTcpOutput(sock, NULL, 0);
- }
- /*
- * \param sock Socket descriptor.
- */
- static void NutTcpProcessSyn(TCPSOCKET * sock, IPHDR * ih, TCPHDR * th)
- {
- uint16_t mss;
- NUTDEVICE *dev;
- IFNET *nif;
- sock->so_local_addr = ih->ip_dst;
- sock->so_remote_port = th->th_sport;
- sock->so_remote_addr = ih->ip_src;
- sock->so_rx_nxt = sock->so_tx_wl1 = sock->so_rx_isn = ntohl(th->th_seq);
- sock->so_rx_nxt++;
- sock->so_tx_win = ntohs(th->th_win);
- /*
- * To avoid unnecessary fragmentation, limit the
- * maximum segment size to the maximum transfer
- * unit of our interface.
- */
- dev = NutIpRouteQuery(ih->ip_src, NULL);
- if (dev) {
- nif = dev->dev_icb;
- mss = nif->if_mtu - sizeof(IPHDR) - sizeof(TCPHDR);
- if (sock->so_mss == 0 || sock->so_mss > mss) {
- sock->so_mss = mss;
- }
- /* Limit output buffer size to mms */
- if (sock->so_devobsz > sock->so_mss) {
- sock->so_devobsz = sock->so_mss;
- }
- }
- }
- /*!
- * \brief ACK processing.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- *
- */
- static int NutTcpProcessAck(TCPSOCKET * sock, TCPHDR * th, uint16_t length)
- {
- NETBUF *nb;
- uint32_t h_seq;
- uint32_t h_ack;
- /*
- * If remote acked something not yet send, reply immediately.
- */
- h_ack = ntohl(th->th_ack);
- if (SeqIsAfter(h_ack, sock->so_tx_nxt)) {
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- return 0;
- }
- /*
- * If the new sequence number or acknowledged sequence number
- * is above our last update, we adjust our transmit window.
- * Avoid dupe ACK processing on window updates.
- */
- if (h_ack == sock->so_tx_una) {
- h_seq = ntohl(th->th_seq);
- if (SeqIsAfter(h_seq, sock->so_tx_wl1) || (h_seq == sock->so_tx_wl1 && !SeqIsAfter(sock->so_tx_wl2, h_ack))) {
- sock->so_tx_win = ntohs(th->th_win);
- sock->so_tx_wl1 = h_seq;
- sock->so_tx_wl2 = h_ack;
- }
- }
- /*
- * Ignore old ACKs but wake up sleeping transmitter threads, because
- * the window size may have changed.
- */
- if (SeqIsAfter(sock->so_tx_una, h_ack)) {
- return 0;
- }
- /*
- * Process duplicate ACKs.
- */
- if (h_ack == sock->so_tx_una) {
- /*
- * Don't count, if nothing is waiting for ACK,
- * segment contains data or on SYN/FIN segments.
- */
- if (sock->so_tx_nbq && length == 0 && (th->th_flags & (TH_SYN | TH_FIN)) == 0) {
- /*
- * If dupe counter reaches it's limit, resend
- * the oldest unacknowledged netbuf.
- */
- if (++sock->so_tx_dup >= 3) {
- sock->so_tx_dup = 0;
- #ifdef NUTDEBUG
- if (__tcp_trf & NET_DBG_SOCKSTATE) {
- NutDumpTcpHeader(__tcp_trs, "RET", sock, sock->so_tx_nbq);
- }
- #endif
- /*
- * Retransmit first unacked packet from queue.
- * Actually we got much more trouble if this fails.
- */
- if (NutTcpStateRetranTimeout(sock)) {
- return -1;
- }
- }
- }
- return 0;
- }
- /*
- * We're here, so the ACK must have actually acked something
- */
- sock->so_tx_dup = 0;
- sock->so_tx_una = h_ack;
- /*
- * Bugfix contributed by Liu Limin: If the remote is slow and this
- * line is missing, then Ethernut will send a full data packet even
- * if the remote closed the window.
- */
- sock->so_tx_win = ntohs(th->th_win);
- /*
- * Do round trip time calculation.
- */
- if (sock->so_rtt_seq && SeqIsAfter(h_ack, sock->so_rtt_seq)) {
- NutTcpCalcRtt(sock);
- }
- sock->so_rtt_seq = 0;
- /*
- * Remove all acknowledged netbufs.
- */
- while ((nb = sock->so_tx_nbq) != NULL) {
- /* Calculate the sequence beyond this netbuf. */
- h_seq = ntohl(((TCPHDR *) (nb->nb_tp.vp))->th_seq) + nb->nb_ap.sz;
- if (((TCPHDR *) (nb->nb_tp.vp))->th_flags & (TH_SYN | TH_FIN)) {
- h_seq++;
- }
- if (SeqIsAfter(h_seq, h_ack)) {
- break;
- }
- sock->so_tx_nbq = nb->nb_next;
- NutNetBufFree(nb);
- }
- /*
- * Reset retransmit timer and wake up waiting transmissions.
- */
- if (sock->so_tx_nbq) {
- sock->so_retran_time = (uint16_t) NutGetMillis() | 1;
- } else {
- sock->so_retran_time = 0;
- }
- sock->so_retransmits = 0;
- return 0;
- }
- /* ================================================================
- * State changes.
- * ================================================================
- */
- /*!
- * State change, possibly inform application.
- *
- * \param sock Socket descriptor.
- * \param state New state to switch to.
- *
- * \return 0 on success, -1 on illegal state change attempt.
- */
- static int NutTcpStateChange(TCPSOCKET * sock, uint8_t state)
- {
- int rc = 0;
- ureg_t txf = 0;
- switch (sock->so_state) {
- /* Handle the most common case first. */
- case TCPS_ESTABLISHED:
- switch (state) {
- case TCPS_FIN_WAIT_1:
- /*
- * Closed by application.
- */
- sock->so_tx_flags |= SO_FIN | SO_ACK;
- txf = 1;
- #ifdef RTLCONNECTHACK
- /*
- * Hack alert!
- * On the RTL8019AS we got a problem. Because of not handling
- * the CHRDY line, the controller drops outgoing packets when
- * a browser opens multiple connections concurrently, producing
- * several short incoming packets. Empirical test showed, that
- * a slight delay during connects and disconnects helped to
- * remarkably reduce this problem.
- */
- NutDelay(5);
- #endif
- break;
- case TCPS_CLOSE_WAIT:
- /*
- * FIN received.
- */
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- txf = 1;
- break;
- default:
- rc = -1;
- break;
- }
- break;
- case TCPS_LISTEN:
- /*
- * SYN received.
- */
- if (state == TCPS_SYN_RECEIVED) {
- sock->so_tx_flags |= SO_SYN | SO_ACK;
- txf = 1;
- #ifdef RTLCONNECTHACK
- /*
- * Hack alert!
- * On the RTL8019AS we got a problem. Because of not handling
- * the CHRDY line, the controller drops outgoing packets when
- * a browser opens multiple connections concurrently, producing
- * several short incoming packets. Empirical test showed, that
- * a slight delay during connects and disconnects helped to
- * remarkably reduce this problem.
- */
- NutDelay(5);
- #endif
- } else
- if (state == TCPS_DESTROY) {
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- } else
- rc = -1;
- break;
- case TCPS_SYN_SENT:
- switch (state) {
- case TCPS_LISTEN:
- /*
- * RST received on passive socket.
- */
- break;
- case TCPS_SYN_RECEIVED:
- /*
- * SYN received.
- */
- sock->so_tx_flags |= SO_SYN | SO_ACK;
- txf = 1;
- break;
- case TCPS_ESTABLISHED:
- /*
- * SYNACK received.
- */
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- txf = 1;
- break;
- case TCPS_DESTROY:
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- default:
- rc = -1;
- break;
- }
- break;
- case TCPS_SYN_RECEIVED:
- switch (state) {
- case TCPS_LISTEN:
- /*
- * RST received on passive socket.
- */
- break;
- case TCPS_ESTABLISHED:
- /*
- * ACK of SYN received.
- */
- break;
- case TCPS_FIN_WAIT_1:
- /*
- * Closed by application.
- */
- sock->so_tx_flags |= SO_FIN;
- txf = 1;
- break;
- case TCPS_CLOSE_WAIT:
- /*
- * FIN received.
- */
- sock->so_tx_flags |= SO_FIN | SO_ACK;
- txf = 1;
- break;
- default:
- rc = -1;
- break;
- }
- break;
- case TCPS_FIN_WAIT_1:
- switch (state) {
- case TCPS_FIN_WAIT_1:
- case TCPS_FIN_WAIT_2:
- /*
- * ACK of FIN received.
- */
- break;
- case TCPS_CLOSING:
- /*
- * FIN received.
- */
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- txf = 1;
- break;
- case TCPS_TIME_WAIT:
- /*
- * FIN and ACK of FIN received.
- */
- break;
- case TCPS_DESTROY:
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- default:
- rc = -1;
- break;
- }
- break;
- case TCPS_FIN_WAIT_2:
- if (state == TCPS_DESTROY) {
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- } else
- /*
- * FIN received.
- */
- if (state != TCPS_TIME_WAIT) {
- rc = -1;
- }
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- txf = 1;
- break;
- case TCPS_CLOSE_WAIT:
- /*
- * Closed by application.
- */
- if (state == TCPS_LAST_ACK) {
- sock->so_tx_flags |= SO_FIN | SO_ACK;
- txf = 1;
- } else {
- rc = -1;
- }
- break;
- case TCPS_CLOSING:
- if (state == TCPS_DESTROY) {
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- } else
- /*
- * ACK of FIN received.
- */
- if (state != TCPS_TIME_WAIT) {
- rc = -1;
- }
- break;
- case TCPS_LAST_ACK:
- if (state == TCPS_DESTROY) {
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- } else
- rc = -1;
- break;
- case TCPS_TIME_WAIT:
- if (state == TCPS_DESTROY) {
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- } else
- rc = -1;
- break;
- case TCPS_CLOSED:
- switch (state) {
- case TCPS_LISTEN:
- /*
- * Passive open by application.
- */
- break;
- case TCPS_SYN_SENT:
- /*
- * Active open by application.
- */
- sock->so_tx_flags |= SO_SYN;
- txf = 1;
- break;
- case TCPS_DESTROY:
- /* Change to DESTROY. Socket will be destroyed ASAP */
- break;
- default:
- rc = -1;
- break;
- }
- break;
- case TCPS_DESTROY:
- /* Do nothing, ignore the state change. Socket will be destroyed ASAP */
- break;
- }
- #ifdef NUTDEBUG
- if (__tcp_trf & NET_DBG_SOCKSTATE) {
- fprintf(__tcp_trs, " %04x-", (unsigned int) sock);
- if (rc) {
- NutDumpSockState(__tcp_trs, sock->so_state, "**ERR ", "**>");
- }
- NutDumpSockState(__tcp_trs, state, "[>", "]");
- }
- #endif
- if (rc == 0) {
- if (state == TCPS_DESTROY) {
- tcp_run_gc = 1;
- }
- sock->so_state = state;
- if (txf && NutTcpOutput(sock, NULL, 0)) {
- if (state == TCPS_SYN_SENT) {
- rc = -1;
- sock->so_last_error = EHOSTDOWN;
- NutEventPostAsync(&sock->so_ac_tq);
- }
- }
- if (state == TCPS_CLOSE_WAIT) {
- /*
- * Inform application.
- */
- NutEventBroadcast(&sock->so_rx_tq);
- NutEventBroadcast(&sock->so_pc_tq);
- NutEventBroadcast(&sock->so_ac_tq);
- /* Wake up all running selects (read and write queue) on this socket */
- NutSelectWakeup(sock->so_rx_wq_list, WQ_FLAG_READ);
- NutSelectWakeup(sock->so_tx_wq_list, WQ_FLAG_WRITE);
- }
- }
- return rc;
- }
- /* ================================================================
- * Application events.
- * ================================================================
- */
- /*!
- * \brief Initiated by the application.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- *
- * \return 0 if granted, -1 otherwise. The specific error code
- * can be retrieved by calling NutTcpError().
- */
- int NutTcpStatePassiveOpenEvent(TCPSOCKET * sock)
- {
- if (sock->so_state != TCPS_CLOSED) {
- sock->so_last_error = EISCONN;
- return -1;
- }
- NutTcpStateChange(sock, TCPS_LISTEN);
- #if TCP_BACKLOG_MAX
- {
- /* If a SYN segment is already waiting in the backlog,
- then process it and return to the caller. */
- NETBUF *nb = NutTcpBacklogCheck(sock->so_local_port);
- if (nb) {
- NutTcpInputOptions(sock, nb);
- NutTcpStateProcess(sock, nb);
- return 0;
- }
- }
- if (NutEventWait(&sock->so_pc_tq, sock->so_read_to)) {
- sock->so_state = TCPS_CLOSED;
- sock->so_last_error = ETIMEDOUT;
- return -1;
- }
- #else
- /* For backward compatibility we simply block the application.
- If we do not have a backlog, then timing out would not make
- much sense anyway, because incoming connection attempts will
- be immediately rejected. */
- NutEventWait(&sock->so_pc_tq, 0);
- #endif /* TCP_BACKLOG_MAX */
- return 0;
- }
- /*!
- * \brief Initiated by the application.
- *
- * The caller must make sure, that the socket is in closed state.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- *
- * \return 0 if granted, -1 otherwise.
- */
- int NutTcpStateActiveOpenEvent(TCPSOCKET * sock)
- {
- /*
- * Switch state to SYN_SENT. This will
- * transmit a SYN packet.
- */
- NutTcpStateChange(sock, TCPS_SYN_SENT);
- /*
- * Block application.
- */
- if (sock->so_state == TCPS_SYN_SENT) {
- if (NutEventWait(&sock->so_ac_tq, sock->so_write_to)) {
- NutTcpAbortSocket(sock, ETIMEDOUT);
- return -1;
- }
- }
- if (sock->so_state != TCPS_ESTABLISHED && sock->so_state != TCPS_CLOSE_WAIT) {
- return -1;
- }
- return 0;
- }
- /*!
- * \brief Socket close by application.
- *
- * If socket is in state SYN_RECEIVED or ESTABLISHED,
- * it is changed to FINWAIT1.
- *
- * No further data sending is accepted.
- * Receiving is still allowed.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- */
- int NutTcpStateCloseEvent(TCPSOCKET * sock)
- {
- if (sock == NULL) {
- return -1;
- }
- NutThreadYield();
- switch (sock->so_state) {
- case TCPS_LISTEN:
- case TCPS_SYN_SENT:
- case TCPS_CLOSED:
- /*
- * No connection yet, immediately destroy the socket.
- */
- NutTcpStateChange(sock, TCPS_DESTROY);
- break;
- case TCPS_SYN_RECEIVED:
- case TCPS_ESTABLISHED:
- /*
- * Send a FIN and wait for ACK or FIN.
- */
- NutTcpStateChange(sock, TCPS_FIN_WAIT_1);
- break;
- case TCPS_CLOSE_WAIT:
- /*
- * RFC 793 is wrong.
- */
- NutTcpStateChange(sock, TCPS_LAST_ACK);
- break;
- case TCPS_FIN_WAIT_1:
- case TCPS_FIN_WAIT_2:
- case TCPS_CLOSING:
- case TCPS_LAST_ACK:
- case TCPS_TIME_WAIT:
- sock->so_last_error = EALREADY;
- return -1;
- case TCPS_DESTROY:
- default:
- sock->so_last_error = ENOTCONN;
- return -1;
- }
- return 0;
- }
- /*!
- * \brief Initiated by the application.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- */
- int NutTcpStateWindowEvent(TCPSOCKET * sock)
- {
- if (sock == NULL) {
- return -1;
- }
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- NutTcpOutput(sock, NULL, 0);
- return 0;
- }
- /* ================================================================
- * Timeout events.
- * ================================================================
- */
- /*!
- * \brief Retransmit a segment after ACK timeout.
- *
- * This function is called by the TCP timer.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \returns Nonzero if socket was aborted due to reach of retransmit
- * limit or network error.
- *
- */
- int NutTcpStateRetranTimeout(TCPSOCKET * sock)
- {
- NETBUF *so_tx_next;
- if (sock->so_retransmits++ > TCP_RETRIES_MAX) {
- /* Abort the socket */
- NutTcpAbortSocket(sock, ETIMEDOUT);
- return -1;
- } else {
- #ifdef NUTDEBUG
- if (__tcp_trf & NET_DBG_SOCKSTATE) {
- NutDumpTcpHeader(__tcp_trs, "RET", sock, sock->so_tx_nbq);
- }
- #endif
- /* We must save sock->so_tx_nbq->nb_next before calling NutIpOutput,
- * because in case of error the NETBUF is release by NutIpOutput and
- * not longer available.
- */
- so_tx_next = sock->so_tx_nbq->nb_next;
- if (NutIpOutput(IPPROTO_TCP, sock->so_remote_addr, sock->so_tx_nbq)) {
- /* Adjust packet queue */
- sock->so_tx_nbq = so_tx_next;
- /* Abort the socket */
- NutTcpAbortSocket(sock, ENETDOWN);
- return -1;
- } else {
- /* Restart the retransmission timer. */
- sock->so_retran_time = (uint16_t) NutGetMillis() | 1;
- /* Double retransmission timeout up to maximum. */
- if (sock->so_rtto < TCP_RTTO_MAX / 2) {
- sock->so_rtto <<= 1;
- } else {
- sock->so_rtto = TCP_RTTO_MAX;
- }
- return 0;
- }
- }
- }
- /* ================================================================
- * Segment arrival events.
- * ================================================================
- */
- /*!
- * \brief
- * Process incoming segments in listening state.
- *
- * Wait for a connection request from a remote socket.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpStateListen(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- /*
- * Got a SYN segment. Store relevant data in our socket
- * structure and switch to TCPS_SYN_RECEIVED.
- */
- if ((flags & (TH_SYN | TH_ACK | TH_RST)) == TH_SYN) {
- NutTcpProcessSyn(sock, nb->nb_nw.vp, th);
- NutTcpStateChange(sock, TCPS_SYN_RECEIVED);
- NutNetBufFree(nb);
- } else
- NutTcpReject(nb);
- }
- /*!
- * \brief Process incoming segments in SYN-SENT state.
- *
- * Wait for a matching connection request after having sent ours.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpStateSynSent(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- /*
- * Validate ACK, if set.
- */
- if (flags & TH_ACK) {
- if (!SeqIsBetween(ntohl(th->th_ack), sock->so_tx_isn + 1, sock->so_tx_nxt)) {
- NutTcpReject(nb);
- return;
- }
- }
- /*
- * Handle RST flag. If we were in the LISTEN state,
- * then we return to the LISTEN state, otherwise we
- * abort the connection and go to the CLOSED state.
- */
- if (flags & TH_RST) {
- if (flags & TH_ACK) {
- NutTcpAbortSocket(sock, ECONNREFUSED);
- }
- NutNetBufFree(nb);
- return;
- }
- /*
- * Handle SYN flag. If we got a valid ACK too, then
- * our connection is established. Otherwise enter
- * SYNRCVD state.
- */
- if (flags & TH_SYN) {
- NutTcpProcessSyn(sock, nb->nb_nw.vp, th);
- if (flags & TH_ACK) {
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- NutTcpStateChange(sock, TCPS_ESTABLISHED);
- /* Wake up the actively connecting thread. */
- NutEventPost(&sock->so_ac_tq);
- } else {
- NutTcpStateChange(sock, TCPS_SYN_RECEIVED);
- }
- }
- NutNetBufFree(nb);
- }
- /*
- * \brief
- * Process incoming segments in SYN-RECEIVED state.
- *
- * Waiting for a confirming connection request
- * acknowledgment after having both received
- * and sent a connection request.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpStateSynReceived(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- /*
- * If our previous ack receives a reset response,
- * then we fall back to the listening state.
- */
- if (flags & TH_RST) {
- if (sock->so_pc_tq) {
- NutTcpStateChange(sock, TCPS_LISTEN);
- } else {
- NutTcpAbortSocket(sock, ECONNREFUSED);
- }
- NutNetBufFree(nb);
- sock->so_retran_time = 0;
- NutTcpDiscardBuffers(sock);
- return;
- }
- /*
- * Silently discard duplicate SYN.
- */
- if (flags & TH_SYN) {
- NutNetBufFree(nb);
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if ((flags & TH_ACK) == 0) {
- NutNetBufFree(nb);
- return;
- }
- /*
- * Reject out of window sequence.
- */
- if (!SeqIsBetween(ntohl(th->th_ack), sock->so_tx_una + 1, sock->so_tx_nxt)) {
- NutTcpReject(nb);
- return;
- }
- /* Acknowledge processing. */
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- /*
- * Even SYN segments may contain application data, which will be stored
- * in the socket's input buffer. However, there is no need to post an
- * event to any thread waiting for data, because our connection is not
- * yet established.
- */
- if (nb->nb_ap.sz) {
- NutTcpProcessAppData(sock, nb);
- } else {
- NutNetBufFree(nb);
- }
- /*
- * Process state change.
- */
- if (flags & TH_FIN) {
- sock->so_rx_nxt++;
- NutTcpStateChange(sock, TCPS_CLOSE_WAIT);
- } else {
- NutTcpStateChange(sock, TCPS_ESTABLISHED);
- NutEventPost(&sock->so_pc_tq);
- NutEventPost(&sock->so_ac_tq);
- }
- }
- /*
- * \brief Process incoming segments from established connections.
- *
- * Received application data will be delivered to the application
- * until we receive a FIN segment.
- *
- * \param sock Socket descriptor.
- * \param flags TCP flags.
- * \param th Pointer to the TCP header within the NETBUF.
- * \param nb Network buffer structure containing a TCP segment.
- *
- * \todo We may remove the unused counter of dropped segments, which
- * were out of sequence.
- */
- static void NutTcpStateEstablished(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- if (flags & TH_RST) {
- NutNetBufFree(nb);
- NutTcpAbortSocket(sock, ECONNRESET);
- return;
- }
- /*
- * Reject SYNs. Silently discard late SYNs
- * (Thanks to Mike Cornelius).
- */
- if (flags & TH_SYN) {
- if (ntohl(th->th_seq) != sock->so_rx_isn) {
- NutTcpReject(nb);
- } else {
- NutNetBufFree(nb);
- }
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if ((flags & TH_ACK) == 0) {
- NutNetBufFree(nb);
- return;
- }
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- /*
- * If the sequence number of the incoming segment is larger than
- * expected, we probably missed one or more previous segments. Let's
- * add this one to a linked list of segments received in advance and
- * hope that the missing data will arrive later.
- */
- if (SeqIsAfter(ntohl(th->th_seq), sock->so_rx_nxt)) {
- NETBUF *nbq;
- NETBUF **nbqp;
- TCPHDR *thq;
- uint32_t th_seq;
- uint32_t thq_seq;
- if (nb->nb_ap.sz) {
- /* Keep track of the number of bytes used by packets
- received in advance. Honor a global limit. */
- tcp_adv_cnt += nb->nb_dl.sz + sizeof(IPHDR) + sizeof(TCPHDR) + nb->nb_ap.sz;
- if (tcp_adv_cnt > tcp_adv_max) {
- /* Limit reached, discard the packet. */
- NutNetBufFree(nb);
- tcp_adv_cnt -= nb->nb_dl.sz + sizeof(IPHDR) + sizeof(TCPHDR) + nb->nb_ap.sz;
- } else {
- nbq = sock->so_rx_nbq;
- nbqp = &sock->so_rx_nbq;
- while (nbq) {
- thq = (TCPHDR *) (nbq->nb_tp.vp);
- th_seq = ntohl(th->th_seq);
- thq_seq = ntohl(thq->th_seq);
- if (SeqIsAfter(thq_seq, th_seq)) {
- *nbqp = nb;
- nb->nb_next = nbq;
- break;
- }
- if (th_seq == thq_seq) {
- NutNetBufFree(nb);
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- NutTcpOutput(sock, NULL, 0);
- return;
- }
- nbqp = &nbq->nb_next;
- nbq = nbq->nb_next;
- }
- if (nbq == NULL) {
- *nbqp = nb;
- nb->nb_next = NULL;
- }
- }
- } else
- NutNetBufFree(nb);
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- NutTcpOutput(sock, NULL, 0);
- return;
- }
- /*
- * Acknowledge any sequence numbers not expected,
- * even if they do not contain any data. Keep alive
- * packets contain a sequence number one less
- * than the next data expected and they do not
- * contain any data.
- */
- if (ntohl(th->th_seq) != sock->so_rx_nxt) {
- sock->so_tx_flags |= SO_ACK | SO_FORCE;
- /* This seems to be unused. */
- sock->so_oos_drop++;
- NutNetBufFree(nb);
- NutTcpOutput(sock, NULL, 0);
- }
- /*
- * The sequence number is exactly what we expected.
- */
- else if (nb->nb_ap.sz) {
- NutTcpProcessAppData(sock, nb);
- /*
- * Process segments we may have received in advance.
- */
- while ((nb = sock->so_rx_nbq) != NULL) {
- th = (TCPHDR *) (nb->nb_tp.vp);
- if (SeqIsAfter(ntohl(th->th_seq), sock->so_rx_nxt)) {
- break;
- }
- sock->so_rx_nbq = nb->nb_next;
- /* Update the heap space used by packets
- received in advance. */
- tcp_adv_cnt -= nb->nb_dl.sz + sizeof(IPHDR) + sizeof(TCPHDR) + nb->nb_ap.sz;
- if (ntohl(th->th_seq) == sock->so_rx_nxt) {
- NutTcpProcessAppData(sock, nb);
- flags |= th->th_flags;
- } else
- NutNetBufFree(nb);
- }
- /* Wake up a thread waiting for data. */
- NutEventPost(&sock->so_rx_tq);
- /* Wake up all running selects (read queue) on this socket */
- NutSelectWakeup(sock->so_rx_wq_list, WQ_FLAG_READ);
- } else {
- NutNetBufFree(nb);
- }
- if (flags & TH_FIN) {
- sock->so_rx_nxt++;
- NutTcpStateChange(sock, TCPS_CLOSE_WAIT);
- }
- }
- /*
- * \brief Process incoming segments in FIN-WAIT1 state.
- *
- * Waiting for a connection termination request
- * from the remote, or an acknowledgment of the
- * connection termination request previously sent.
- *
- * The application already closed the socket.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- *
- * \todo The out of sync case seems to be ignored. Anyway, do we
- * really need to process application data in this state?
- */
- static void NutTcpStateFinWait1(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- if (flags & TH_RST) {
- NutNetBufFree(nb);
- NutTcpStateChange(sock, TCPS_DESTROY);
- return;
- }
- /*
- * Reject SYNs.
- */
- if (flags & TH_SYN) {
- NutTcpReject(nb);
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if ((flags & TH_ACK) == 0) {
- NutNetBufFree(nb);
- return;
- }
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- /*
- * All segments had been acknowledged, including our FIN.
- */
- if (sock->so_tx_nxt == sock->so_tx_una) {
- NutTcpStateChange(sock, TCPS_FIN_WAIT_2);
- }
- /*
- * Process application data and release the network buffer.
- * Is this really required?
- */
- if (nb->nb_ap.sz) {
- NutTcpProcessAppData(sock, nb);
- /* Wake up a thread waiting for data. */
- NutEventPost(&sock->so_rx_tq);
- /* Wake up all running selects (read queue) on this socket */
- NutSelectWakeup(sock->so_rx_wq_list, WQ_FLAG_READ);
- } else {
- NutNetBufFree(nb);
- }
- if (flags & TH_FIN) {
- sock->so_rx_nxt++;
- /*
- * Our FIN has been acked.
- */
- sock->so_time_wait = 0;
- if (sock->so_state == TCPS_FIN_WAIT_2) {
- NutTcpStateChange(sock, TCPS_TIME_WAIT);
- } else {
- NutTcpStateChange(sock, TCPS_CLOSING);
- }
- }
- }
- /*
- * \brief Process incoming segments in FIN-WAIT2 state.
- *
- * Waiting for a connection termination request
- * from the remote.
- *
- * The application already closed the socket.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- *
- * \todo There's probably no need to process application data.
- */
- static void NutTcpStateFinWait2(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- if (flags & TH_RST) {
- NutNetBufFree(nb);
- NutTcpStateChange(sock, TCPS_DESTROY);
- return;
- }
- /*
- * Reject SYNs.
- */
- if (flags & TH_SYN) {
- NutTcpReject(nb);
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if ((flags & TH_ACK) == 0) {
- NutNetBufFree(nb);
- return;
- }
- /*
- * Process acknowledge and application data and release the
- * network buffer.
- */
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- /* Do we really need this? */
- if (nb->nb_ap.sz) {
- NutTcpProcessAppData(sock, nb);
- /* Wake up a thread waiting for data. */
- NutEventPost(&sock->so_rx_tq);
- /* Wake up all running selects (read queue) on this socket */
- NutSelectWakeup(sock->so_rx_wq_list, WQ_FLAG_READ);
- } else
- NutNetBufFree(nb);
- if (flags & TH_FIN) {
- sock->so_rx_nxt++;
- sock->so_time_wait = 0;
- NutTcpStateChange(sock, TCPS_TIME_WAIT);
- }
- }
- /*
- * \brief
- * Process incoming segments in CLOSE-WAIT state.
- *
- * Waiting for a connection termination request
- * from the local application.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpStateCloseWait(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- if (flags & TH_RST) {
- NutNetBufFree(nb);
- NutTcpAbortSocket(sock, ECONNRESET);
- return;
- }
- /*
- * Reject SYNs.
- */
- if (flags & TH_SYN) {
- NutTcpReject(nb);
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if (flags & TH_ACK) {
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- }
- NutNetBufFree(nb);
- }
- /*
- * \brief
- * Process incoming segments in CLOSING state.
- *
- * Waiting for a connection termination request
- * acknowledgment from the remote.
- *
- * The application already closed the socket.
- *
- * \param sock Socket descriptor.
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpStateClosing(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- if (flags & TH_RST) {
- NutNetBufFree(nb);
- NutTcpStateChange(sock, TCPS_DESTROY);
- return;
- }
- /*
- * Reject SYNs.
- */
- if (flags & TH_SYN) {
- NutTcpReject(nb);
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if ((flags & TH_ACK) == 0) {
- NutNetBufFree(nb);
- return;
- }
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- /*
- * All segments had been acknowledged, including our FIN.
- */
- if (sock->so_tx_nxt == sock->so_tx_una) {
- sock->so_time_wait = 0;
- NutTcpStateChange(sock, TCPS_TIME_WAIT);
- }
- NutNetBufFree(nb);
- }
- /*!
- * \brief
- * Process incoming segment in LAST_ACK state.
- *
- * Waiting for an acknowledgment of the connection termination
- * request previously sent.
- *
- * The application already closed the socket.
- *
- * \param sock Socket descriptor.
- * \param flags TCP flags of incoming segment.
- * \param th TCP header of incoming segment.
- * \param nb Network buffer structure containing a TCP segment.
- */
- static void NutTcpStateLastAck(TCPSOCKET * sock, uint8_t flags, TCPHDR * th, NETBUF * nb)
- {
- if (flags & TH_RST) {
- NutNetBufFree(nb);
- NutTcpStateChange(sock, TCPS_DESTROY);
- return;
- }
- /*
- * Reject SYNs.
- */
- if (flags & TH_SYN) {
- NutTcpReject(nb);
- return;
- }
- /*
- * Silently discard segments without ACK.
- */
- if ((flags & TH_ACK) == 0) {
- NutNetBufFree(nb);
- return;
- }
- NutTcpProcessAck(sock, th, nb->nb_ap.sz);
- NutNetBufFree(nb);
- if (sock->so_tx_nxt == sock->so_tx_una) {
- NutTcpStateChange(sock, TCPS_DESTROY);
- }
- }
- /*!
- * \brief Process incoming TCP segments.
- *
- * Processing is based on the current state of the socket connection.
- *
- * \param sock Socket descriptor. This pointer must have been
- * retrieved by calling NutTcpCreateSocket().
- * \param nb Network buffer structure containing a TCP segment.
- * Will be released within this routine.
- */
- static void NutTcpStateProcess(TCPSOCKET * sock, NETBUF * nb)
- {
- uint32_t tx_win;
- uint32_t tx_una;
- TCPHDR *th = (TCPHDR *) nb->nb_tp.vp;
- uint8_t flags = th->th_flags;
- #ifdef NUTDEBUG
- if (__tcp_trf & NET_DBG_SOCKSTATE) {
- fprintf(__tcp_trs, " %04x-", (unsigned int) sock);
- NutDumpSockState(__tcp_trs, sock->so_state, "[", ">]");
- }
- #endif
- switch (sock->so_state) {
- /* Handle the most common case first. */
- case TCPS_ESTABLISHED:
- tx_win = sock->so_tx_win;
- tx_una = sock->so_tx_una;
- NutTcpStateEstablished(sock, flags, th, nb);
- /* Wake up all threads waiting for transmit, if something interesting happened. */
- if (sock->so_state != TCPS_ESTABLISHED || /* Status changed. */
- sock->so_tx_win > tx_win || /* Windows changed. */
- sock->so_tx_una != tx_una) { /* Unacknowledged data changed. */
- NutEventBroadcast(&sock->so_tx_tq);
- /* Wake up all running selects (write queue) on this socket */
- NutSelectWakeup(sock->so_tx_wq_list, WQ_FLAG_WRITE);
- }
- break;
- case TCPS_LISTEN:
- NutTcpStateListen(sock, flags, th, nb);
- break;
- case TCPS_SYN_SENT:
- NutTcpStateSynSent(sock, flags, th, nb);
- break;
- case TCPS_SYN_RECEIVED:
- NutTcpStateSynReceived(sock, flags, th, nb);
- break;
- case TCPS_FIN_WAIT_1:
- NutTcpStateFinWait1(sock, flags, th, nb);
- break;
- case TCPS_FIN_WAIT_2:
- NutTcpStateFinWait2(sock, flags, th, nb);
- break;
- case TCPS_CLOSE_WAIT:
- NutTcpStateCloseWait(sock, flags, th, nb);
- break;
- case TCPS_CLOSING:
- NutTcpStateClosing(sock, flags, th, nb);
- break;
- case TCPS_LAST_ACK:
- NutTcpStateLastAck(sock, flags, th, nb);
- break;
- case TCPS_TIME_WAIT:
- /*
- * Ignore everything while in TIME_WAIT state.
- */
- NutNetBufFree(nb);
- break;
- case TCPS_CLOSED:
- case TCPS_DESTROY:
- /*
- * Reject everything while in CLOSED or DESTROY state.
- */
- NutTcpReject(nb);
- break;
- default:
- NutNetBufFree(nb);
- break;
- }
- }
- /*!
- * \brief Destroy previously allocated socket marked for destroy.
- *
- * Remove socket from the socket list and release occupied memory.
- *
- * This function is called, if the TCP statemachine is idle
- */
- void NutTcpGarbadgeCollect(void)
- {
- TCPSOCKET *sp;
- TCPSOCKET *volatile *spp;
- TCPSOCKET *sock;
- /*
- * Remove socket from the list and free its allocated memory
- */
- sp = tcpSocketList;
- spp = &tcpSocketList;
- while (sp) {
- if (sp->so_state == TCPS_DESTROY) {
- sock = sp;
- *spp = sp->so_next;
- sp = sp->so_next;
- NutTcpDestroySocket(sock);
- continue;
- }
- spp = &sp->so_next;
- sp = sp->so_next;
- }
- }
- /*! \fn NutTcpSm(void *arg)
- * \brief TCP state machine thread.
- *
- * The TCP state machine serves two purposes: It processes incoming TCP
- * segments and handles TCP timers.
- */
- THREAD(NutTcpSm, arg)
- {
- NETBUF *nb;
- NETBUF *nbx;
- TCPHDR *th;
- IPHDR *ih;
- TCPSOCKET *sock;
- uint8_t tac = 0;
- (void)arg;
- /*
- * It won't help giving us a higher priority than the application
- * code. We depend on the speed of the reading application.
- */
- NutThreadSetPriority(32);
- for (;;) {
- if (tcp_run_gc) {
- tcp_run_gc = 0;
- NutTcpGarbadgeCollect();
- }
- if (++tac > 3 || NutEventWait(&tcp_in_rdy, 200)) {
- tac = 0;
-
- #if TCP_BACKLOG_MAX
- /* Process backlog timer.
- *
- * Note, that the tac counter will spoil any exact timing.
- * On the other hand, if we are very busy, it may not be that
- * bad to kill early SYN segments soon.
- */
- nb = NutTcpBacklogTimer();
- if (nb) {
- NutTcpReject(nb);
- }
- #endif /* TCP_BACKLOG_MAX */
- for (sock = tcpSocketList; sock; sock = sock->so_next) {
- /*
- * Send late acks.
- */
- if (sock->so_tx_flags & SO_ACK) {
- sock->so_tx_flags |= SO_FORCE;
- NutTcpOutput(sock, NULL, 0);
- }
- /*
- * Process retransmit timer.
- */
- if (sock->so_tx_nbq && sock->so_retran_time) {
- if ((uint16_t) ((uint16_t) NutGetMillis() - (sock->so_retran_time & ~1)) >= sock->so_rtto) {
- NutTcpStateRetranTimeout(sock);
- }
- }
- /*
- * Destroy sockets after timeout in TIMEWAIT state.
- */
- if (sock->so_state == TCPS_TIME_WAIT || sock->so_state == TCPS_FIN_WAIT_2) {
- if (sock->so_time_wait++ >= 9) {
- NutTcpStateChange(sock, TCPS_DESTROY);
- break;
- }
- }
- /*
- * Recover from SYN flood attacks.
- */
- else if (sock->so_state == TCPS_SYN_RECEIVED) {
- if (sock->so_time_wait++ >= 45) {
- sock->so_state = TCPS_LISTEN;
- sock->so_time_wait = 0;
- }
- }
- }
- } else {
- nb = tcp_in_nbq;
- tcp_in_nbq = NULL;
- tcp_in_cnt = 0;
- while (nb) {
- ih = (IPHDR *) nb->nb_nw.vp;
- th = (TCPHDR *) nb->nb_tp.vp;
- sock = NutTcpFindSocket(th->th_dport, th->th_sport, ih->ip_src);
- #ifdef NUTDEBUG
- if (__tcp_trf & NET_DBG_SOCKSTATE) {
- NutDumpTcpHeader(__tcp_trs, " IN", sock, nb);
- }
- #endif
- nbx = nb->nb_next;
- /* If a matching socket exists, process the NETBUF. */
- if (sock) {
- NutTcpInputOptions(sock, nb);
- NutTcpStateProcess(sock, nb);
- }
- #if TCP_BACKLOG_MAX
- /* No matching socket, try to add it to the backlog. */
- else if (NutTcpBacklogAdd(nb) == 0) {
- }
- #endif
- /* No matching socket and no backlog. Reject it. */
- else {
- NutTcpReject(nb);
- }
- nb = nbx;
- }
- }
- }
- }
- /*!
- * \brief Process incoming TCP segments.
- *
- * All incoming TCP packets are passed to this routine. They will
- * be added to a global queue and processed by the TCP state
- * machine, which is running on a separate thread.
- *
- * \note This routine is called by the IP layer on incoming
- * TCP segments. Applications typically do not call
- * this function.
- */
- void NutTcpStateMachine(NETBUF * nb)
- {
- NETBUF *nbp;
- uint16_t size;
- nb->nb_next = NULL;
- /*
- * Incoming TCP segments are rejected and released if no TCP
- * sockets have been opened. Not doing so would add them
- * to the queue and never release the NETBUF. Thanks to
- * Ralph Mason for this fix.
- */
- if (tcpThread == NULL) {
- NutTcpReject(nb);
- return;
- }
- nbp = tcp_in_nbq;
- if (nbp == NULL) {
- tcp_in_nbq = nb;
- NutEventPost(&tcp_in_rdy);
- } else {
- size = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz;
- if (tcp_in_cnt + size + 2048 < NutHeapAvailable()) {
- tcp_in_cnt += size;
- while (nbp->nb_next)
- nbp = nbp->nb_next;
- nbp->nb_next = nb;
- NutEventPost(&tcp_in_rdy);
- } else
- NutNetBufFree(nb);
- }
- }
- /*!
- * \brief Start TCP state machine.
- *
- * The socket interface will automatically call this routine as
- * soon as the first socket is created.
- *
- * \return 0 on success, -1 otherwise.
- */
- int NutTcpInitStateMachine(void)
- {
- if (tcpThread == NULL) {
- tcpThread = NutThreadCreate("tcpsm", NutTcpSm, NULL, NUT_THREAD_TCPSMSTACK * NUT_THREAD_STACK_MULT + NUT_THREAD_STACK_ADD);
- if (tcpThread == NULL) {
- return -1;
- }
- }
- return 0;
- }
- /*!
- * \brief Closes socket with error.
- *
- * Aborts any socket activity and sets last error.
- *
- * \param sock Socket descriptor.
- * \param last_error Error number to report
- *
- * \note This routine is called internally by Nut/Net.
- * Applications typically do not call this function.
- *
- * \return 0 on success, -1 otherwise.
- */
- int NutTcpAbortSocket(TCPSOCKET * sock, uint16_t last_error)
- {
- uint8_t current_state = sock->so_state;
- sock->so_last_error = last_error;
- sock->so_retran_time = 0;
- sock->so_time_wait = 0;
- /*
- * If NutTcpCloseSocket was already called, we have to change
- * to TCPS_TIME_WAIT state, otherwise the socket will not be destroyed.
- * For the other cases just go to TCPS_CLOSED.
- */
- if (sock->so_state >= TCPS_FIN_WAIT_1) {
- sock->so_state = TCPS_TIME_WAIT;
- } else {
- sock->so_state = TCPS_CLOSED;
- }
- NutTcpDiscardBuffers(sock);
- NutEventBroadcast(&sock->so_rx_tq);
- NutEventBroadcast(&sock->so_tx_tq);
- NutEventBroadcast(&sock->so_pc_tq);
- NutEventBroadcast(&sock->so_ac_tq);
- /* Check if we had a timeout on NutTcpConnect(). In this case there is no
- need for a notification of the select wait queues.
- */
- if (!((current_state == TCPS_SYN_SENT) && (last_error == ETIMEDOUT))) {
- /* Wake up all running selects on this socket */
- NutSelectWakeup(sock->so_rx_wq_list, WQ_FLAG_READ);
- NutSelectWakeup(sock->so_tx_wq_list, WQ_FLAG_WRITE);
- }
- return 0;
- }
- /*@}*/
|