tcp.c

Go to the documentation of this file.
00001 /*##############################################################################
00002 
00003 nIP - nano IP stack
00004 
00005 File        : tcp.c
00006 
00007 Description : TCP
00008 
00009 Copyright notice:
00010 
00011 Copyright (C) 2005 -
00012 Andreas Dittrich, dittrich@informatik.hu-berlin.de
00013 Jon Kowal, kowal@informatik.hu-berlin.de
00014 
00015 This program is free software; you can redistribute it and/or
00016 modify it under the terms of the GNU General Public License
00017 as published by the Free Software Foundation; either version 2
00018 of the License, or (at your option) any later version.
00019 
00020 This program is distributed in the hope that it will be useful,
00021 but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00023 GNU General Public License for more details.
00024 
00025 You should have received a copy of the GNU General Public License
00026 along with this program; if not, write to the Free Software
00027 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00028 
00029 #############################################################################*/
00030 
00031 #include "nip_init.h"
00032 #include "dispatcher.h"
00033 #include "inet.h"
00034 #include "os_core.h"
00035 #include "nip_error.h"
00036 #include "net/net_if.h"
00037 #include "net/tcp.h"
00038 #include "net/numbers.h"
00039 
00040 #if NIP_TCP_ENABLE == 1
00041 
00042 // static socket array
00043 #if NIP_TCP_MAX_SOCKETS == 1
00044         #define sockid 0
00045         struct nip_tcp_sock nip_tcp_sock = 0;
00046         #define NIP_TCP_SOCKATTR(sockid, attr) nip_tcp_sock.attr
00047 #else
00048         struct nip_tcp_sock nip_tcp_sockets[ NIP_TCP_MAX_SOCKETS ];
00049         #define NIP_TCP_SOCKATTR(sockid, attr) nip_tcp_sockets[sockid].attr
00050 #endif
00051 
00052 nip_tcp_sock_cnt_t nip_tcp_unaccepted_cnt;
00053 nip_tcp_sock_id_t  nip_tcp_unaccepted[ NIP_TCP_MAX_UNACCEPTED ];
00054 nip_tcp_sock_cnt_t nip_tcp_conn_cnt; /**< number of connections in nip_tcp_conns */
00055 nip_mem_handle_t   nip_tcp_conns = NIP_MEM_NULL; /**< dynamic memory for connection array */
00056 
00057 
00058 /** Register TCP socket with given address and flags.
00059  * @param addr structure to be filled with address and port. If all address
00060  *             bytes are set to Zero, the socket will listen on all interfaces.
00061  *             If port is set to Zero, the implementation will choose a random
00062  *             port, which should be the usual way to go for sockets with the
00063  *             NIP_TCP_SOCK_FLG_LISTENING-flag set.
00064  * @param flags possible flags are:
00065  *               - NIP_TCP_SOCK_FLG_NON_BLOCK
00066  *               - NIP_TCP_SOCK_FLG_LISTENING
00067  * @return NIP_TCP_NO_SOCKET on error. Global nip_error variable will be set to
00068  * NIP_E_OUT_OF_RESSOURCES in that case.
00069  */
00070 nip_tcp_sock_id_t nip_tcp_socket( struct nip_tcp_sock_addr *addr, uint8_t flags )
00071 {
00072 #if NIP_TCP_MAX_SOCKETS > 1
00073         // more than one socket available?
00074         // -> Loop through them until we find unused one.
00075         nip_tcp_sock_id_t sockid = 0;
00076         do
00077         {
00078 #endif
00079                 // check if socket is unused
00080                 if ( (NIP_TCP_SOCKATTR(sockid,flags) & NIP_TCP_SOCK_FLG_USED) == 0 )
00081                 {
00082                         // configure socket
00083                         NIP_TCP_SOCKATTR(sockid,flags) = flags | NIP_TCP_SOCK_FLG_USED;
00084                         nip_memcpy( &NIP_TCP_SOCKATTR(sockid,addr), addr, 4 );
00085                         return sockid;
00086                 }
00087 #if NIP_TCP_MAX_SOCKETS > 1
00088         } while ( ++sockid < NIP_TCP_MAX_SOCKETS );
00089 #endif
00090 
00091         // out of ressources
00092         nip_error = NIP_E_OUT_OF_RESSOURCES;
00093         return NIP_TCP_NO_SOCKET;
00094 }
00095 
00096 
00097 /** Remove connection TCB and entry in socket list.
00098  */
00099 void nip_tcp_del_conn( nip_tcp_sock_id_t connid )
00100 {
00101         struct nip_tcb     *tcb;
00102         nip_tcp_sock_cnt_t cnt = 0;
00103         nip_tcp_sock_id_t  sockid = NIP_TCP_NO_SOCKET;
00104         uint8_t            moveup;
00105         uint8_t            children;
00106 
00107         if ( connid < NIP_TCP_MAX_SOCKETS )
00108                 sockid = connid;
00109 
00110         do
00111         {
00112                 moveup = 0;
00113                 children = 0;
00114                 if ( nip_tcp_conn_cnt > 0 )
00115                 {
00116                 // get pointer to tcp array
00117                         tcb = nip_mem_obtain_ptr( nip_tcp_conns );
00118                         if ( tcb != NULL )
00119                         {
00120                                 do
00121                                 {
00122                                         if ( tcb->connid == connid )
00123                                         {
00124                                                 // free buffers
00125                                                 nip_mem_free( tcb->snd_buf );
00126                                                 nip_mem_free( tcb->rcv_buf );
00127                                                 // mark connection for removal
00128                                                 moveup++;
00129                                                 sockid = tcb->sockid;
00130                                         }
00131                                         else
00132                                         {
00133                                                 if ( tcb->sockid == sockid )
00134                                                         children++;
00135                                                 // move connection
00136                                                 nip_memcpy( tcb - moveup, tcb, sizeof( struct nip_tcb ) );
00137                                         }
00138                                         tcb++;
00139                                 } while ( ++cnt < nip_tcp_conn_cnt );
00140 
00141                                 // reduce connection count
00142                                 nip_tcp_conn_cnt -= moveup;
00143 
00144                                 // free pointer
00145                                 nip_mem_release_block( nip_tcp_conns );
00146                                 nip_mem_set_used( nip_tcp_conns, nip_tcp_conn_cnt * sizeof( struct nip_tcb ) );
00147                         }
00148                 }
00149         }
00150         // repeat loop to count children
00151         while ( moveup > 0 );
00152 
00153         // Delete socket, if set to autoclose, which means the user confirmed it will
00154         // not use the socket ID for reading or writing anymore.
00155         if ( sockid != NIP_TCP_NO_SOCKET
00156      && NIP_TCP_SOCKATTR(sockid,flags) & NIP_TCP_SOCK_FLG_AUTOCLOSE )
00157         {
00158                 // Make sure there aren't any spawned child-sockets still using it. If so,
00159                 // flag socket to not accept new connections.
00160                 if ( children > 0 )
00161                 {
00162                         NIP_TCP_SOCKATTR(sockid,flags) &= ~NIP_TCP_SOCK_FLG_LISTENING;
00163                 }
00164                 else
00165                         NIP_TCP_SOCKATTR(sockid,flags) = 0;
00166         }
00167 
00168 }
00169 
00170 /** Find transmission control block (tcb) for connection with matching address.
00171  * If rem_ip is not NULL the given sockid will be matched against the Ids of the
00172  * socket-array, otherwise sockid will be matched against connection IDs.
00173  *
00174  * @return pointer to tcb or NULL if tcb was not found. If NULL is returned,
00175  * nip_error will contain one of the following error codes
00176  *  - NIP_E_AGAIN         needed ressources were locked
00177  *  - NIP_E_INVALID_SOCK  given sockid does not belong to any open connection
00178  * @note You _have_ to release the given pointer after using it by calling
00179  *    nip_mem_release_block( nip_tcp_conns );
00180  */
00181 struct nip_tcb *
00182 nip_tcp_find_conn( nip_tcp_sock_id_t sockid, uint8_t *rem_ip, uint16_t rem_port)
00183 {
00184         struct nip_tcb     *tcb;
00185         nip_tcp_sock_cnt_t cnt = 0;
00186         if ( nip_tcp_conn_cnt > 0 )
00187         {
00188                 // get pointer to tcp array
00189                 tcb = nip_mem_obtain_ptr( nip_tcp_conns );
00190                 if ( tcb != NULL )
00191                 {
00192                         do
00193                         {
00194                                 // Search by address won't retrieve CLOSED sockets.
00195                                 if(( tcb->sockid        == sockid
00196                                   && rem_ip             != NULL
00197                                   && tcb->rem_addr.port == rem_port
00198                                   && nip_memcmp( tcb->rem_addr.ip, rem_ip, 4 ) == 0
00199                                   && tcb->status        != NIP_TCP_STAT_CLOSED )
00200                                 || ( tcb->connid  == sockid
00201                                   && rem_ip       == NULL )
00202                                   )
00203                                 {
00204                                         return tcb;
00205                                 }
00206                                 tcb++;
00207                         } while ( ++cnt < nip_tcp_conn_cnt );
00208 
00209                         // connection not found -> free pointer
00210                         nip_mem_release_block( nip_tcp_conns );
00211                         nip_error = NIP_E_INVALID_SOCK;
00212                 }
00213                 else
00214                 {
00215                         nip_error = NIP_E_AGAIN;
00216                 }
00217         }
00218 
00219         return NULL;
00220 }
00221 
00222 /** Create new transmission control block (tcb) with given parameters
00223  * @return pointer to tcb or NULL if there were no ressources to setup connection.
00224  * @note You _have_ to release the given pointer after using it by calling
00225  *    nip_mem_release_block( nip_tcp_conns );
00226  */
00227 struct nip_tcb *
00228 nip_tcp_create_conn( nip_tcp_sock_id_t sockid, uint8_t *rem_ip, uint16_t rem_port,
00229                      nip_tcp_stat_t init_status )
00230 {
00231         struct nip_tcb     *tcb   = NULL;
00232         nip_tcp_sock_cnt_t cnt;
00233         nip_tcp_sock_id_t  connid;
00234         if ( nip_tcp_conns == NIP_MEM_NULL )
00235         {
00236                 nip_tcp_conn_cnt = 0;
00237                 nip_tcp_conns = nip_mem_alloc( sizeof( struct nip_tcb ), 0, 0, NULL );
00238         }
00239 
00240         // append 0-initialized memory for new tcb
00241         if ( nip_mem_write( nip_tcp_conns, NULL, sizeof( struct nip_tcb ) )
00242                         == NIP_E_OK )
00243         {
00244                 // get pointer to tcp array
00245                 tcb = nip_mem_obtain_ptr( nip_tcp_conns );
00246                 if ( tcb != NULL )
00247                 {
00248                         // For listening sockets a new connection ID has to be determined, as
00249                         // they are forked off of the current listening socket. Connection Ids
00250                         // start beyond space for normal sockets.
00251                         if ( init_status == NIP_TCP_STAT_LISTEN )
00252                                 connid = NIP_TCP_MAX_SOCKETS;
00253                         else
00254                                 connid = sockid;
00255 
00256                         // check uniqueness of connection-id
00257                         unique_check:
00258                         cnt = 0;
00259                         while ( cnt < nip_tcp_conn_cnt )
00260                         {
00261                                 // check if other connection is using that id
00262                                 if ( tcb->connid == connid )
00263                                 {
00264                                         // try new id and restart probing
00265                                         connid++;
00266                                         tcb -= cnt;
00267                                         goto unique_check;
00268                                 }
00269                                 tcb++;
00270                                 cnt++;
00271                         };
00272 
00273                         nip_memset( tcb, 0, sizeof( struct nip_tcb ) );
00274 
00275                         tcb->snd_buf     = nip_mem_alloc( 0, 0, 0, NULL );
00276                         tcb->rcv_buf     = nip_mem_alloc( 0, 0, 0, NULL );
00277 
00278                         // if all went well, return tcb-pointer
00279                         if ( tcb->snd_buf != NIP_MEM_NULL && tcb->rcv_buf != NIP_MEM_NULL )
00280                         {
00281                                 // increase connection count
00282                                 nip_tcp_conn_cnt++ ;
00283 
00284                                 // configure tcb
00285                                 tcb->sockid = sockid;
00286                                 tcb->connid = connid;
00287                                 tcb->status = init_status;
00288                                 nip_memcpy( tcb->rem_addr.ip, rem_ip, 4 );
00289                                 tcb->rem_addr.port = rem_port;
00290 
00291                                 // initial send sequence number (iss) should be random.. we'll
00292                                 // postpone the randomness and base it on the time.
00293                                 ///@todo make iss random!
00294                                 tcb->iss     = nip_tickcount();
00295                                 tcb->rcv_wnd = NIP_TCP_DEFAULT_RCV_WND;
00296 
00297                                 tcb->snd_nxt = tcb->iss + 1; // Next received ACK has to be iss+1
00298                                 tcb->snd_una = tcb->iss;
00299 
00300                                 return tcb;
00301                         }
00302 
00303                         // on error free buffers and tcb-pointer
00304                         nip_mem_free( tcb->snd_buf );
00305                         nip_mem_free( tcb->rcv_buf );
00306                         nip_mem_release_block( nip_tcp_conns );
00307 
00308                 }
00309 
00310                 // failed to set up connection?
00311                 // -> cancel operation, reset size of tcb-array which will clip off the
00312                 // newly created tcb.
00313                 nip_mem_set_used( nip_tcp_conns, nip_tcp_conn_cnt * sizeof( struct nip_tcb ) );
00314         }
00315 
00316 
00317         return NULL;
00318 }
00319 
00320 /** Close connection or remove socket if not connected.
00321 */
00322 void nip_tcp_close( nip_tcp_sock_id_t connid )
00323 {
00324         struct nip_tcb  *tcb;
00325 
00326         // Stack functions operating outside the dispatcher must not be interrupted
00327 //      nip_int_off();
00328 
00329         // get pointer to connection TCB
00330         tcb = nip_tcp_find_conn( connid, NULL, 0 );
00331 
00332         // shutdown mode NIP_TCP_SOCK_FLG_SHUT_WR will actually lead to TCP doing
00333         // something. NIP_TCP_SOCK_FLG_SHUT_RD is just for the connection to know,
00334         // that the user won't perform read on it anymore.
00335         if (tcb == NULL && nip_error == NIP_E_INVALID_SOCK)
00336         {
00337                 // no open connection found for connid?
00338                 // -> Try to remove connection data. Socket will be removed unless
00339                 // there are still connections depending on it.
00340                 nip_tcp_del_conn( connid );
00341         }
00342         else if ( tcb != NULL )
00343         {
00344                 // Close connection
00345 
00346                 if ( tcb->status == NIP_TCP_STAT_SYN_SENT
00347                         || tcb->status == NIP_TCP_STAT_LISTEN )
00348                 {
00349                         tcb->status = NIP_TCP_STAT_CLOSED;
00350                 }
00351 
00352                 if ( tcb->status == NIP_TCP_STAT_SYN_RECEIVED
00353                 || tcb->status == NIP_TCP_STAT_ESTABLISHED )
00354                 {
00355                         tcb->status = NIP_TCP_STAT_FIN_WAIT1;
00356                 }
00357                 else
00358                 if ( tcb->status == NIP_TCP_STAT_CLOSE_WAIT )
00359                 {
00360                         tcb->status = NIP_TCP_STAT_LAST_ACK;
00361                 }
00362                 else
00363                         goto release;
00364 
00365                 // configure transmission for FIN to be sent
00366                 if ( nip_mem_buf_used( tcb->snd_buf ) == 0 )
00367                 {
00368                         tcb->snd_nxt ++;
00369                         tcb->flags   &= ~NIP_TCP_SOCK_FLG_SEND;
00370                 }
00371         }
00372 
00373         if ( tcb != NULL )
00374         {
00375                 release:
00376                 tcb->flags |= NIP_TCP_SHUT_WR;
00377                 nip_mem_release_block( nip_tcp_conns );
00378 
00379                 // notify TCP to check on state changes
00380                 nip_disp_notify( NIP_DISP_CHECK_TCP );
00381 //              nip_int_on();
00382 
00383                 nip_dispatcher();
00384         }
00385 
00386 //      nip_int_on();
00387 }
00388 
00389 /** Looks for incoming connections on given listening socket.
00390  *
00391  */
00392 nip_tcp_sock_id_t nip_tcp_accept( nip_tcp_sock_id_t sockid )
00393 {
00394         nip_tcp_sock_cnt_t i;
00395         nip_tcp_sock_id_t  res = NIP_TCP_NO_SOCKET;
00396         struct nip_tcb     *tcb;
00397 
00398         // Stack functions operating outside the dispatcher must not be interrupted
00399 //      nip_int_off();
00400 
00401         for ( i = 0; i < nip_tcp_unaccepted_cnt; i++ )
00402         {
00403                 tcb = nip_tcp_find_conn( nip_tcp_unaccepted[i], NULL, 0 );
00404                 if ( tcb == NULL || (tcb->flags&NIP_TCP_SOCK_FLG_ACCEPTED) )
00405                 {
00406                         // invalid list entry? -> remove
00407                 }
00408                 else if ( tcb->sockid == sockid )
00409                 {
00410                         // found connection
00411                         res = tcb->connid;
00412                         tcb->flags |= NIP_TCP_SOCK_FLG_ACCEPTED;
00413                 }
00414                 else
00415                         goto next;
00416 
00417                 // remove entry from list
00418                 nip_tcp_unaccepted_cnt--;
00419                 nip_memmove( &nip_tcp_unaccepted[i],&nip_tcp_unaccepted[i+1],nip_tcp_unaccepted_cnt-i);
00420 
00421                 next:
00422                 if ( tcb != NULL )
00423                         nip_mem_release_block( nip_tcp_conns );
00424 
00425                 if ( res != NIP_TCP_NO_SOCKET )
00426                         break;
00427         }
00428 
00429 //      nip_int_on();
00430 
00431         return res;
00432 }
00433 
00434 /** Write data to connection
00435  * @return number of bytes written. If 0 was returned, nip_error will contain one
00436  * of the following error codes
00437  *  - NIP_E_EOF    socket has been closed and is now read-only
00438  *  - NIP_E_AGAIN  not enough space in send buffer or ressource locked
00439  *  - NIP_E_INVALID_SOCK given sockid did not match any open connection
00440  */
00441 nip_mem_size_t nip_tcp_write( nip_tcp_sock_id_t connid, uint8_t *buf, nip_mem_size_t size )
00442 {
00443         struct nip_tcb   *tcb;
00444         nip_mem_handle_t snd_buf;
00445 
00446 
00447         // Stack functions operating outside the dispatcher must not be interrupted
00448 //      nip_int_off();
00449 
00450         // get pointer to connection TCB
00451         tcb = nip_tcp_find_conn( connid, NULL, 0 );
00452         if ( tcb == NULL )
00453         {
00454                 // nip_error will be set by nip_tcp_find_conn
00455                 size = 0;
00456         }
00457 
00458         else
00459         {
00460                 // connection already closed?
00461                 if ( tcb->status == NIP_TCP_STAT_FIN_WAIT1
00462                 || tcb->status == NIP_TCP_STAT_FIN_WAIT2
00463                 || tcb->status == NIP_TCP_STAT_CLOSING
00464                 || tcb->status == NIP_TCP_STAT_TIMEWAIT
00465                 || tcb->status == NIP_TCP_STAT_LAST_ACK
00466                         )
00467                 {
00468                         nip_error = NIP_E_EOF;
00469                         size = 0;
00470                 }
00471                 snd_buf = tcb->snd_buf;
00472                 nip_mem_release_block( nip_tcp_conns );
00473 
00474                 if ( size > 0 && NIP_E_OK != nip_mem_write( snd_buf, buf, size ) )
00475                 {
00476                         nip_error = NIP_E_AGAIN;
00477                         size = 0;
00478                 }
00479         }
00480 
00481 //      nip_int_on();
00482 
00483         if ( size > 0 )
00484         {
00485                 // notify TCP to transmit data
00486                 nip_disp_notify( NIP_DISP_CHECK_TCP );
00487                 nip_dispatcher();
00488         }
00489 
00490         return size;
00491 }
00492 
00493 /** Read data from connection into given buffer
00494  * @return number of bytes copied. If 0 was returned, nip_error will contain one
00495  * of the following error codes
00496  *  - NIP_E_EOF    socket has been closed by remote peer
00497  *  - NIP_E_AGAIN  no data available or ressource locked
00498  *  - NIP_E_INVALID_SOCK given sockid did not match any open connection
00499  */
00500 nip_mem_size_t nip_tcp_read( nip_tcp_sock_id_t connid, uint8_t *buf, nip_mem_size_t size )
00501 {
00502         struct nip_tcb *tcb;
00503 
00504         // Stack functions operating outside the dispatcher must not be interrupted
00505 //      nip_int_off();
00506 
00507         // get pointer to connection TCB
00508         tcb = nip_tcp_find_conn( connid, NULL, 0 );
00509         if ( tcb == NULL )
00510         {
00511                 // nip_error will be set by nip_tcp_find_conn
00512                 size = 0;
00513         }
00514         else
00515         {
00516                 // read data
00517                 size = nip_mem_read( tcb->rcv_buf, buf, size );
00518 
00519                 // read all data from block?
00520                 if ( size == 0 && nip_error == NIP_E_EOF )
00521                 {
00522                         // if remote peer didn't close connection, try again later.
00523                         if ( tcb->status < NIP_TCP_STAT_ESTABLISHED || (tcb->flags & NIP_TCP_SOCK_FLG_RECV) )
00524                         {
00525                                 nip_error = NIP_E_AGAIN;
00526                         }
00527 
00528                         // socket closed by remote peer? Record this as "user has been notified"
00529                         else
00530                         {
00531 //                              nip_error = NIP_E_EOF;
00532                                 tcb->flags |= NIP_TCP_SHUT_RD;
00533                         }
00534                 }
00535                 nip_mem_release_block( nip_tcp_conns );
00536         }
00537 
00538 //      nip_int_on();
00539 
00540         return size;
00541 }
00542 
00543 /** Calculate TCP checksum. If trans->header is set the checksum will be
00544  * inserted there. Make sure neither trans->header nor trans->payload are locked
00545  * @return checksum in network byte order
00546  */
00547 uint16_t nip_tcp_checksum( nip_net_if_trans_t *trans )
00548 {
00549         uint16_t sum    = 0;
00550 //      uint16_t proto  = NIP_NUM_IP_PROTO_TCP;
00551         uint16_t tcplen = trans->header_size + trans->payload_size;
00552         uint16_t word;
00553         uint16_t *word_ptr; //NOTE don't worry, it's not being used uninitialized
00554         struct nip_tcp_header *head = nip_mem_obtain_ptr( trans->header  );
00555 
00556         // checkpos describes position in checksum calculation:
00557         // 0 -> srcaddr1-2
00558         // 1 -> srcaddr3-4
00559         // 2 -> dstaddr1-2
00560         // 3 -> dstaddr3-4
00561         // 4 -> proto
00562         // 5 -> tcp length
00563         // >=6 -> some header pos
00564         // >=6+headerlen -> payload
00565         uint8_t  checkpos = 0;
00566 
00567         do
00568         {
00569                 if      ( checkpos == 0 )
00570                         word_ptr = (uint16_t*)trans->params.ip.src_nw_addr;
00571                 else if ( checkpos == 2 )
00572                         word_ptr = (uint16_t*)trans->params.ip.dst_nw_addr;
00573 /*              else if ( checkpos == 4 )
00574                         word_ptr = &proto;*/
00575                 else if ( checkpos == 6 + trans->header_size )
00576                         word_ptr = nip_mem_obtain_ptr( trans->payload );
00577                 else if ( checkpos == 6 )
00578                         word_ptr = (uint16_t*)head;
00579 
00580                 if ( word_ptr == NULL )
00581                         break;
00582 
00583                 if ( checkpos == 4 )
00584                         word = NIP_IP_PROTO_TCP;
00585                 else if ( checkpos == 5 )
00586                         word = tcplen;
00587                 else if ( checkpos == 6 + tcplen -1 )
00588                         // uneven number of bytes in payload? -> pad last byte
00589                         word = ((uint8_t*)word_ptr)[0] << 8;
00590                 else
00591                         word = ntohs(*word_ptr);
00592                 sum += word;
00593                 if ( sum < word )
00594                         sum++;
00595 
00596                 word_ptr++;
00597                 if ( checkpos >= 6 )
00598                         checkpos++;
00599                 checkpos++;
00600 
00601         } while ( checkpos < tcplen + 6 );
00602 
00603         sum = htons(~sum);
00604 
00605         if ( head != NULL )
00606                 head->checksum = sum;
00607 
00608         nip_mem_release_block( trans->payload );
00609         nip_mem_release_block( trans->header  );
00610 
00611         return sum;
00612 }
00613 
00614 
00615 /** Dispatcher function to send TCP segment. The following dispatcher parameters
00616  * are expected to be configured:
00617  *   - nip_disp.next.tcp.sockid
00618  *   - nip_disp.next.tcp.flags
00619  */
00620 void nip_tcp_disp_send( void )
00621 {
00622         struct nip_tcb        *tcb;
00623         nip_net_if_trans_t    *trans;
00624         struct nip_tcp_header *head;
00625         uint8_t               rto;
00626         uint8_t               optlen     = 0; /** make sure it's multiples of 4 */
00627         uint16_t              new_data_size = 0;
00628         uint32_t              seq;
00629         nip_mem_size_t        snd_buf_size;
00630 
00631 
00632         tcb = nip_tcp_find_conn( nip_disp.next.tcp.sockid, NULL, 0 );
00633 
00634         if ( tcb == NULL )
00635                 goto quit;
00636 
00637         snd_buf_size = nip_mem_buf_used( tcb->snd_buf );
00638 
00639         // get transmission
00640         trans = nip_ip_route( tcb->rem_addr.ip, NIP_NET_NO_IF, NULL );
00641         if ( trans == NULL )
00642         {
00643                 // out of ressources or no route to host -> try again later
00644                 /// @todo if SYN flag is set and NO_ROUTE_TO_HOST error occured -> cancel
00645                 goto release_connection;
00646         }
00647 
00648         /// @todo Recognize failed connection attempts when ARP fails? Don't do
00649         /// automatic reset of transmission status. You have to implement some check
00650         /// function then, to reset transmission status later on.
00651         trans->flags.reset_status = 1;
00652 
00653         // build transmission header
00654         trans->header = nip_mem_alloc(
00655                 NIP_TCP_MIN_HEADER_SIZE,
00656                 NIP_TCP_MIN_HEADER_SIZE,
00657                 0,
00658                 NULL
00659         );
00660 
00661         if ( trans->header == NIP_MEM_NULL
00662           || nip_mem_write( trans->header, NULL, NIP_TCP_MIN_HEADER_SIZE ) != NIP_E_OK )
00663                 goto reset_transmission;
00664 
00665         head = nip_mem_obtain_ptr( trans->header );
00666         if ( head == NULL )
00667                 goto reset_transmission;
00668 
00669         trans->header_size  = NIP_TCP_MIN_HEADER_SIZE + optlen;
00670         trans->flags.free_payload = 0;
00671 
00672         // set header flags
00673         head->flags         = nip_disp.next.tcp.flags;
00674 
00675         if ( tcb->status != NIP_TCP_STAT_SYN_SENT )
00676         {
00677                 head->flags     |= NIP_TCP_ACK_FLAG;
00678         }
00679 
00680                 // synchronized connections are waiting for ACK.
00681         if ( tcb->status == NIP_TCP_STAT_SYN_RECEIVED
00682           || tcb->status == NIP_TCP_STAT_SYN_SENT
00683                 )
00684         {
00685                 head->flags  |= NIP_TCP_SYN_FLAG;
00686         }
00687         else
00688         // All data transmitted? Send FIN
00689         if ( snd_buf_size == 0
00690           && ( tcb->status == NIP_TCP_STAT_FIN_WAIT1
00691             || tcb->status == NIP_TCP_STAT_LAST_ACK
00692             || tcb->status == NIP_TCP_STAT_CLOSING
00693             || tcb->status == NIP_TCP_STAT_LAST_ACK )
00694                 )
00695         {
00696                 head->flags  |= NIP_TCP_FIN_FLAG;
00697         }
00698         else
00699         {
00700                 /// @todo implement check, that TCP segments don't become too large
00701                 /// --> mind tcb->snd_wnd and default-segment-size
00702                 // calculate how much new (not retransmitted) data will be sent
00703                 new_data_size = snd_buf_size - tcb->snd_nxt + tcb->snd_una;
00704         }
00705 
00706         /// @todo check if it's ok to set seq to snd_nxt for the default case?! If
00707         /// not: what other default value would seq get?
00708         seq           = tcb->snd_nxt;
00709 
00710         // send data?
00711         if ( (tcb->flags & NIP_TCP_SOCK_FLG_SEND) == 0 )
00712         {
00713                 trans->payload_size = 0;
00714                 trans->payload      = NIP_MEM_NULL;
00715         }
00716         else
00717         {
00718                 if ( head->flags & NIP_TCP_PSH_FLAG )
00719                         tcb->push_ptr = tcb->snd_nxt;
00720 
00721                 trans->payload      = tcb->snd_buf;
00722                 // this would be the else-case for the following retransmit condition. To
00723                 // save program memory we omit the else and live with little more
00724                 // processing time.
00725                 trans->payload_off  = snd_buf_size - new_data_size;
00726                 trans->payload_size = new_data_size;
00727 
00728                 // payload depends on retransmission
00729                 if ( tcb->rto <= nip_tickcount() )
00730                 {
00731                         trans->payload_off  = 0;
00732                         trans->payload_size = snd_buf_size;
00733                         seq                 = tcb->snd_una;
00734                         if ( tcb->snd_una < tcb->push_ptr )
00735                                 head->flags      = nip_disp.next.tcp.flags | NIP_TCP_PSH_FLAG;
00736                         tcb->rt_count++;
00737 
00738                         if ( tcb->push_ptr > seq )
00739                                 head->flags |= NIP_TCP_PSH_FLAG;
00740                 }
00741         }
00742 
00743         // Set retransmission timeout
00744         if ( seq == tcb->snd_una )
00745         {
00746                 rto = tcb->srtt * NIP_TCP_RTO_BETA;
00747                 if ( rto < NIP_TCP_RTO_LBOUND ) rto = NIP_TCP_RTO_LBOUND;
00748                 if ( rto > NIP_TCP_RTO_UBOUND ) rto = NIP_TCP_RTO_UBOUND;
00749                 tcb->rto = nip_tickcount() + rto;
00750                 tcb->rt_una = seq + trans->payload_size;
00751                 tcb->rt_count = 0;
00752         }
00753 
00754         // FIN and SYN are special messages, containing no data but increase snd_nxt
00755         // nevertheless for following messages. In this implementation snd_nxt has
00756         // been increased prior to calling tcp_disp_snd, so it won't interfere with
00757         // retransmissions. While waiting for SYN or FIN to be acked we'll still
00758         // send with the old sequence number, though.
00759         if ( head->flags & ( NIP_TCP_FIN_FLAG | NIP_TCP_SYN_FLAG ) )
00760                 seq           = tcb->snd_nxt-1;
00761 
00762         head->seq           = htonl( seq );
00763         head->srcport       = NIP_TCP_SOCKATTR(tcb->sockid,addr.port);
00764         head->dstport       = tcb->rem_addr.port;
00765         head->ack           = htonl( tcb->rcv_nxt );
00766         head->dataoffset    = (trans->header_size / 4 ) << 4;
00767         /// @todo adjust window size??
00768         head->window        = htons( tcb->rcv_wnd );
00769         /// @todo implement urgent pointer
00770 
00771         // calculate checksum
00772         head->checksum      = 0;
00773         nip_mem_release_block( trans->header );
00774 
00775         nip_tcp_checksum( trans );
00776 
00777         // increment snd_nxt-pointer
00778         tcb->snd_nxt    += new_data_size;
00779 
00780         // IP Send
00781         trans->params.ip.protocol = NIP_IP_PROTO_TCP;
00782         trans->status   = NIP_NET_IF_RESOLV_ADDR;
00783         nip_disp.next.common.trans = trans;
00784         NIP_CURR_CMD    = & nip_ip_disp_send;
00785 
00786         goto release_connection;
00787 
00788 reset_transmission:
00789         trans->payload = NIP_MEM_NULL;
00790         nip_mem_release_block( trans->header );
00791         nip_reset_trans( trans );
00792 
00793 release_connection:
00794         nip_mem_release_block( nip_tcp_conns );
00795 
00796 quit:
00797         return;
00798 }
00799 
00800 
00801 /** Dispatcher function to receive TCP segment.
00802  * Requires nip_disp.next.common.trans to point to valid transmission
00803  */
00804 void nip_tcp_disp_receive( void )
00805 {
00806         nip_net_if_trans_t    *trans = nip_disp.next.common.trans;
00807 //      nip_time_t            tc     = nip_tickcount();
00808         uint8_t               flags;
00809         uint16_t              wnd;
00810         uint32_t              seq;
00811         uint32_t              ack;
00812         uint32_t              nxt;   // next seq after current segment
00813         uint32_t              eownd; // end of receive window. NOTE Don't worry, it's not being used uninitalized
00814         nip_mem_size_t        seglen;
00815         nip_mem_size_t        hlen;  // tcp header length
00816         nip_mem_size_t        payoff;// payload offset in payload buffer
00817         nip_mem_size_t        rcvbuf_size;
00818         nip_mem_handle_t      rcvbuf;
00819         struct nip_tcp_header *head;
00820         struct nip_tcb        *tcb = NULL;
00821         uint8_t               tmp;
00822         nip_mem_handle_t      hbuf;  // header buffer
00823 #if NIP_TCP_MAX_SOCKETS > 1
00824         nip_tcp_sock_id_t sockid;
00825 #endif
00826 
00827         // sanity check of payload
00828         if ( trans->payload_size < NIP_TCP_MIN_HEADER_SIZE )
00829                 goto discard;
00830 
00831         if  ( trans->header != NIP_MEM_NULL )
00832                 hbuf = trans->header;
00833         else
00834                 hbuf = trans->payload;
00835         head = nip_mem_obtain_ptr( hbuf );
00836 
00837         if ( head == NULL )
00838                 goto discard;
00839 
00840         // check packet
00841         hlen   = (head->dataoffset>>4) * 4;
00842         seglen = trans->payload_size;
00843         if ( hbuf == trans->payload )
00844         {
00845                 if ( hlen > trans->payload_size )
00846                 {
00847                         nip_mem_release_block( hbuf );
00848                         goto discard;
00849                 }
00850                 seglen -= hlen;
00851         }
00852         ///\todo check checksum
00853 
00854         // cache some often needed header variables
00855         trans->params.tcp.src_port = head->srcport;
00856         trans->params.tcp.dst_port = head->dstport;
00857         flags   = head->flags;
00858         wnd     = ntohs( head->window );
00859         seq     = ntohl( head->seq );
00860         ack     = ntohl( head->ack );
00861         nxt     = seq + seglen;
00862 
00863 
00864         // Release and discard header. We saved all we need. Make sure to not read
00865         // from payload buffer, that shall not be freed
00866         nip_mem_release_block( hbuf );
00867         payoff = 0;
00868         if ( hbuf != trans->payload || trans->flags.free_payload == 1 )
00869         {
00870                 nip_mem_read( hbuf, NULL, hlen );
00871         }
00872         else if ( hbuf == trans->payload )
00873         {
00874                 payoff = hlen;
00875         }
00876         trans->payload_size = payoff + seglen;
00877 
00878 
00879         // check if we have socket on destination port
00880 #if NIP_TCP_MAX_SOCKETS > 1
00881         sockid = 0;
00882         do
00883         {
00884 #endif
00885                 if ( NIP_TCP_SOCKATTR(sockid,addr).port == trans->params.tcp.dst_port
00886                   && ( nip_memcmp( NIP_TCP_SOCKATTR(sockid,addr).ip, trans->params.ip.dst_nw_addr, 4 ) == 0
00887                     || nip_memcmp( NIP_TCP_SOCKATTR(sockid,addr).ip, nip_ip_null, 4 ) == 0 )
00888                    )
00889                         goto socketfound;
00890 #if NIP_TCP_MAX_SOCKETS > 1
00891         } while ( ++sockid < NIP_TCP_MAX_SOCKETS );
00892 #endif
00893         // socket not found? Discard SYN attempts, Reset everything else.
00894         if ( flags & NIP_TCP_SYN_FLAG )
00895                 goto discard;
00896         else
00897                 goto reset;
00898 
00899         socketfound:
00900 
00901         // Read header from payload buffer
00902 
00903         // check for matching connection on that socket
00904         tcb = nip_tcp_find_conn( sockid, trans->params.ip.src_nw_addr, trans->params.tcp.src_port );
00905 
00906         // P R O C E S S   R E Q U E S T
00907 
00908         // configure parameters for possible replies
00909         nip_disp.next.tcp.flags  = 0;
00910         nip_disp.next.tcp.sockid = NIP_TCP_NO_SOCKET;
00911 
00912         if ( tcb != NULL )
00913         {
00914                 eownd = tcb->rcv_nxt + tcb->rcv_wnd;
00915 
00916                 // check if we received RESET
00917                 if ( flags & NIP_TCP_RST_FLAG && tcb->status != NIP_TCP_STAT_LISTEN )
00918                 {
00919                         // check validity of received RESET
00920                         if ( tcb->status == NIP_TCP_STAT_SYN_SENT )
00921                         {
00922                                 if ( tcb->snd_nxt != ack )
00923                                         goto release_conns;
00924                         }
00925                         else if ( seq < tcb->rcv_nxt || seq > eownd )
00926                                 goto release_conns;
00927 
00928                         // valid? -> reset connection
00929                         tcb->status = NIP_TCP_STAT_CLOSED;
00930                         nip_mem_release_block( nip_tcp_conns );
00931                         goto release_conns;
00932                 }
00933         }
00934 
00935         // Update connection
00936         if ( flags & NIP_TCP_ACK_FLAG )
00937         {
00938                 // Received segment is ACKing a non-existent connetion?
00939                 // -> Tell remote host to reset his connection.
00940                 if ( tcb == NULL || tcb->status == NIP_TCP_STAT_LISTEN )
00941                         goto reset;
00942 
00943                 // received acceptable ACK?
00944                 if ( tcb->snd_una < ack && ack <= tcb->snd_nxt )
00945                 {
00946                         // remove acked data from transmission buffer
00947                         if ( tcb->flags & NIP_TCP_SOCK_FLG_SEND )
00948                         {
00949                                 nip_mem_read( tcb->snd_buf, NULL, ack - tcb->snd_una );
00950 
00951                                 // Send buffer emptied? -> check FIN states
00952                                 if ( nip_mem_buf_used( tcb->snd_buf ) == 0
00953                                  &&  tcb->snd_una != tcb->snd_nxt
00954                                  &&  ( tcb->status == NIP_TCP_STAT_FIN_WAIT1
00955                                     || tcb->status == NIP_TCP_STAT_LAST_ACK )
00956                                         )
00957                                 {
00958                                         // increment snd_nxt and send FIN
00959                                         tcb->flags   &= ~NIP_TCP_SOCK_FLG_SEND;
00960                                         tcb->snd_nxt++;
00961                                         NIP_CURR_CMD             = &nip_tcp_disp_send;
00962                                 }
00963                         }
00964 
00965                         tcb->snd_una = ack;
00966 
00967                         // check retransmission queue
00968                         if ( ack >= tcb->rt_una )
00969                         {
00970                                 /// @todo delete top of queue. Current implementation has no queue
00971                                 /// and therefor assumes next item to be snd_nxt, which is not the
00972                                 /// case if there have been multiple transmissions since. Also there
00973                                 /// is nothing known about the original transmission time of the
00974                                 /// next segment, to assume the current time is wrong. We're doing
00975                                 /// that to save program memory, as TCP will also work this way. It
00976                                 /// might just perform slower, as retransmissions of subsequent
00977                                 /// segments may be delayed.
00978                                 tcb->rt_una = tcb->snd_nxt;
00979                                 tcb->rto    = nip_tickcount() + NIP_TCP_RTO_LBOUND;
00980 
00981                                 /// @todo perform roundtriptime calculation here
00982                         }
00983 
00984                 }
00985 
00986                 // if data has been received, update remote peer with current status
00987                 if ( seglen > 0 )
00988                         NIP_CURR_CMD             = &nip_tcp_disp_send;
00989 
00990                 // received valid data?
00991                 if ( (tcb->rcv_wnd == 0 && seglen == 0 && tcb->rcv_nxt == seq)
00992                   || ( tcb->rcv_wnd >  0
00993                     && ( (seq <= tcb->rcv_nxt )
00994                       && (tcb->rcv_nxt <= nxt && nxt <= eownd )
00995                        )
00996                           )
00997                         )
00998                 {
00999                         // make sure connection is not synchronizing or closing
01000                         if ( tcb->flags & NIP_TCP_SOCK_FLG_RECV )
01001                         {
01002                                 // Try to add data to socket. This may fail if receive-buffer
01003                                 // is too small and can also not be increased due to locked buffers.
01004                                 // To minimize that risk, we release (unlock) the connection buffer
01005                                 // so it won't interrupt that operation.
01006                                 rcvbuf      = tcb->rcv_buf;
01007                                 rcvbuf_size = nip_mem_buf_used( tcb->rcv_buf );
01008                                 // don't read bytes, that have already been received
01009                                 payoff += tcb->rcv_nxt - seq;
01010 
01011                                 if ( (tcb->flags & NIP_TCP_SOCK_FLG_SHUT_RD) == 0)
01012                                 {
01013                                         nip_mem_release_block( nip_tcp_conns );
01014 
01015                                         if ( trans->flags.free_payload == 1 )
01016                                         {
01017                                                 // move data from transmission payload to socket buffer.
01018                                                 // first dump data we have already received
01019                                                 nip_mem_read( trans->payload, NULL, payoff );
01020                                                 seglen = nip_mem_move( rcvbuf, trans->payload, seglen - payoff );
01021                                         }
01022                                         else
01023                                         {
01024                                                 // determine number of bytes to be copied
01025                                                 seglen = trans->payload_size - payoff;
01026 
01027                                                 // perform slow copy operation
01028                                                 do
01029                                                 {
01030                                                         if ( 1 != nip_mem_read_at_pos( trans->payload, &tmp, 1, payoff++ )
01031                                                         || NIP_E_OK != nip_mem_write( rcvbuf, &tmp, 1 ) )
01032                                                         {
01033                                                                 break;
01034                                                         }
01035                                                 } while ( payoff < trans->payload_size );
01036 
01037                                                 // subtract number of bytes not copied, so we won't ACK those
01038                                                 seglen -= (trans->payload_size - payoff);
01039                                         }
01040 
01041                                         tcb = nip_tcp_find_conn( sockid, trans->params.ip.src_nw_addr, trans->params.tcp.src_port );
01042                                         // since we just had access to the TCB there should be no problem
01043                                         // accessing it again. If we do that's a programming error in the
01044                                         // stack.
01045                                         if ( tcb == NULL )
01046                                         {
01047                                                 // remove the newly read data from the receive buffer, because
01048                                                 // we're unable to update the TCB
01049                                                 nip_mem_set_used( rcvbuf, rcvbuf_size );
01050                                                 goto discard;
01051                                         }
01052                                 }
01053 
01054                                 tcb->rcv_nxt     += seglen;
01055 
01056                                 /// @TODO update receive window?
01057 
01058                         }
01059                 }
01060         }
01061 
01062         // synchronize
01063         if ( flags & NIP_TCP_SYN_FLAG )
01064         {
01065                 // No connection found? attempt to create new connection
01066                 if ( tcb == NULL
01067                   && (NIP_TCP_SOCKATTR(sockid,flags) & NIP_TCP_SOCK_FLG_LISTENING)
01068                   &&  nip_tcp_unaccepted_cnt < NIP_TCP_MAX_UNACCEPTED )
01069                 {
01070                         tcb = nip_tcp_create_conn(
01071                                 sockid,
01072                                 trans->params.ip.src_nw_addr,
01073                                 trans->params.tcp.src_port,
01074                                 NIP_TCP_STAT_LISTEN
01075                         );
01076                         tcb->flags = NIP_TCP_SOCKATTR(sockid,flags);
01077                         nip_tcp_unaccepted[nip_tcp_unaccepted_cnt] = tcb->connid;
01078                         nip_tcp_unaccepted_cnt++;
01079                 }
01080                 // we should now have a pointer to a valid connection control block for
01081                 // the incoming packet. If we don't, we ran out of ressources and discard.
01082                 if ( tcb == NULL )
01083                         goto discard;
01084 
01085                 // update connection status for LISTEN or SYN_SENT connections
01086                 if ( tcb->status == NIP_TCP_STAT_LISTEN
01087                   || tcb->status == NIP_TCP_STAT_SYN_SENT )
01088                 {
01089                         tcb->irs     = seq;
01090                         tcb->rcv_nxt = tcb->irs + 1;
01091                         // wl1 and wl2 will help to prevent using old segments to update
01092                         // the window (snd_wnd)
01093                         tcb->snd_wl1 = seq;
01094                         tcb->snd_wl2 = ack;
01095                         tcb->snd_wnd = wnd;
01096 
01097                         tcb->status  = NIP_TCP_STAT_SYN_RECEIVED;
01098                         ///@todo check options
01099                         ///@todo tcb->snd_maxsegsize = ?
01100                 }
01101 
01102                 // Reply to any SYN with current system state, even if SYN was
01103                 // unexpected.
01104                 NIP_CURR_CMD             = &nip_tcp_disp_send;
01105         }
01106 
01107         // received last data?
01108         if  ( flags & NIP_TCP_FIN_FLAG )
01109         {
01110                 if ( tcb == NULL )
01111                         goto reset;
01112 
01113                 // check that we've received everything we should
01114                 if ( tcb->rcv_nxt == seq )
01115                 {
01116 
01117                         // setup connection to not receive any more data
01118                         tcb->flags &= ~NIP_TCP_SOCK_FLG_RECV;
01119 
01120                         if ( tcb->status == NIP_TCP_STAT_ESTABLISHED )
01121                                 tcb->status = NIP_TCP_STAT_CLOSE_WAIT;
01122 
01123                         if ( tcb->status == NIP_TCP_STAT_FIN_WAIT1 )
01124                                 tcb->status = NIP_TCP_STAT_CLOSING;
01125 
01126                         if ( tcb->status == NIP_TCP_STAT_FIN_WAIT2 )
01127                         {
01128                                 tcb->timeout = nip_tickcount() + NIP_TCP_MSL;
01129                                 tcb->status  = NIP_TCP_STAT_TIMEWAIT;
01130                         }
01131 
01132                         tcb->rcv_nxt++;
01133                 }
01134 
01135                 // acknowledge FIN
01136                 NIP_CURR_CMD = &nip_tcp_disp_send;
01137         }
01138 
01139 
01140         // Perform state transitions after last data has been acked
01141         if ( tcb->snd_una == tcb->snd_nxt )
01142         {
01143                 if ( tcb->status == NIP_TCP_STAT_SYN_RECEIVED )
01144                 {
01145                         tcb->status = NIP_TCP_STAT_ESTABLISHED;
01146                         tcb->flags |= NIP_TCP_SOCK_FLG_SEND | NIP_TCP_SOCK_FLG_RECV;
01147                 }
01148 
01149                 if ( tcb->status == NIP_TCP_STAT_CLOSING )
01150                 {
01151                         tcb->timeout = nip_tickcount() + NIP_TCP_MSL;
01152                         tcb->status  = NIP_TCP_STAT_TIMEWAIT;
01153                 }
01154 
01155                 if ( tcb->status == NIP_TCP_STAT_FIN_WAIT1 )
01156                         tcb->status = NIP_TCP_STAT_FIN_WAIT2;
01157 
01158                 if ( tcb->status == NIP_TCP_STAT_LAST_ACK )
01159                         tcb->status = NIP_TCP_STAT_CLOSED;
01160         }
01161 
01162         if ( tcb != NULL )
01163                 nip_disp.next.tcp.sockid = tcb->connid;
01164 
01165 
01166         release_conns:
01167                 nip_mem_release_block( nip_tcp_conns );
01168 
01169 
01170         // D I S C A R D
01171         discard:
01172                 trans->status = NIP_NET_IF_DONE;
01173                 nip_reset_trans( trans );
01174                 return;
01175 
01176 
01177         // S E N D   R E S E T
01178         reset:
01179 
01180                 if ( tcb != NULL )
01181                         nip_mem_release_block( nip_tcp_conns );
01182 
01183                 // send RST segment based on current transmission
01184 
01185                 trans->payload_size = 0;
01186 
01187                 // get pointer to header
01188                 if ( trans->header == NIP_MEM_NULL )
01189                 {
01190                         // build transmission header
01191                         trans->header = nip_mem_alloc(
01192                                 NIP_TCP_MIN_HEADER_SIZE,
01193                                 NIP_TCP_MIN_HEADER_SIZE,
01194                                 0,
01195                                 NULL
01196                         );
01197 
01198                 }
01199                 else
01200                 {
01201                         // use given payload buffer for header, as we're not sending any payload
01202                         nip_mem_set_used( trans->header, 0 );
01203                 }
01204 
01205                 // Fill header buffer with Zeros
01206                 if ( trans->header == NIP_MEM_NULL
01207                 || nip_mem_write( trans->header, NULL, NIP_TCP_MIN_HEADER_SIZE ) != NIP_E_OK )
01208                 {
01209                         goto discard;
01210                 }
01211 
01212                 head = nip_mem_obtain_ptr( trans->header );
01213                 if ( head == NULL )
01214                 {
01215                         goto discard;
01216                 }
01217 
01218                 trans->header_size  = NIP_TCP_MIN_HEADER_SIZE;
01219 
01220                 // write header
01221                 // swap ports from original transmission
01222                 nip_memcpy( trans->params.tcp.dst_nw_addr, trans->params.tcp.src_nw_addr, 4 );
01223                 head->srcport    = trans->params.tcp.dst_port;
01224                 head->dstport    = trans->params.tcp.src_port;
01225                 head->dataoffset = NIP_TCP_MIN_DATA_OFFSET;
01226                 head->flags      = NIP_TCP_RST_FLAG;
01227                 head->ack        = NIP_TCP_RESET_ACK;
01228                 if ( flags & NIP_TCP_ACK_FLAG )
01229                         head->seq        = htonl(ack);
01230 
01231                 nip_mem_release_block( trans->header );
01232 
01233                 trans = nip_ip_route( trans->params.tcp.dst_nw_addr, NIP_NET_NO_IF, trans );
01234                 if ( trans == NULL )
01235                 {
01236                         // no route to host? whatever... it's just a reset we're losing.
01237                         goto discard;
01238                 }
01239                 nip_tcp_checksum( trans );
01240 
01241                 // IP Send
01242                 trans->params.ip.protocol = NIP_IP_PROTO_TCP;
01243                 trans->status   = NIP_NET_IF_RESOLV_ADDR;
01244                 NIP_CURR_CMD    = & nip_ip_disp_send;
01245 
01246                 return;
01247 }
01248 
01249 /** dispatcher function to remove closed connection
01250  */
01251 void nip_tcp_disp_close( void )
01252 {
01253         nip_tcp_del_conn( nip_disp.next.tcp.sockid );
01254 }
01255 
01256 
01257 
01258 /** Dispatcher function to check TCP connection states/retransmissions/..
01259  */
01260 void nip_tcp_disp_check( void )
01261 {
01262         struct nip_tcb     *tcb;
01263         nip_tcp_sock_cnt_t cnt = 0;
01264         nip_mem_size_t     used;
01265 
01266         if ( nip_tcp_conn_cnt > 0 )
01267         {
01268                 // get pointer to tcp array
01269                 tcb = nip_mem_obtain_ptr( nip_tcp_conns );
01270                 if ( tcb != NULL )
01271                 {
01272                         // check all tcp connections
01273                         do
01274                         {
01275                                 // if we need to move on before all all connections have been
01276                                 // checked, notify dispatcher to come back.
01277                                 if ( NIP_CURR_CMD != NULL )
01278                                 {
01279                                         nip_disp_notify( NIP_DISP_CHECK_TCP );
01280                                         break;
01281                                 }
01282 
01283                                 nip_disp.next.tcp.flags  = 0;
01284                                 nip_disp.next.tcp.sockid = tcb->connid;
01285 
01286                                 used = nip_mem_buf_used( tcb->snd_buf );
01287 
01288                                 // check for closed connections
01289                                 if ( tcb->timeout >= nip_tickcount()
01290                                   && tcb->status == NIP_TCP_STAT_TIMEWAIT )
01291                                 {
01292                                         tcb->status = NIP_TCP_STAT_CLOSED;
01293                                 }
01294 
01295                                 if ( tcb->status == NIP_TCP_STAT_CLOSED
01296                                   && tcb->flags & NIP_TCP_SOCK_FLG_AUTOCLOSE )
01297                                 {
01298                                         NIP_CURR_CMD = &nip_tcp_disp_close;
01299                                 }
01300                                 else
01301                                 // check for data or FIN that needs to be transmitted
01302                                 if ( (used  > 0 && used > tcb->snd_nxt - tcb->snd_una)
01303                                   || (used == 0
01304                                     && ( tcb->status == NIP_TCP_STAT_FIN_WAIT1
01305                                       || tcb->status == NIP_TCP_STAT_LAST_ACK )
01306                                      )
01307                                   || ( tcb->snd_nxt > tcb->snd_una && tcb->rto <= nip_tickcount() )
01308                                         )
01309                                         ///@todo insert congestion control
01310                                 {
01311                                         NIP_CURR_CMD = &nip_tcp_disp_send;
01312                                 }
01313                                 tcb++;
01314                         } while ( ++cnt < nip_tcp_conn_cnt );
01315 
01316                         // done? -> free connection pointer
01317                         nip_mem_release_block( nip_tcp_conns );
01318                 }
01319         }
01320 
01321 }
01322 
01323 #endif /* NIP_TCP_ENABLE */

Generated on Thu Jul 10 01:09:29 2008 for NIP by  doxygen 1.5.5