/***************************************************************************** * server_v2.c X25 API: SVC Server Application * * Author(s): Nenad Corbic * * Copyright: (c) 1995-2000 Sangoma Technologies Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * ============================================================================ * * Description: * * The server.c utility will accept, user defined number, of * outgoing calls and tx/rx, user defined number, of packets on each * active svc. * * This utility should be used as an architectual model. It is up to * the user to handle all conditions of x25. Please refer to the * X25API programming manual for futher details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FALSE 0 #define TRUE 1 #define TX_PACKET_LEN 128 #define NO_FRMS_TO_TX 100 #define MAX_RX_PKT_LEN 4096 #define MAX_X25_ADDR_SIZE 16 #define MAX_X25_DATA_SIZE 129 #define MAX_X25_FACL_SIZE 110 #define MAX_SOCK_NUM 255 #define TIMEOUT 10 #define NOWAIT_ON_CONNECT 1 #define CLEAR_NO_WAIT 0x00 #define CLEAR_WAIT_FOR_DATA 0x80 #define LISTEN 0 /* A single packet will be used to simulate tx data */ unsigned char Tx_data[TX_PACKET_LEN + sizeof(x25api_hdr_t)]; /* A singel Rx buffer will be used to accept incoming * data. The buffer will be overwritten with each new * packet. */ unsigned char Rx_data[MAX_RX_PKT_LEN]; /* For each socket created, we will keep the * socket file descriptor and tx/rx pkt counts */ typedef struct sock_api { int sock; int tx; int rx; }sock_api_t; sock_api_t sock_fd[MAX_SOCK_NUM]; int max_sock_fd=0; /*============================================================ * Function Prototypes *===========================================================*/ int MakeConnection( char * ); void socket_event_handler( void); void setup_signal_handlers (void); void quit (int); void handle_oob_event(x25api_t* api_hdr, int sk_index); int issue_clear_call(int i, int opt); int CreateListenSock( char * ); int accept_incoming_call(int sock); /* Defined global so it would only get allocated once. * If it was defined in MakeConnection() function it * would be allocated and dealocated every time we * run that function. */ x25api_t api_cmd; int con_max=MAX_SOCK_NUM; int tx_pkt=NO_FRMS_TO_TX; #define APP_NAME "[server]" void sig_chld (int sigio) { pid_t pid; int stat; while ((pid=waitpid(-1,&stat,WNOHANG)) > 0){ printf("Child %d terminated\n",pid); } return; } /*============================================================= * Main: * * o Make a socket connection to the driver. * o Call send_socket() to transmit data * *============================================================*/ int main(int argc, char* argv[]) { pid_t pid; int stat; int i; if (argc != 2){ printf("Usage: ./wait_client \n"); exit(0); } signal(SIGCHLD,&sig_chld); memset(sock_fd,0,sizeof(sock_fd)); sock_fd[LISTEN].sock = CreateListenSock(argv[argc-1]); if (sock_fd[LISTEN].sock <= 0) return -EINVAL; max_sock_fd = sock_fd[LISTEN].sock; socket_event_handler(); //Clean up for (i=0;i 0){ printf("Child %d terminated\n",pid); } return 0; }; /*************************************************** * socket_event_handler * * o Send and Receive data via socket * o Create a tx packet using x25api_t data type. * o Both the tx and rx packets contains 16 bytes headers * in front of the real data. It is the responsibility * of the applicatino to insert this 16 bytes on tx, and * remove the 16 bytes on rx. * * ------------------------------------------ * | 16 bytes | X bytes ... * ------------------------------------------ * Header Data * * o 16 byte Header: data structure: * * typedef struct { * unsigned char qdm PACKED; Q/D/M bits * unsigned char cause PACKED; cause field * unsigned char diagn PACKED; diagnostics * unsigned char pktType PACKED; * unsigned short length PACKED; * unsigned char result PACKED; * unsigned short lcn PACKED; * char reserved[7] PACKED; * }x25api_hdr_t; * * typedef struct { * x25api_hdr_t hdr PACKED; * char data[X25_MAX_DATA] PACKED; * }x25api_t; * * TX DATA: * -------- * Each tx data packet must contain the above 16 byte header! * * Only relevant byte in the 16 byte tx header, is the * QDM byte. The driver will look at this byte to determine * if the Mbit (more data bit) should be set. * * QDM: byte is a bit map of three bits: * * Q bit: Bit 2 : Qualifier bit, special kind of packet. * Can be used as control packet. * D bit: Bit 1 : Data acknolwedge bit. The remote will * acknowledge every packet sent. * M bit: Bit 0 : If your packet is greater than x25 MTU, * i.e 1024 bytes, than cut the packet to MTU size * and set the M bit to indicate more data. * * RX DATA: * -------- * Each rx data will contain the above 16 byte header! * * Relevant bytes in the 16 byte rx header, are the * LCN and QDM bytes. * * QDM: byte is a bit map of three bits: * * Q bit: Bit 2 : Qualifier bit, special kind of packet. * Can be used as control packet. * D bit: Bit 1 : Data acknolwedge bit. The remote will * acknowledge every packet sent. * M bit: Bit 0 : If your packet is greater than x25 MTU, * i.e 1024 bytes, than cut the packet to MTU size * and set the M bit to indicate more data. * * LCN = contains the lcn number for the rx frame. * * OOB DATA: * --------- * The OOB (out of band) data is used by the driver to * indicate x25 events for the active channel: * clear call, or restarts. * * Each OOB packet contains the above 16 byte header! * * Relevant bytes in the 16 byte oob header, are the * pktType, cause and diagn bytes. * * pktType = event type (ex Clear Call, Restart ...) * cause = x25 cause of the event * diagn = x25 diagnostic information used to determine * the cause of the event. * * Upon receiving an event, the sock should be considered * DEAD!!! Meaning it must be closed using the close() * function. */ void socket_event_handler(void) { unsigned short Tx_lgth,timeout=0; int err=0, i; struct timeval tv; x25api_t* api_tx_el; fd_set writeset,readset,oobset; /* Setup a timeout value for the select statement, which is * going to block until the socket(s) are/is ready to tx/rx * or oob. In this case we will timeout afte 10 seconds of * inactivity */ tv.tv_usec = 0; tv.tv_sec = 10; /* Initialize the tx packet length */ Tx_lgth = TX_PACKET_LEN; /* Cast the x25 16 byte header to the begining * of the tx packet. Using the pointer fill * in appropriate header information such as * QDM bits */ api_tx_el = (x25api_t *)Tx_data; /* Initialize the 16 byte header */ memset(api_tx_el, 0, sizeof(x25api_hdr_t)); /* Set the Mbit (more bit) * currently this option is disabled */ #ifdef _MORE_BIT_SET_ api_tx_el->hdr.qdm = 0x01 #endif /* Fill in the tx packet data with arbitrary * information */ for (i=0; idata[i] = (unsigned char) i; } /* Start an infinite loop which will tx/rx data * over connected x25 svc's */ for(;;) { /* Initialize select() flags */ FD_ZERO(&readset); FD_ZERO(&oobset); FD_ZERO(&writeset); /* For all connected sockets, tell the select * what to wait for. Tx, Rx and OOB */ for (i=0;iqdm, ++sock_fd[i].rx); if (api_data->qdm & 0x01){ /* More bit is set, thus * handle it accordingly */ } } } if (FD_ISSET(sock_fd[i].sock,&writeset)){ /* This socket is ready to tx */ /* The tx packet length contains the 16 byte * header. The tx packet was created above. */ err = send(sock_fd[i].sock, Tx_data, Tx_lgth + sizeof(x25api_hdr_t), 0); /* err contains number of bytes transmited */ if (err>0){ printf("%s: SockId=%i : TX : Cnt=%i\n", APP_NAME, sock_fd[i].sock, ++sock_fd[i].tx); } /* If err<=0 it means that the send failed and that * driver is busy. Thus, the packet should be * requeued for re-transmission on the next * try !!!! */ /* In this example I am clearing the connection * after I sent user specified number of packets */ //if (sock_fd[i].tx >= tx_pkt){ // issue_clear_call(i, CLEAR_WAIT_FOR_DATA); //} } } tx_rx_end: for (i=0;i=con_max) break; }else{ /* select timeout occured. The svc could be * waiting for the accept, or there is something wrong * with the x25 configuration. * * It is up to the user to handle this condition * as they see fit */ if (err == 0){ if (++timeout == 5){ printf("Select() timeout exceeded MAXIMUM\n"); break; } printf("Select() timeout try again !!!\n"); }else{ printf("Error in Select() !!!\n"); break; } }//if select }//for for (i=0;ihdr.pktType){ case ASE_RESET_RQST: printf("%s: SockId=%i : OOB : Rx Reset Call : Lcn=%i : diag=0x%02X : cause=0x%02X\n", APP_NAME, sock_fd[sk_index].sock, api_hdr->hdr.lcn, api_hdr->hdr.diagn, api_hdr->hdr.cause); /* NOTE: we don't have to close the socket, * since the reset doesn't clear the call * however, it means that there is something really * wrong and that data has been lost */ return; case ASE_CLEAR_RQST: printf("%s: SockId=%i : OOB : Rx Clear Call : Lcn=%i : diag=0x%02X : cause=0x%02X\n", APP_NAME, sock_fd[sk_index].sock, api_hdr->hdr.lcn, api_hdr->hdr.diagn, api_hdr->hdr.cause); break; case ASE_RESTART_RQST: printf("%s: SockId=%i : OOB : Rx Restart Req : Lcn=%i : diag=0x%02X : cause=0x%02X\n", APP_NAME, sock_fd[sk_index].sock, api_hdr->hdr.lcn, api_hdr->hdr.diagn, api_hdr->hdr.cause); break; case ASE_INTERRUPT: printf("%s: SockId=%i : OOB : Rx Interrupt Req : Lcn=%i : diag=0x%02X : cause=0x%02X\n", APP_NAME, sock_fd[sk_index].sock, api_hdr->hdr.lcn, api_hdr->hdr.diagn, api_hdr->hdr.cause); break; default: printf("%s: SockId=%i : OOB : Rx Type=0x%02X : Lcn=%i : diag=0x%02X : cause=0x%02X\n", APP_NAME, sock_fd[sk_index].sock, api_hdr->hdr.pktType, api_hdr->hdr.lcn, api_hdr->hdr.diagn, api_hdr->hdr.cause); break; } close(sock_fd[sk_index].sock); sock_fd[sk_index].sock=0; } /* ============================================================ * issue_clear_call * * Input options (opt): * CLEAR_WAIT_FOR_DATA : Fail the clear call if data is * still pending the transmisstion. * This way tx data is not lost * due to the clear call. * CLEAR_NO_WAIT: Clear call regardless of current * pending data. * * NOTE: This function can block, which can cause buffer * overrun on other lcn's. Thus, it should be executed * in a separate process. */ int issue_clear_call(int i, int opt) { volatile int err=0; //FIXME: Fork is expensive use Pthread if (!fork()){ memset(&api_cmd,0,sizeof(api_cmd)); api_cmd.hdr.qdm=opt; for (;;){ err=ioctl(sock_fd[i].sock,SIOC_WANPIPE_CLEAR_CALL,&api_cmd); if (!err) break; //printf("\t%s: SockId=%i: Clear Call Failed : Busy - Trying agin =%i!\n", // APP_NAME, sock_fd[i].sock,err); /* If the state is disconnected, the call has been * cleared, thus break out */ if (ioctl(sock_fd[i].sock,SIOC_WANPIPE_SOCK_STATE,0) == 1){ break; } usleep(100); } printf("%s: SockId=%i: Call Cleared !\n", APP_NAME, sock_fd[i].sock); close(sock_fd[i].sock); sock_fd[i].sock=0; }else{ close(sock_fd[i].sock); sock_fd[i].sock=0; } return 0; } int accept_incoming_call(int sock) { int sock1=0; int err; int index=-1; for (index=0; index < MAX_SOCK_NUM; index++){ if (!sock_fd[index].sock) break; } if (index >= MAX_SOCK_NUM){ printf("%s: Maximum incoming SVCs reached!\n",APP_NAME); return -EINVAL; } sock1 = accept(sock,NULL,NULL); if (sock1 <= 0){ printf ("%s: Sock Accept call failed ! new sock=%i\n",APP_NAME,sock1); return -EINVAL; } if ((err=ioctl(sock1,SIOC_WANPIPE_GET_CALL_DATA,&api_cmd)) != 0){ printf("%s: Failed to get accept call data %i\n",APP_NAME,err); close(sock1); return -EINVAL; }else{ api_cmd.data[api_cmd.hdr.length]=0; } if ((err=ioctl(sock1,SIOC_WANPIPE_ACCEPT_CALL,0)) != 0){ printf("%s: Accept call failed %i\n",APP_NAME,err); close(sock1); return -EINVAL; }else{ printf("%s: Call Accept on LCN=%i\n",APP_NAME,api_cmd.hdr.lcn); } sock_fd[index].sock = sock1; if (sock1 > max_sock_fd){ max_sock_fd=sock1; } return 0; } /*************************************************** * CreateListenSock * * o Create a Socket * o Bind a socket to a wanpipe network interface */ int CreateListenSock (char *i_name ) { int sock=0; struct wan_sockaddr_ll sa; memset(&sa,0,sizeof(struct wan_sockaddr_ll)); /* Create a new socket */ sock = socket( AF_WANPIPE, SOCK_RAW, 0); if( sock < 0 ) { perror("Socket"); return -EINVAL; } /* Fill in binding information * before we use connect() system call * a socket must be binded into a virtual * network interface svc_connect. * * Card name must be supplied as well. In * this case the user supplies the name * eg: wanpipe1. */ sa.sll_family = AF_WANPIPE; sa.sll_protocol = htons(X25_PROT); strcpy(sa.sll_device, "svc_listen"); strcpy(sa.sll_card, i_name); /* Bind a sock using the above address structure */ if(bind(sock, (struct sockaddr *)&sa, sizeof(struct wan_sockaddr_ll)) == -1){ perror("bind"); printf("Failed to bind socket to %s interface\n",i_name); close(sock); return -EINVAL; } if (listen(sock,500) != 0){ close(sock); return -EINVAL; } return sock; }