src/vfer_api.c

Go to the documentation of this file.
00001 /*
00002  * Copyright 2005, 2006, Internet2
00003  * Legal conditions are in file LICENSE
00004  * (MD5 = c434f2e53b8089d8b4d0172c7ce07360).
00005  */
00006 /**
00007  *
00008  * @file   vfer_api.c
00009  * @author Ivan Beschastnikh
00010  * @brief  Implements the api calls that can be made to the library.
00011  *
00012  * The application may only call the functions prototyped in
00013  * api.h. The API has been written by Steven Senger of Internet2, and
00014  * can be found online at
00015  * http://transport.internet2.edu/transport-api-06.pdf
00016  *
00017  * -     05/08-11/06     ivan            Moved to using vfer_fd data type for all api calls, mane changes due to new threading model
00018  * -     04/30/06        ivan            implemented vfer_recvfile and vfer_sendfile
00019  * -     03/25/06        ivan            added debugging support for congestion control (ccontrol.c) in the form of DEBUG_CCTL var in vfer_debug
00020  * -     12/29/05        ivan            added VFERSO_SNDSIZE support to control packet|frag size dynamically with vfer_setsockopt
00021  *                                       added nonblocking socket vfer_connect() functionality, moved all connect logic to Control_ConnectT @ control.c
00022  * -     12/28/05        ivan            tsci2 integration ; vfer_setsockopt and vfer_sendfile bug fixes
00023  * -     10/17/05        ivan            added vfer_debug function to specify level of debug verbosity at runtime
00024  * -     10/13/05        ivan            now includes a main page blurb for doxygen created docs
00025  * -     09/01/05        ivan            modified to use new packet structures
00026  * -     08/28/05        ivan            added recv/send buffer setting and udp checksum enable checking in vfer_bind
00027  * -     08/05/05        ivan            changed vfer_connect to release socket correctly on error and support nonblocking vfer_sockets
00028  * -     07/28/05        ivan            c_control hooks to vfer{accept,connect,send,recv}, debug printing for all api calls added
00029  * -     07/18,19,20/05  ivan            select related function implementations
00030  * -     06/29/05        ivan            migration to the api specified in I2's writeup (version 6), all doxygened
00031  * -     06/10/05        ivan            renamed to api.[c,h] (from protocol.[c,h]) ; wrote Protocol_Init code & Accept code for barebones connection
00032  * -     05/24/05        ivan            initially added to app_api layer & recoded for this
00033  * -     05/18/05        ivan            created
00034  */
00035 
00036 /** \mainpage VFER
00037  *
00038  * \section intro_sec Introduction
00039  *
00040  * VFER is a functionally reliable, congestion controlled transport
00041  * protocol. It is connection based, bidirectional and is meant to
00042  * work on top of IP. The protocol is TCP friendly, and is
00043  * functionally reliable. This documentation is generated for a user
00044  * level prototype implementation of VFER written in C.
00045  *
00046  * For more information regarding VFER or this implementation please
00047  * visit the project's homepage: http://vfer.sf.net
00048  */
00049 
00050 #include "vfer_api.h"
00051 
00052 /*
00053  * controls whether the protocol has been initialized
00054  * or not (tested by a macro defined in api.h)
00055  */
00056 static int Initialized = -1;
00057 static int Debug_Initialized = -1;
00058 
00059 /* an internal function used for initializeation of components, etc */
00060 static int vfer_internal_init();
00061 
00062 /*
00063  * test is the protocol has been initialized and if it hasn't, calls
00064  * vfer_internal_init() to initialize all layers of the protocol
00065  */
00066 #define PROTOCOL_INIT_TEST ({ \
00067         if (Initialized == -1) \
00068                 vfer_internal_init(); \
00069         -1;})
00070 
00071 /* Begin I2 api Version 6 */
00072 
00073 /**
00074  * Return a newly reserved vfer socket identifier
00075  *
00076  * In case the call fails the error status can be obtained through
00077  * vfer_sockstatus(). Only socktype of type SOCK_DGRAM is supported
00078  * right now.
00079  *
00080  * @param socktype a SOCK_STREAM or SOCK_DGRAM
00081  * @return
00082  *      - a vfer_fd identifier >= 0
00083  *      - a negative vfer_fd identifier varrying the error code:
00084  *        - VFER_BADTYPE invalid socktype error
00085  *        - VFER_IMPL underlying socket descriptor failure
00086  */
00087 vfer_fd vfer_socket(int socktype) {
00088         int sockfd, icmp_fd;
00089         int i;
00090         char is_df_set;
00091         vfer_sock* sock;
00092 
00093         if (!PROTOCOL_INIT_TEST) return VFER_IMPL;
00094         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_socket", "Entered");
00095 
00096         if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) {
00097                 return VFER_BADTYPE;
00098         }
00099 
00100         if (socktype == SOCK_STREAM) {
00101                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_socket", "only socktype of SOCK_DGRAM supported");
00102                 return VFER_BADTYPE;
00103         }
00104 
00105         for (i = 0; i < MAX_SOCKETS; i++) {
00106                 pthread_mutex_lock(&(sockets.s[i].mutex));
00107                 if (sockets.s[i].state == CONN_DISCONNECTED) {
00108                         sock = &(sockets.s[i]);
00109                         sock->state = CONN_ACQUIRED;
00110                         pthread_mutex_unlock(&(sockets.s[i].mutex));
00111                         break;
00112                 }
00113                 pthread_mutex_unlock(&(sockets.s[i].mutex));
00114         }
00115         if (i == MAX_SOCKETS) {
00116                 return VFER_IMPL;
00117         }
00118 
00119         Datagrams_Clear(&(sock->recvd_frames));
00120 
00121         if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
00122                 perror("vfer_socket :: socket");
00123                 pthread_mutex_lock(&(sock->mutex));
00124                 sock->state = CONN_DISCONNECTED;
00125                 pthread_mutex_unlock(&(sock->mutex));
00126                 return VFER_IMPL;
00127         }
00128 
00129         if (fcntl(sockfd, F_SETFD, O_NONBLOCK) == -1) {
00130                 perror("vfer_socket :: fcntl");
00131                 pthread_mutex_lock(&(sock->mutex));
00132                 sock->state = CONN_DISCONNECTED;
00133                 pthread_mutex_unlock(&(sock->mutex));
00134                 return VFER_IMPL;
00135         }
00136 
00137         /* PMTU: Set DF bit */
00138         SET_DF_BIT(sockfd, is_df_set);
00139         if(is_df_set < 0){
00140                 sock->is_df_set = 0;
00141                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_socket", "Unable to set DF bit. Continuing without PMTUD");
00142         }else{
00143                 sock->is_df_set = 1;
00144         }
00145 
00146         /* PMTU: Create socket for ICMP messages. Useful but not required for PMTU discovery. */
00147         if(sock->is_df_set == 0){
00148                 icmp_fd = -1;
00149                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_socket", "DF bit not set so ICMP not needed.");
00150         }else if ((icmp_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) == -1) {
00151                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_socket", "Unable to create ICMP socket. Continuing anyways");
00152         }else if (fcntl(icmp_fd, F_SETFD, O_NONBLOCK) == -1) {
00153                 icmp_fd = -1;
00154                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_socket", "Unable to remove blocking on ICMP socket. Continuing without ICMP.");
00155         }else{
00156                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_socket", "ICMP socket created.");
00157         }
00158 
00159         sock->fd = sockfd;
00160         sock->icmp_fd = icmp_fd;
00161         sock->type = socktype;
00162         sock->err = 0;
00163         sock->opts = 0;
00164         sock->state = CONN_ACQUIRED;
00165         sock->select_mark = 0;
00166         sock->select_result = 0;
00167         sock->select_active = 0;
00168         sock->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
00169         sock->close_timeout = 0;
00170         sock->accept_timeout = 0;
00171         sock->snd_timeout = 0;
00172         sock->rcv_timeout = 0;
00173         return i;
00174 } /* vfer_sock() */
00175 
00176 
00177 /**
00178  * Closes the socket identified by a vfer identifier.
00179  *
00180  * This call returns immediately without changing the error status
00181  * of sock if sock is marked with an error on entry.
00182  *
00183  * @param vfd vfer socket identifier
00184  * @return
00185  *      - 0 on success
00186  *      - VFER_BADSOCK bad vfd argument
00187  *      - VFER_IMPL failure closing underlying socket descriptor
00188  */
00189 int vfer_close(vfer_fd vfd) {
00190         vfer_sock* sock;
00191 
00192         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
00193         sock = &(sockets.s[vfd]);
00194         if (sock->err != 0) { return VFER_BADSOCK; }
00195 
00196         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_close", "Entered vfd[%d]", vfd);
00197 
00198         pthread_mutex_lock(&(sock->mutex));
00199         switch (sock->state) {
00200         case CONN_DISCONNECTED:
00201         case CONN_DISCONNECTING:
00202                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_close", "Socket already disconnected or disconnecting");
00203                 pthread_mutex_unlock(&(sock->mutex));
00204                 return VFER_BADSOCK;
00205         case CONN_CONNECTED:
00206                 /* Control_Close() changes the state of the socket, make sure not to alter afterwards! */
00207                 Control_Close(sock);
00208                 break;
00209         case CONN_LISTENING:
00210                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_close", "Closing listening socket");
00211                 Accept_Queue_Delete(sock->accept_queue);
00212                 sock->state = CONN_DISCONNECTED;
00213                 pthread_mutex_unlock(&(sock->mutex));
00214                 Control_Unregister_Socket(sock);
00215                 return 0;
00216         default:
00217                 close(sock->fd);
00218                 sock->state = CONN_DISCONNECTED;
00219         }
00220         pthread_mutex_unlock(&(sock->mutex));
00221         return 0;
00222 } /* vfer_close() */
00223 
00224 
00225 /**
00226  * Establishes a connection with a peer.
00227  *
00228  * This works equally for a SOCK_DGRAM or SOCK_STREAM (if
00229  * implemented): attempts a connection operation with the peer defined
00230  * by addr and len. This call returns immediately without changing the
00231  * error status of sock if sock is marked with an error on entry. If
00232  * the socket is nonblocking then this call returns as soon as
00233  * possible (connection will happen in the background ie. with the
00234  * ControlT thread). A vfer_select() call can then determine if the
00235  * connection succeded or not. For a nonblocking socket, this call
00236  * returns VFER_INPROGRESS.
00237  *
00238  * @param vfd vfer socket identifier
00239  * @param serv_addr a peer address
00240  * @param addrlen length of addr
00241  * @return
00242  *      - 0 on success
00243  *      - VFER_BADSOCK bad vfd argument
00244  *      - VFER_TIMEOUT connection attempt timedout
00245  *      - VFER_REFUSED connection attempt refused
00246  *      - VFER_NOCONN socket is not able to perform a connect.
00247  *      - VFER_INPROGRESS only returned for nonblocking socket- indicates success
00248  */
00249 int vfer_connect(vfer_fd vfd, const struct sockaddr *serv_addr, int addrlen) {
00250         vfer_sock* sock;
00251         int ret;
00252 
00253         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
00254         sock = &(sockets.s[vfd]);
00255         if (sock->err != 0) { return VFER_BADSOCK; }
00256 
00257         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_connect", "Entered");
00258 
00259         /***************/
00260         pthread_mutex_lock(&(sock->mutex));
00261         if (sock->state != CONN_BOUND) {
00262                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_connect", "socket not bound");
00263                 pthread_mutex_unlock(&(sock->mutex));
00264                 return VFER_BADSOCK;
00265         }
00266         memcpy(&sock->addr, serv_addr, addrlen);
00267         sock->addr_len = sizeof(sock->addr);
00268 
00269         if ((ret = Control_Connect(sock)) == -1) {
00270                 pthread_mutex_unlock(&(sock->mutex));
00271                 return sock->err;
00272         } else if (ret == -2) {
00273                 pthread_mutex_unlock(&(sock->mutex));
00274                 return VFER_INPROGRESS;
00275         }
00276 
00277         pthread_mutex_unlock(&(sock->mutex));
00278         /***************/
00279         return 0;
00280 } /* vfer_connect() */
00281 
00282 
00283 /**
00284  * Associate the socket with the specified addr.
00285  *
00286  * This call returns BADSOCK immediately without changing the error
00287  * status of sock if sock is marked with an error on entry.
00288  *
00289  * @param vfd vfer socket identifier
00290  * @param addr a peer address
00291  * @param len length of addr
00292  * @return
00293  *      - 0 on success
00294  *      - VFER_BADSOCK bad vfd argument
00295  *      - VFER_NOBIND socket is not able to perform a bind because it is bound
00296  *      - VFER_BADADDR address is unavailable or in use.
00297  */
00298 int vfer_bind(vfer_fd vfd, struct sockaddr *addr, int len) {
00299         int err;
00300         int val;
00301         int ret;
00302         int num_packets;
00303         vfer_sock* sock;
00304 
00305         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
00306         sock = &(sockets.s[vfd]);
00307         if (sock->err != 0) { return VFER_BADSOCK; }
00308         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_bind", "Entered");
00309 
00310         /* PMTU: try to bind ICMP socket */
00311         if (sock->icmp_fd == -1 || bind(sock->icmp_fd, addr, len) < 0) {
00312                 sock->icmp_fd = -1;
00313                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_bind", "Unable to bind ICMP socket. Continuing without ICMP.");
00314         }else{
00315                 DEBUG_PMTUD_PRINT(DEBUG_PMTUD, "api.c", "vfer_bind", "ICMP socket bind succeeded.");
00316         }
00317 
00318         /***************/
00319         pthread_mutex_lock(&(sock->mutex));
00320         switch (sock->state) {
00321         case CONN_BOUND:
00322                 pthread_mutex_unlock(&(sock->mutex));
00323                 return VFER_BADSOCK;
00324         case CONN_ACQUIRED:
00325                 break;
00326         default:
00327                 pthread_mutex_unlock(&(sock->mutex));
00328                 return VFER_BADSOCK;
00329         }
00330 
00331         if (bind(sock->fd, addr, len) < 0) {
00332                 err = errno;
00333                 pthread_mutex_unlock(&(sock->mutex));
00334                 perror("vfer_bin :: bind");
00335                 if (err == EINVAL) {
00336                         return VFER_NOBIND;
00337                 }
00338                 return VFER_BADADDR;
00339         }
00340 
00341         /* try to set the receive buffer sizes */
00342         num_packets = DEFAULT_RECV_BUF_MAXPACKETS;
00343         val = CALC_UDP_RECV_BUF_SIZE(num_packets);
00344         do {
00345                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_bind", "Trying to set SO_RCVBUF to %d", val);
00346                 ret = setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val));
00347                 if (ret != 0) {
00348                         if (errno == ENOBUFS && num_packets > 100) {
00349                                 num_packets -= 100;
00350                                 val = CALC_UDP_RECV_BUF_SIZE(num_packets);
00351                         } else {
00352                                 perror("vfer_bind :: setsockopt");
00353                                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_bind", "Could not set RCVBUF size to [%d]", val);
00354                                 pthread_mutex_unlock(&(sock->mutex));
00355                                 return VFER_NOBIND;
00356                         }
00357                 }
00358         } while (ret != 0);
00359         sock->udp_recv_buf_size = val;
00360 
00361         /* try to set the send buffer size */
00362         num_packets = DEFAULT_SEND_BUF_MAXPACKETS;
00363         val = CALC_UDP_SEND_BUF_SIZE(num_packets);
00364         do {
00365                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_bind", "Trying to set SO_SNDBUF to %d", val);
00366                 ret = setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val));
00367                 if (ret != 0) {
00368                         if (errno == ENOBUFS && num_packets > 100) {
00369                                 num_packets -= 100;
00370                                 val = CALC_UDP_SEND_BUF_SIZE(num_packets);
00371                         } else {
00372                                 perror("vfer_bind :: setsockopt");
00373                                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_bind", "Could not set SNDBUF size to [%d]", val);
00374                                 pthread_mutex_unlock(&(sock->mutex));
00375                                 return VFER_NOBIND;
00376                         }
00377                 }
00378         } while (ret != 0);
00379         sock->udp_send_buf_size = val;
00380 
00381         sock->state = CONN_BOUND;
00382         pthread_mutex_unlock(&(sock->mutex));
00383         /*****************/
00384         return 0;
00385 } /* vfer_bind() */
00386 
00387 /**
00388  * Configures socket to listen for incoming connection requests.
00389  *
00390  * The call returns VFER_BADSOCK immediately without changing the error status
00391  * of sock if sock is marked with an error on entry.
00392  *
00393  * @param vfd vfer socket identifier
00394  * @param backlog the size of the queue for pending requests
00395  * @return
00396  *      - 0 on success
00397  *      - VFER_BADSOCK bad vfd argument
00398  *      - VFER_NOLISTEN socket is not able to perform a listen
00399  */
00400 int vfer_listen(vfer_fd vfd, int backlog) {
00401         vfer_sock* sock;
00402 
00403         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS) || (backlog < 1)) { return VFER_BADSOCK; }
00404         sock = &(sockets.s[vfd]);
00405         if (sock->err != 0) { return VFER_BADSOCK; }
00406 
00407         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_listen", "Entered");
00408         pthread_mutex_lock(&(sock->mutex));
00409         if (sock->state != CONN_BOUND) {
00410                 pthread_mutex_unlock(&(sock->mutex));
00411                 return VFER_BADSOCK;
00412         }
00413 
00414         if (!(sock->accept_queue = Accept_Queue_Create(backlog))) {
00415                 pthread_mutex_unlock(&(sock->mutex));
00416                 return VFER_NOLISTEN;
00417         }
00418 
00419         sock->state = CONN_LISTENING;
00420         pthread_mutex_unlock(&(sock->mutex));
00421         Control_Register_Socket(sock);
00422         return 0;
00423 } /* vfer_listen() */
00424 
00425 
00426 /**
00427  * For a socket in the listening state, removes the first pending
00428  * connection request from the queue and establishes a connection on
00429  * that socket.
00430  *
00431  * The call returns a socket identifier for use with the
00432  * connection. If, on entry, the listening socket is marked with an
00433  * error, this call immediately returns VFER_BADSOCK without changing
00434  * the error status.
00435  *
00436  * @param vfd vfer socket identifier
00437  * @param addr pointer to an address structure to be filled in with information
00438  * @param len length of the address strucutre
00439  * @return
00440  *      - a positive vfer socket identifier on success
00441  *      - VFER_BADSOCK bad vfd argument
00442  *      - VFER_TIMEOUT accept connection attempt timedout
00443  *      - VFER_NOACCEPT socket is not able to perform an accept because it is not listening,
00444  *      - VFER_NOTSTREAM socket is not of type SOCK_STREAM
00445  *      - VFER_WOULDBLOCK socket is non-blocking and there are no waiting connections
00446  *      - VFER_IMPL underlying socket error
00447  */
00448 vfer_fd vfer_accept(vfer_fd vfd, struct sockaddr *addr, socklen_t *len) {
00449         vfer_sock* accepted_socket;     /* socket pointer to socket to use as accepting socket */
00450         vfer_sock* sock;                /* listening socket pointer */
00451         int ret, i;
00452 
00453         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS) || !addr || !len) { return VFER_BADSOCK; }
00454         sock = &(sockets.s[vfd]);
00455         if (sock->err != 0) { return VFER_BADSOCK; }
00456 
00457         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_accept", "Entered");
00458 
00459         pthread_mutex_lock(&(sock->mutex));
00460         if (sock->state != CONN_LISTENING) {
00461                 pthread_mutex_unlock(&(sock->mutex));
00462                 return VFER_BADSOCK;
00463         }
00464         pthread_mutex_unlock(&(sock->mutex));
00465 
00466         /* reserve a socket for acquisition */
00467         for (i = 0; i < MAX_SOCKETS; i++) {
00468                 pthread_mutex_lock(&(sockets.s[i].mutex));
00469                 if (sockets.s[i].state == CONN_DISCONNECTED) {
00470                         accepted_socket = &(sockets.s[i]);
00471                         accepted_socket->state = CONN_ACQUIRED;
00472                         /* note that we locked the accepted_socket's mutex */
00473                         break;
00474                 }
00475                 pthread_mutex_unlock(&(sockets.s[i].mutex));
00476         }
00477         if (i == MAX_SOCKETS) {
00478                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_accept", "reached connections limit");
00479                 return VFER_IMPL;
00480         }
00481 
00482         /* accept connection on top of queue */
00483         ret = Control_Accept(sock, accepted_socket);
00484         if (ret == 0) {
00485                 ret = i;
00486         } else {
00487                 // printf("setting state to disconnected for ret_sock\n");
00488                 accepted_socket->err = 0;
00489                 accepted_socket->state = CONN_DISCONNECTED;
00490         }
00491         pthread_mutex_unlock(&(accepted_socket->mutex));
00492         return ret;
00493 } /* vfer_accept() */
00494 
00495 
00496 /**
00497  * Uses the data described by optval and optlen to set optname on socket.
00498  *
00499  * If, on entry, the socket is marked with an error, this call
00500  * immediately returns without changing the error status. The possible
00501  * option names are:
00502  * - SOCK_STREAM
00503  * - SOCK_DGRAM
00504  *   - QTTL (int) The duration (in msec) that unacknowledged messages will
00505  *     remain available for retransmission.
00506  * - Both
00507  *   - SNDSIZE (int) The packet size used for the underlying transport.
00508  *     This defaults to the MTU.
00509  *   - NOPMTUD (int) Disable PMTUD.
00510  *   - NONBLOCK (int) Set socket to use non-blocking mode for send, recv,
00511  *     accept and connect calls.
00512  *
00513  * @param vfd vfer socket identifier
00514  * @param optname option name
00515  * @param optval option value
00516  * @param optlen option length,description
00517  * @return
00518  *      0 on success
00519  *      - VFER_BADSOCK bad vfd argument
00520  *      - VFER_BADOPT unknown or illegal option/length
00521  */
00522 int vfer_setsockopt(vfer_fd vfd, int optname, void *optval, int optlen) {
00523         int ival;
00524 
00525         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS) || (sockets.s[vfd].err != 0)) { return VFER_BADSOCK; }
00526         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_setsockopt", "Entered with optname[%d]", optname);
00527 
00528         pthread_mutex_lock(&(sockets.s[vfd].mutex));
00529         switch (optname) {
00530         case VFERSO_QTTL:
00531                 /* not implemented */
00532                 break;
00533         case VFERSO_SNDSIZE:
00534                 /* not implemented */
00535                 if (optlen != sizeof(int)) {
00536                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00537                         return VFER_BADOPT;
00538                 }
00539 
00540                 ival = *((int*)(optval));
00541                 if (ival < 1) {
00542                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00543                         return VFER_BADOPT;
00544                 }
00545                 sockets.s[vfd].ccontrol.mtu = ival;
00546                 break;
00547         case VFERSO_RELFUN:
00548                 if (optlen != sizeof(relfun_t*)) {
00549                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00550                         return VFER_BADOPT;
00551                 }
00552                 sockets.s[vfd].relfun = optval;
00553                 break;
00554         case VFERSO_CONNECTTIMEOUT:
00555                 /* controls timeout value for vfer_connect() */
00556                 if (optlen != sizeof(int)) {
00557                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00558                         return VFER_BADOPT;
00559                 }
00560                 sockets.s[vfd].connect_timeout = *((int*)(optval));
00561                 break;
00562         case VFERSO_CLOSETIMEOUT:
00563                 /* controls timeout value for vfer_close- irrespective of whether send queue is empty or not */
00564                 if (optlen != sizeof(int)) {
00565                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00566                         return VFER_BADOPT;
00567                 }
00568                 sockets.s[vfd].close_timeout = *((int*)(optval));
00569                 break;
00570         case VFERSO_ACCEPTTIMEOUT:
00571                 /* controls timeout value for vfer_accept() */
00572                 if (optlen != sizeof(int)) {
00573                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00574                         return VFER_BADOPT;
00575                 }
00576                 sockets.s[vfd].accept_timeout = *((int*)(optval));
00577                 break;
00578         case VFERSO_SNDTIMEOUT:
00579                 /* controls timeout value for vfer_send() */
00580                 if (optlen != sizeof(int)) {
00581                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00582                         return VFER_BADOPT;
00583                 }
00584                 sockets.s[vfd].snd_timeout = *((int*)(optval));
00585                 break;
00586         case VFERSO_RCVTIMEOUT:
00587                 /* controls timeout value for vfer_recv() */
00588                 if (optlen != sizeof(int)) {
00589                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00590                         return VFER_BADOPT;
00591                 }
00592                 sockets.s[vfd].rcv_timeout = *((int*)(optval));
00593                 break;
00594         case VFERSO_NOPMTUD:
00595                 /* controls whether path mtu is performed. Default is 0. */
00596                 if (optlen != sizeof(int)) {
00597                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00598                         return VFER_BADOPT;
00599                 }
00600                 if(1 == *((int*)(optval))){
00601                         sockets.s[vfd].pmtu.probe_state = NO_PMTUD;
00602                 }
00603                 break;
00604         case VFERSO_NONBLOCK:
00605                 if (*((int*)(optval)) == 1) {
00606                         sockets.s[vfd].opts |= optname;
00607                 } else {
00608                         sockets.s[vfd].opts &= (~optname);
00609                 }
00610                 break;
00611         default:
00612                 pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00613                 return VFER_BADOPT;
00614         }
00615         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00616         return 0;
00617 } /* vfer_setsockopt() */
00618 
00619 
00620 /**
00621  * Returns data describing option optname of socket.
00622  *
00623  * On entry optlen is the size of space pointed to by optval. On exit,
00624  * optlen is changed to reflect the actual size of the returned
00625  * data. If, on entry, sock is marked with an error, this call
00626  * immediately returns without changing the error status. In
00627  * addition to the option names valid for the vfer_setsockopt() call the
00628  * following additional options are available.
00629  * - SOCK_STREAM
00630  * - SOCK_DGRAM
00631  *   - MTU (int) current MTU
00632  *   - MSGSIZE (int) The maximum size of an individual message.
00633  *
00634  * @param vfd vfer socket identifier
00635  * @param optname option name pointer
00636  * @param optval option value pointer
00637  * @param optlen option length,description pointer
00638  * @return
00639  *      - 0 on success
00640  *      - VFER_BADSOCK bad vfd argument
00641  *      - VFER_BADOPT unknown or illegal option/length
00642  */
00643 int vfer_getsockopt(vfer_fd vfd, int optname, void *optval, int *optlen) {
00644 
00645         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS) || !optval || !optlen || (sockets.s[vfd].err != 0)) { return VFER_BADSOCK; }
00646         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_getsockopt", "Entered with optname[%d]", optname);
00647 
00648         pthread_mutex_lock(&(sockets.s[vfd].mutex));
00649         if (*optlen <= 0) {
00650                 pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00651                 return VFER_BADOPT;
00652         }
00653 
00654         switch (optname) {
00655         case VFERSO_QTTL:
00656                 /* not implemented */
00657                 break;
00658         case VFERSO_SNDSIZE:
00659                 /* not implemented */
00660                 break;
00661         case VFERSO_NOPMTUD:
00662                 /* okay not to check optlen since > 0 */
00663                 *((char*)(optval)) = SOCK_OPT_ISSET((&(sockets.s[vfd])), VFERSO_NOPMTUD);
00664                 *optlen = 1;
00665                 break;
00666         case VFERSO_NONBLOCK:
00667                 /* okay not to check optlen since > 0 */
00668                 *((char*)(optval)) = SOCK_OPT_ISSET((&(sockets.s[vfd])), VFERSO_NONBLOCK);
00669                 *optlen = 1;
00670                 break;
00671         case VFERSO_RELFUN:
00672                 /* not implemented */
00673                 break;
00674         case VFERSO_CONNECTTIMEOUT:
00675                 if (*optlen < sizeof(int)) {
00676                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00677                         return VFER_BADOPT;
00678                 }
00679                 *((int*)(optval)) = sockets.s[vfd].connect_timeout;
00680                 *optlen = sizeof(int);
00681                 break;
00682         case VFERSO_CLOSETIMEOUT:
00683                 if (*optlen < sizeof(int)) {
00684                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00685                         return VFER_BADOPT;
00686                 }
00687                 *((int*)(optval)) = sockets.s[vfd].close_timeout;
00688                 *optlen = sizeof(int);
00689                 break;
00690         case VFERSO_ACCEPTTIMEOUT:
00691                 if (*optlen < sizeof(int)) {
00692                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00693                         return VFER_BADOPT;
00694                 }
00695                 *((int*)(optval)) = sockets.s[vfd].accept_timeout;
00696                 *optlen = sizeof(int);
00697                 break;
00698         case VFERSO_SNDTIMEOUT:
00699                 if (*optlen < sizeof(int)) {
00700                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00701                         return VFER_BADOPT;
00702                 }
00703                 *((int*)(optval)) = sockets.s[vfd].snd_timeout;
00704                 *optlen = sizeof(int);
00705                 break;
00706         case VFERSO_RCVTIMEOUT:
00707                 if (*optlen < sizeof(int)) {
00708                         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00709                         return VFER_BADOPT;
00710                 }
00711                 *((int*)(optval)) = sockets.s[vfd].rcv_timeout;
00712                 *optlen = sizeof(int);
00713                 break;
00714         default:
00715                 pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00716                 return VFER_BADOPT;
00717         }
00718         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00719         return 0;
00720 } /* vfer_getsockopt() */
00721 
00722 /**
00723  * Computes and return current performance data for socket sock.
00724  *
00725  * If, on entry, vfd is invalid or marked with an error this call
00726  * immediately returns a zeroed out vfer_stats structure. On
00727  * successfully return, this structure contains among many other
00728  * things, average performance statistics for the socket and
00729  * incremental statistics since the last call to
00730  * vfer_sockstats(). Performance statistics include the following.
00731  * - send rate in Mbps
00732  * - recv rate in Mbps
00733  * - retransmit traffic as a fraction of total traffic
00734  * - round trip time statistics
00735  *
00736  * For a SOCK_DGRAM socket, configured for partial reliability, the
00737  * performance statistics also include the number of messages that
00738  * expire after exceeding the QTTL limit.
00739  *
00740  * @param vfd vfer socket identifier
00741  * @return
00742  *      - a stats structure filled with current stats about socket sock
00743  *      - a zeroed out stats structure on error
00744  */
00745 vfer_stats vfer_sockstats(vfer_fd vfd) {
00746         vfer_stats stats;
00747         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return stats; }
00748         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_sockstats", "Entered");
00749 
00750         pthread_mutex_lock(&(sockets.s[vfd].mutex));
00751         stats = sockets.s[vfd].stats;
00752         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00753         return stats;
00754 } /* vfer_sockstats() */
00755 
00756 
00757 /**
00758  * Returns the error condition of a socket
00759  *
00760  * @param vfd vfer socket identifier
00761  * @return
00762  *      the error condition of socket sock.
00763  */
00764 int vfer_sockerror(vfer_fd vfd) {
00765         int err;
00766         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return 0; }
00767         /* DEBUG_PRINT(DEBUG_API, "api.c", "vfer_sockerror", "Entered"); */
00768 
00769         pthread_mutex_lock(&(sockets.s[vfd].mutex));
00770         err = sockets.s[vfd].err;
00771         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00772         return err;
00773 } /* vfer_sockerror() */
00774 
00775 
00776 /**
00777  * Returns a text description of the error code err.
00778  *
00779  * @param err error code
00780  * @return
00781  *      a pointer to a text description of err
00782  */
00783 char* vfer_errortext(int err) {
00784         if (!PROTOCOL_INIT_TEST) return 0;
00785         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_errortext", "Entered");
00786 
00787         switch (err) {
00788         case 0:
00789                 return "success";
00790         case VFER_BADTYPE:
00791                 return "invalid socket type";
00792         case VFER_IMPL:
00793                 return "internal implemenation error";
00794         case VFER_BADSOCK:
00795                 return "bad socket passed as argument";
00796         case VFER_TIMEOUT:
00797                 return "connection attempt timedout";
00798         case VFER_REFUSED:
00799                 return "connection refused";
00800         case VFER_NOCONN:
00801                 return "not able to connect on a socket";
00802         case VFER_NOBIND:
00803                 return "can't bind the socket";
00804         case VFER_BADADDR:
00805                 return "bad adress passed as argument";
00806         case VFER_NOLISTEN:
00807                 return "can't listen on socket";
00808         case VFER_NOACCEPT:
00809                 return "socket not able to perform accept because it is not listening";
00810         case VFER_NOTSTREAM:
00811                 return "socket is not of type SOCK_STREAM";
00812         case VFER_WOULDBLOCK:
00813                 return "socket is non-blocking and there are no waiting connections";
00814         case VFER_BADOPT:
00815                 return "unknown or illegal option/length";
00816         case VFER_BADFD:
00817                 return "error using mmap on fd";
00818         case VFER_INVAL:
00819                 return "argument value is invalid";
00820         case VFER_UNCONN:
00821                 return "socket is not connected";
00822         default:
00823                 return "unrecognized error";
00824         }
00825 } /* vfer_errortext() */
00826 
00827 
00828 /**
00829  * Mmap the file described by fd and send size bytes of data starting
00830  * at offset. For now we assume that this send call will use the
00831  * current reliability function, ie. if the file will be arbitrarily
00832  * parititioned into frames (into MAX_SEND_WINDOW chunks), each frame
00833  * will be treated separately as far as reliability is
00834  * concerned. Therefore this call makes most sense if the reliability
00835  * function is the constant 1 (ie. full reliability).
00836  *
00837  * @param vfd vfer socket identifier
00838  * @param fd file descriptor to map into mem and to send
00839  * @param offset data starting position in the file
00840  * @param size number of bytes to send
00841  * @return
00842  *      - Returns the actual number of bytes sent on success
00843  *      - VFER_BADSOCK bad vfd argument
00844  *      - VFER_BADFD error using mmap on fd
00845  *      - VFER_INVAL invalid offset or size
00846  */
00847 int vfer_sendfile(vfer_fd vfd, int fd, off_t offset, size_t size) {
00848     if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
00849     DEBUG_PRINT(DEBUG_API, "api.c", "vfer_sendfile", "Entered fd[%d] offset[%d] size[%d]", fd, (int)(offset), (int)(size));
00850 
00851     return vfer__sendfile( (vfer_trnsm_fn) vfer_send,
00852                            (vfer_trnsm_arg) vfd, MAX_FRAME_SIZE, fd,
00853                            offset, size);
00854 }
00855 
00856 /**
00857  * Mmap the file described by \a fd and send \a size bytes of data
00858  * starting at \a offset into socket \a arg using function \a fn.
00859  *
00860  * @param[in] fn  send function to use
00861  * @param[in] arg socket argument to pass to send function
00862  * @param[in] frame_size maximum frame size that \a fn can handle for
00863  *            \a arg
00864  * @param[in] fd file descriptor to map into mem and to send
00865  * @param[in] offset writing starting position in the file
00866  * @param[in] size number of bytes to send
00867  * @return
00868  *      - Returns the actual number of bytes sent on success
00869  *      - VFER_BADFD error using mmap on fd
00870  *      - VFER_INVAL invalid offset or size
00871  *      - Every error code that \a fn can return
00872  */
00873 int vfer__sendfile(vfer_trnsm_fn fn, vfer_trnsm_arg arg,
00874                    int frame_size, int fd, off_t offset, size_t size) {
00875         void* ptr;
00876         int bytes_sent, err, n, to_send;
00877         size_t to_map, bytes_mapped;
00878         int page_size;
00879         size_t mmap_chunk_size;
00880 
00881         /* determine the mmap_chunk_size as a multiple of page_size */
00882         page_size = getpagesize();
00883         mmap_chunk_size = (MMAP_CHUNK_SIZE / page_size) * page_size;
00884 
00885         bytes_mapped = 0;
00886         while (bytes_mapped != size) {
00887                 if ((size - bytes_mapped) >= mmap_chunk_size) {
00888                         to_map = mmap_chunk_size;
00889                 } else {
00890                         to_map = size - bytes_mapped;
00891                 }
00892                 ptr = mmap(0, to_map, PROT_READ, MAP_PRIVATE, fd, bytes_mapped + offset);
00893                 if (ptr == MAP_FAILED) {
00894                         err = errno;
00895                         perror("vfer__sendfile :: mmap");
00896                         DEBUG_PRINT(DEBUG_API, "api.c", "vfer__sendfile", "mmap failed with err[%d]", err);
00897                         switch (err) {
00898                         case EBADF:
00899                                 return VFER_BADFD;
00900                         default:
00901                                 return VFER_INVAL;
00902                         }
00903                 }
00904 
00905                 /* loop and send max sized frames until eof */
00906                 bytes_sent = 0;
00907                 while (bytes_sent != to_map) {
00908                         if ((to_map - bytes_sent) >= frame_size) {
00909                                 to_send = frame_size;
00910                         } else {
00911                                 to_send = to_map - bytes_sent;
00912                         }
00913                         n = fn(arg, ptr + bytes_sent, to_send);
00914                         if (n < 0) {
00915                                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer__sendfile", "send function failed with err[%d]", n);
00916                                 munmap(ptr, to_map);
00917                                 return n;
00918                         }
00919                         bytes_sent += n;
00920                 }
00921                 bytes_mapped += to_map;
00922                 munmap(ptr, to_map);
00923         }
00924         return bytes_mapped;
00925 } /* vfer__sendfile() */
00926 
00927 /**
00928  * Sends len bytes from buf on a connected socket.
00929  *
00930  * If the socket is of type SOCK_DGRAM then the data is treated as a
00931  * single message and len must be less then the maximum message
00932  * length. Messages greater than SNDSIZE are fragmented. On a fully
00933  * reliable socket messages will be delivered intact. Messages always
00934  * arrive *in* the original order. On an unreliable socket messages
00935  * are either delivered intact or discarded also *in* the original
00936  * order.
00937  *
00938  * @param vfd vfer socket identifier
00939  * @param buf a pointer to a buffer to send
00940  * @param len length of the data to send starting at buf
00941  * @return
00942  *      - Returns the actual number of bytes sent on success (a positive number)
00943  *      - VFER_BADSOCK bad vfd argument
00944  *      - VFER_TIMEOUT timed out while sending data
00945  *      - VFER_UNCONN sock is not connected
00946  *      - VFER_INVAL invalid buf or len
00947  *      - VFER_WOULDBLOCK socket is non-blocking and requested operation would block
00948  */
00949 int vfer_send(vfer_fd vfd, const void * buf, size_t len) {
00950         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
00951         /* DEBUG_PRINT(DEBUG_API, "api.c", "vfer_send", "Entered len[%d]", len); */
00952 
00953         pthread_mutex_lock(&(sockets.s[vfd].mutex));
00954         if (sockets.s[vfd].state == CONN_CONNECTED) {
00955                 if (len > MAX_FRAME_SIZE) {
00956                         return VFER_INVAL;
00957                 }
00958                 return Control_Send(&(sockets.s[vfd]), buf, len); /* this function sets sock->err */
00959         }
00960         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
00961         return VFER_UNCONN;
00962 } /* vfer_send() */
00963 
00964 
00965 /**
00966  * Reads size bytes from socket writing them into the file described
00967  * by fd at offset offset.
00968  *
00969  * @param vfd vfer socket identifier
00970  * @param fd file descriptor to write received data to
00971  * @param offset writing starting position in the file
00972  * @param size number of bytes to read
00973  * @return
00974  *      - Returns the actual number of bytes read on success (a positive number)
00975  *      - VFER_TIMEOUT timed out while waiting to receive file
00976  *      - VFER_BADSOCK bad vfd argument
00977  *      - VFER_INVAL invalid offset or size
00978  */
00979 int vfer_recvfile(vfer_fd vfd, int fd, off_t offset, size_t size) {
00980     if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
00981     DEBUG_PRINT(DEBUG_API, "api.c", "vfer_recvfile", "Entered fd[%d] offset[%d] size[%d]", fd, (int)(offset), (int)(size));
00982 
00983     return vfer__recvfile( (vfer_trnsm_fn) vfer_recv,
00984                            (vfer_trnsm_arg) vfd, MAX_FRAME_SIZE, fd,
00985                            offset, size);
00986 }
00987 
00988 
00989 
00990 /**
00991  * Reads \a size bytes from socket \a arg using function \a fn and
00992  * writes them into the file described by \a fd at offset \a offset.
00993  *
00994  * @param[in] fn  receive function to use
00995  * @param[in] arg socket argument to pass to receive function
00996  * @param[in] frame_size maximum frame size that \a fn can handle for
00997  *            \a arg
00998  * @param[out] fd file descriptor to write received data to
00999  * @param[in] offset writing starting position in the file
01000  * @param[in] size number of bytes to read
01001  * @return
01002  *      - Returns the actual number of bytes read on success (a positive number)
01003  *      - Every error code that \a fn can return
01004  *      - VFER_INVAL invalid offset or size
01005  */
01006 int vfer__recvfile(vfer_trnsm_fn fn, vfer_trnsm_arg arg,
01007                    uint32_t frame_size, int fd, off_t offset, size_t size) {
01008     char file_buf[frame_size];
01009     int i, n;
01010 
01011     /* set the file pointer to requested offset position in the file */
01012     lseek(fd, offset, SEEK_SET);
01013 
01014     /* i counts the number of frames received */
01015     /* size maintains number of bytes left to write  */
01016     i = n = 0;
01017     while (size != 0 && ((n = fn(arg, file_buf, frame_size)) > 0)) {
01018         if (n > size) {
01019             n = size;
01020         }
01021                 if (write(fd, file_buf, n) != n) {
01022                     perror("vfer_recvfile :: write");
01023                     DEBUG_PRINT(DEBUG_API, "api.c", "vfer__recvfile", "write failed on frame[%d]", i);
01024                     return VFER_INVAL;
01025                 }
01026                 i++;
01027                 size -= n;
01028     }
01029     if (n < 0) {
01030         DEBUG_PRINT(DEBUG_API, "api.c", "vfer__recvfile", "receive function failed on frame[%d] error[%d]", i, n);
01031         return n;
01032     }
01033     return size;
01034 } /* vfer__recvfile() */
01035 
01036 
01037 /**
01038  * Read up to len bytes from socket into buf. If the socket is of type
01039  * SOCK_DGRAM then an entire message is read into buf with bytes in
01040  * excess of len being discarded.
01041  *
01042  * @param vfd vfer socket identifier
01043  * @param buf a pointer to a buffer into which to receive
01044  * @param len maximum length of data to receive into buf
01045  * @return
01046  *      - number of bytes read on success
01047  *      - VFER_TIMEOUT timed out while waiting to receive data
01048  *      - VFER_BADSOCK bad vfd argument
01049  *      - VFER_UNCONN sock is not connected
01050  *      - VFER_INVAL invalid buf or len
01051  *      - VFER_WOULDBLOCK socket is non-blocking and requested operation would block
01052  */
01053 int vfer_recv(vfer_fd vfd, void *buf, size_t len) {
01054         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
01055         /* DEBUG_PRINT(DEBUG_API, "api.c", "vfer_recv", "Entered len[%d]", len); */
01056 
01057         if (len > MAX_FRAME_SIZE) { return VFER_INVAL; }
01058 
01059         pthread_mutex_lock(&(sockets.s[vfd].mutex));
01060         if (sockets.s[vfd].state != CONN_CONNECTED &&
01061             sockets.s[vfd].state != CONN_DISCONNECTED &&
01062             sockets.s[vfd].state != CONN_DISCONNECTING) {
01063                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_recv", "socket not in CONN_CONNECTED/DISCONNECTED/DISCONNECTING states but state[%d]",
01064                             sockets.s[vfd].state);
01065                 pthread_mutex_unlock(&(sockets.s[vfd].mutex));
01066                 return VFER_UNCONN;
01067         }
01068         return Control_Recv(&(sockets.s[vfd]), buf, len); /* this function sets sock->err */
01069 } /* vfer_recv() */
01070 
01071 /**
01072  * Marks socket with the conditions to be tested by the select call.
01073  *
01074  * Called prior to using vfer_select(). Mark is a bitwise OR of
01075  * VFER_READABLE, VFER_WRITABLE and VFER_EXCEPTION.
01076  *
01077  * @param vfd vfer socket identifier
01078  * @param mark a mark to associate with the socket sock
01079  * @return
01080  *      - 0 on succes
01081  *      - VFER_BADSOCK bad vfd argument
01082  *      - VFER_INVAL invalid mark value
01083  */
01084 int vfer_selectmark(vfer_fd vfd, int mark) {
01085         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
01086         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_selectmark", "Entered");
01087         if ((mark ^ (VFER_READABLE | VFER_WRITABLE | VFER_EXCEPTION)) != 0) {
01088                 return VFER_INVAL;
01089         }
01090         pthread_mutex_lock(&(sockets.s[vfd].mutex));
01091         sockets.s[vfd].select_mark = mark;
01092         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
01093         return 0;
01094 } /* vfer_selectmark() */
01095 
01096 /**
01097  * Returns the result of the vfer_select() call for socket.
01098  *
01099  * The return value will be a bitwise OR of VFER_READABLE,
01100  * VFER_WRITABLE and VFER_EXCEPTION depending on how sock was marked
01101  * and the result of the vfer_select() call.
01102  *
01103  * @param vfd vfer socket identifier
01104  * @return
01105  *      - bitwise OR of marks for socket sock on succes
01106  *      - VFER_BADSOCK bad vfer_sock
01107  */
01108 int vfer_selecttest(vfer_fd vfd) {
01109         int ret;
01110         if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS)) { return VFER_BADSOCK; }
01111         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_selecttest", "Entered");
01112         pthread_mutex_lock(&(sockets.s[vfd].mutex));
01113         ret = (int)(sockets.s[vfd].select_result);
01114         pthread_mutex_unlock(&(sockets.s[vfd].mutex));
01115         return ret;
01116 } /* vfer_selecttest() */
01117 
01118 /**
01119  * Returns the number of sockets in an array that satisfy the marked
01120  * conditions.
01121  *
01122  * Timeout is the maximum time to wait before the call returns. If
01123  * timeout is null the call blocks indefinitely. A timeout value of
01124  * zero can be used to effect a poll operation. Timeout is not changed
01125  * by the call.
01126  *
01127  * @param len length of vfds array
01128  * @param vfds an array of socket vfer_fds to check for marked conditions
01129  * @param timeout a timeout value to control waiting time
01130  * @return
01131  *      - number of sockets in array socks that satisfy the marked conditions
01132  *      - VFER_BADSOCK one of the vfer_sock's in socks is bad
01133  *      - VFER_IMPL underlying select error
01134  *      - VFER_INVAL timeout value is invalid
01135  */
01136 int vfer_select(int len, vfer_fd *vfds, struct timeval *timeout) {
01137         int i;                  /* itterator var */
01138         int count;              /* counts number of sockets that have non 0 select marks */
01139         int ret;                /* return var for pthread_cond_timedwait */
01140         struct timespec timedwait_timeout; /* used as argument to  pthread_cond_timedwait */
01141 
01142         if (!PROTOCOL_INIT_TEST) { return VFER_BADSOCK; }
01143         DEBUG_PRINT(DEBUG_API, "api.c", "vfer_select", "Entered");
01144 
01145         /* count the number of sockets marked with a condition to test
01146            for and set their select_active to 1 indicating that
01147            select_mark should be consulted and select_result should be
01148            set in case of correct condition for the socket */
01149         count = 0;
01150         pthread_mutex_lock(&(sockets.select_mutex));
01151         for (i = 0; i < len; i++) {
01152                 if ((vfds[i] < 0) || (vfds[i] > MAX_SOCKETS)) {
01153                         return VFER_BADSOCK;
01154                 }
01155                 pthread_mutex_lock(&(sockets.s[vfds[i]].mutex));
01156                 if (sockets.s[vfds[i]].select_mark != 0) {
01157                         sockets.s[vfds[i]].select_active = 1;
01158                         sockets.s[vfds[i]].select_result = 0;
01159                         count++;
01160                 }
01161                 pthread_mutex_unlock(&(sockets.s[vfds[i]].mutex));
01162         }
01163         /* if none of the sockets are marked with conditions to test, return 0 */
01164         if (count == 0) {
01165                 pthread_mutex_unlock(&(sockets.select_mutex));
01166                 return 0;
01167         }
01168         /* set up the timeout var */
01169         timedwait_timeout.tv_sec = timeout->tv_sec;
01170         timedwait_timeout.tv_nsec = timeout->tv_usec * 1000;
01171 
01172         /* wait until a select_cond is signalled or we timeout */
01173         do {
01174                 ret = pthread_cond_timedwait(&(sockets.select_cond), &(sockets.select_mutex), &(timedwait_timeout));
01175         } while (ret != 0 && ret != ETIMEDOUT);
01176 
01177         /* count the number of mutexes that are marked with some select result */
01178         count = 0;
01179         for (i = 0; i < len; i++) {
01180                 pthread_mutex_lock(&(sockets.s[vfds[i]].mutex));
01181                 if (sockets.s[vfds[i]].select_active == 1) {
01182                         if (sockets.s[vfds[i]].select_result != 0) {
01183                                 count++;
01184                         }
01185                         sockets.s[vfds[i]].select_active = 0;
01186                 }
01187                 pthread_mutex_unlock(&(sockets.s[vfds[i]].mutex));
01188         }
01189         pthread_mutex_unlock(&(sockets.select_mutex));
01190         return count;
01191 } /* vfer_select() */
01192 
01193 /**
01194  * Controls debugging for the library.
01195  *
01196  * By specifying a debug file output, and an error file output as FILE
01197  * pointers, debug information can be outputted to specific
01198  * files. Printing is also controlled by a selection of logical levels
01199  * of the internal implementation (via character string wherein each
01200  * char encodes one level) needing debug output.
01201  *
01202  * @param debug_file output FILE pointer for debug messages
01203  * @param error_file output FILE pointer for error messages
01204  * @param layers a string containing chars specifying layers needing debugging output
01205  *      the char to component map is:
01206  *      - q : accept queue component
01207  *      - a : api component
01208  *      - c : control component
01209  *      - C : ccontrol component (congestion control)
01210  *      - p : packet component
01211  * @return
01212  *      0 on succes
01213  *      -1 on error
01214  */
01215 int vfer_debug(FILE* debug_file, FILE* error_file, const char* layers) {
01216         int len;
01217         DEBUG_ACCEPTQ           = 0;
01218         DEBUG_API               = 0;
01219         DEBUG_CTL               = 0;
01220         DEBUG_PACKET            = 0;
01221         DEBUG_CCTL              = 0;
01222         DEBUG_PMTUD     = 0;
01223 
01224         if (layers == NULL) {
01225                 len = 0;
01226         } else {
01227                 len = strlen(layers);
01228         }
01229         while (len > 0) {
01230                 switch (layers[len-1]) {
01231                 case 'q':
01232                         DEBUG_ACCEPTQ = 1;
01233                         break;
01234                 case 'a':
01235                         DEBUG_API = 1;
01236                         break;
01237                 case 'c':
01238                         DEBUG_CTL = 1;
01239                         break;
01240                 case 'C':
01241                         DEBUG_CCTL = 1;
01242                         break;
01243                 case 'p':
01244                         DEBUG_PACKET = 1;
01245                         break;
01246                 case 'P':
01247                         DEBUG_PMTUD = 1;
01248                         break;
01249                 }
01250                 len--;
01251         }
01252         debug_out = debug_file;
01253         error_out = error_file;
01254         /* signals to vfer_internal_init not to set the printing vars
01255          * as we've already set them per user's request */
01256         Debug_Initialized = 0;
01257         if (!PROTOCOL_INIT_TEST) return -1;
01258         return 0;
01259 } /* vfer_debug() */
01260 
01261 /**
01262  * Initializes all the data structures & component used by the
01263  * vfer protocol
01264  *
01265  * This function runs only once for every time we load the protocol
01266  * into a program. Note that this function is internal and should NOT
01267  * be used outside of the protocol's scope. It does NOT set any
01268  * socket's err field.
01269  *
01270  * @return
01271  *      0 on success
01272  *      -1 on error
01273  */
01274 static int vfer_internal_init() {
01275         int i;
01276 #ifdef TSCI2
01277         int tsci2_methods;
01278 #endif
01279         if (Debug_Initialized == -1) {
01280                 /* initialize all debug controlling vars to 0 (no printing) unless vfer_debug was called prior */
01281                 debug_out = NULL;
01282                 error_out = stdout;
01283                 DEBUG_ACCEPTQ           = 0;
01284                 DEBUG_API               = 0;
01285                 DEBUG_CTL               = 0;
01286                 DEBUG_CCTL              = 0;
01287                 DEBUG_PACKET            = 0;
01288         }
01289 
01290         sockets.num_active = 0;
01291         sockets.max_fd = 0;
01292         FD_ZERO(&(sockets.fds));
01293         pthread_mutex_init(&(sockets.mutex), NULL);
01294         pthread_mutex_init (&(sockets.select_mutex), NULL);
01295         pthread_cond_init(&(sockets.select_cond), NULL);
01296 
01297         for (i = 0; i < MAX_SOCKETS; i++) {
01298                 pthread_mutex_init (&(sockets.s[i].mutex), NULL);
01299                 pthread_cond_init(&(sockets.s[i].cond), NULL);
01300 
01301                 sockets.s[i].send_frames.first  = sockets.s[i].send_frames.last = NULL;
01302                 sockets.s[i].recv_frames.first  = sockets.s[i].recv_frames.last = NULL;
01303                 sockets.s[i].recvd_frames.first = sockets.s[i].recvd_frames.last        = NULL;
01304                 sockets.s[i].send_frames.num            = 0;
01305                 sockets.s[i].recv_frames.num            = 0;
01306                 sockets.s[i].recvd_frames.num           = 0;
01307                 sockets.s[i].recvd_frames.length        = 0;
01308                 sockets.s[i].recv_frames.length         = 0;
01309                 sockets.s[i].send_frames.length         = 0;
01310                 pthread_mutex_init(&(sockets.s[i]).mutx_readable, NULL);
01311                 pthread_cond_init(&(sockets.s[i]).cond_readable, NULL);
01312                 sockets.s[i].state = CONN_DISCONNECTED;
01313         }
01314 
01315         /* initialize the TSCI2 library if we are compiled with it */
01316 #ifdef TSCI2
01317         tsci2_methods = TSCI2_DAEMON | TSCI2_CLIENT | TSCI2_FALLBACK;
01318         tsci2_methods = tsci2_init(tsci2_methods);
01319         switch ( tsci2_methods )  {
01320         case TSCI2_DAEMON:
01321                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_internal_init", "using method TSCI2_DAEMON for gettimeofday");
01322                 break;
01323         case TSCI2_CLIENT:
01324                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_internal_init", "using method TSCI2_CLIENT for gettimeofday");
01325                 break;
01326         case TSCI2_FALLBACK:
01327                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_internal_init", "using method TSCI2_FALLBACK for gettimeofday");
01328                 break;
01329         default:
01330                 DEBUG_PRINT(DEBUG_API, "api.c", "vfer_internal_init", "TSCI2 initialization failed");
01331         }
01332 #endif
01333 
01334         /* rand gen init (rand() is used to gen random seq/frame numbers in packet.c) */
01335         srand(time(NULL));
01336 
01337         /* memory allocation var reset */
01338         ReqMem = 0;
01339 
01340         Initialized = 1;
01341         return 0;
01342 } /* vfer_internal_init() */
01343 
01344 
01345 /**
01346  * Returns the current name for the specified socket. The
01347  * \a namelen parameter should be initialized to indicate the amount of
01348  * space pointed to by name. On return it contains the actual size of
01349  * the name returned (in bytes).
01350  *
01351  * @param[in] vfd vfer socket identifier
01352  * @param[out] name pointer to name structure
01353  * @param[in] len length of the name structure
01354  * @return
01355  *      - 0 on success
01356  *      - VFER_BADSOCK bad vfd argument
01357  */
01358 int vfer_getsockname (vfer_fd vfd, struct sockaddr *name, socklen_t* len) {
01359     if (!PROTOCOL_INIT_TEST || (vfd < 0) || (vfd > MAX_SOCKETS))
01360         return VFER_BADSOCK;
01361 
01362     vfer_sock* sock = &(sockets.s[vfd]);
01363     if (sock->err != 0)
01364         return VFER_BADSOCK;
01365 
01366     // \todo do proper error checking
01367     if (getsockname(sock->fd, name, len) != 0) {
01368         perror("getsockname failed");
01369         abort();
01370     }
01371     return 0;
01372 }
01373 
01374 /**
01375  * Returns \a true if the given socket is stream and not datagram based.
01376  *
01377  * \param[in] fd VFER FD to check
01378  * \return
01379  *  - \a true for a streamed connection
01380  *  - \a false for a datagram based connection
01381  *
01382  *  \todo Implement me properly
01383  **/
01384 bool vfer_is_stream (vfer_fd fd) {
01385     return false;
01386 }
01387 
01388 /**
01389  * Returns \a the maximum size of a frame.
01390  *
01391  * \param[in] fd VFER FD to check
01392  * \return
01393  *  - the maximum frame size > 0 on success
01394  *
01395  *  \todo Implement me properly
01396  **/
01397 ssize_t vfer_max_frame_size (vfer_fd fd) {
01398     return MAX_FRAME_SIZE;
01399 }

Generated on Tue Aug 8 16:07:18 2006 for VFER by  doxygen 1.4.7