| /* SCTP kernel Implementation |
| * (C) Copyright IBM Corp. 2003 |
| * |
| * The SCTP implementation 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, or (at your option) |
| * any later version. |
| * |
| * The SCTP implementation is distributed in the hope that it |
| * will be useful, but WITHOUT ANY WARRANTY; without even the implied |
| * ************************ |
| * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with GNU CC; see the file COPYING. If not, write to |
| * the Free Software Foundation, 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| * |
| * Please send any bug reports or fixes you make to the |
| * email address(es): |
| * lksctp developers <lksctp-developers@lists.sourceforge.net> |
| * |
| * Or submit a bug report through the following website: |
| * http://www.sf.net/projects/lksctp |
| * |
| * Any bugs reported given to us we will try to fix... any fixes shared will |
| * be incorporated into the next SCTP release. |
| * |
| * Written or modified by: |
| * Ryan Layer <rmlayer@us.ibm.com> |
| */ |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/uio.h> |
| #include <netinet/in.h> |
| #include <errno.h> |
| #include <netinet/sctp.h> |
| #include <sctputil.h> |
| #include <netdb.h> |
| #include <getopt.h> |
| |
| char *TCID = __FILE__; |
| int TST_TOTAL = 1; |
| int TST_CNT = 0; |
| |
| #define MAXHOSTNAME 64 |
| |
| #define MAXCLIENTNUM 10000 |
| |
| #define TRUE 1 |
| |
| #define SERVER 1 |
| #define CLIENT 0 |
| #define NOT_DEFINED -1 |
| |
| int mode = NOT_DEFINED; |
| |
| int assoc_num, |
| remote_port, |
| local_port; |
| int active = 0; |
| |
| char *local_host = NULL; |
| char *remote_host = NULL; |
| sockaddr_storage_t client_loop, |
| server_loop; |
| struct hostent *hst; |
| char big_buffer[REALLY_BIG]; |
| |
| void usage(char *argv0); |
| void parse_arguments(int argc, char*argv[]); |
| void data_received(struct msghdr *inmessage, int len, int stream, |
| int server_socket); |
| int event_received(struct msghdr *inmessage, int assoc_num); |
| void process_ready_sockets(int client_socket[], int assoc_num, fd_set *rfds); |
| void server_mode(void); |
| void client_mode(void); |
| |
| /* Print the syntax/usage */ |
| void usage(char *argv0) |
| { |
| printf("usage: %s -H localhost -P localport -l|c [-h remotehost]\n" |
| "\t\t[-p remoteport] [-a] [-n <cnt>]\n" |
| " -H\t\tspecify a local address.\n" |
| " -P\t\tspecify the local port number to be used\n" |
| " -l\t\trun in server mode.\n" |
| " -c\t\trun in client mode.\n" |
| " -h\t\tspecify the peer address.\n" |
| " -p\t\tspecify the port number for the peer address.\n" |
| " -a\t\tactively generate traffic with the server.\n" |
| " -n\t\tspecify the number of associations to create.\n", |
| argv0); |
| } |
| |
| /* Parse command line options */ |
| void parse_arguments(int argc, char*argv[]) { |
| int c; |
| |
| while ((c = getopt(argc, argv, ":H:P:ach:ln:p:")) >= 0) { |
| switch (c) { |
| case 'H': |
| local_host = optarg; |
| break; |
| case 'P': |
| local_port = atoi(optarg); |
| break; |
| case 'c': |
| if (mode == NOT_DEFINED) |
| mode = CLIENT; |
| else { |
| usage(argv[0]); |
| exit(0); |
| } |
| break; |
| case 'a': |
| active = 1; |
| break; |
| case 'h': |
| remote_host = optarg; |
| break; |
| case 'l': |
| if (mode == NOT_DEFINED) |
| mode = SERVER; |
| else { |
| usage(argv[0]); |
| exit(0); |
| } |
| break; |
| case 'n': |
| assoc_num = atoi(optarg); |
| break; |
| case 'p': |
| remote_port = atoi(optarg); |
| break; |
| default: |
| usage(argv[0]); |
| exit(0); |
| } |
| } /* while() */ |
| |
| if (mode == CLIENT) { |
| if (assoc_num) { |
| if (assoc_num > MAXCLIENTNUM) { |
| printf("The number of associations indicated " |
| "is greater than the"); |
| printf("max number of associations " |
| "allowed(%d).", MAXCLIENTNUM); |
| usage(argv[0]); |
| exit(0); |
| } |
| } else |
| assoc_num = 1; |
| |
| if (remote_host && remote_port) { |
| hst = gethostbyname(remote_host); |
| |
| memcpy(&server_loop.v4.sin_addr, hst->h_addr_list[0], |
| sizeof(server_loop.v4.sin_addr)); |
| |
| server_loop.v4.sin_family = AF_INET; |
| server_loop.v4.sin_port = htons(remote_port); |
| } else { |
| printf("Remote host and remote port must be defined " |
| "in client mode\n"); |
| usage(argv[0]); |
| exit(0); |
| } |
| |
| if (local_host) { |
| hst = gethostbyname(local_host); |
| |
| memcpy(&client_loop.v4.sin_addr, hst->h_addr_list[0], |
| sizeof(client_loop.v4.sin_addr)); |
| } else |
| client_loop.v4.sin_addr.s_addr = INADDR_ANY; |
| |
| if (local_port) |
| client_loop.v4.sin_port = htons(local_port); |
| else |
| client_loop.v4.sin_port = 0; |
| |
| client_loop.v4.sin_family = AF_INET; |
| } else if (mode == SERVER) { |
| if (active) { |
| printf("This option if for client use only"); |
| usage(argv[0]); |
| exit(0); |
| } |
| |
| if (remote_host || remote_port) { |
| printf("Remote values not needed in server mode.\n"); |
| usage(argv[0]); |
| exit(0); |
| } |
| |
| if (local_host) { |
| hst = gethostbyname(local_host); |
| |
| memcpy(&server_loop.v4.sin_addr, hst->h_addr_list[0], |
| sizeof(server_loop.v4.sin_addr)); |
| } else |
| server_loop.v4.sin_addr.s_addr = INADDR_ANY; |
| |
| if (local_port) |
| server_loop.v4.sin_port = htons(local_port); |
| else { |
| printf("Specify a local port in server mode.\n"); |
| usage(argv[0]); |
| exit(0); |
| } |
| |
| server_loop.v4.sin_family = AF_INET; |
| } else { |
| printf("Must assisgn a client or server mode.\n"); |
| usage(argv[0]); |
| exit(0); |
| } |
| } /* parse_arguments() */ |
| |
| /* Handle data received */ |
| void data_received(struct msghdr *inmessage, int len, int stream, int socket) { |
| |
| int ppid, error; |
| char *ping = "PING"; |
| |
| if (mode == SERVER) { |
| ppid = rand(); |
| |
| error = sctp_sendmsg(socket, |
| inmessage->msg_iov->iov_base, |
| len, |
| (struct sockaddr *)inmessage->msg_name, |
| inmessage->msg_namelen, |
| ppid, |
| 0, |
| stream, |
| 0, 0); |
| |
| if (error < 0) { |
| printf("Send Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| } else { |
| ppid = rand(); |
| |
| printf("Data Received by socket #: %d.\n", socket); |
| printf("\tMessage = %s\n", |
| (char *)inmessage->msg_iov->iov_base); |
| |
| if (active) { |
| error = sctp_sendmsg(socket, ping, strlen(ping) + 1, |
| (struct sockaddr *)&server_loop, |
| sizeof(server_loop), ppid, 0, |
| stream, 0, 0); |
| if (error < 0) { |
| printf("Send Failure: %s.\n", |
| strerror(errno)); |
| DUMP_CORE; |
| } |
| } |
| } |
| } |
| |
| /* This will print what type of SCTP_ASSOC_CHANGE state that was received */ |
| void print_sctp_sac_state(struct msghdr *msg) { |
| |
| char *data; |
| union sctp_notification *sn; |
| |
| if (msg->msg_flags & MSG_NOTIFICATION) { |
| data = (char *)msg->msg_iov[0].iov_base; |
| |
| sn = (union sctp_notification *)data; |
| |
| switch (sn->sn_assoc_change.sac_state) { |
| case SCTP_COMM_UP: |
| printf("SCTP_COMM_UP\n"); |
| break; |
| case SCTP_COMM_LOST: |
| printf("SCTP_COMM_LOST\n"); |
| break; |
| case SCTP_RESTART: |
| printf("SCTP_RESTART"); |
| break; |
| case SCTP_SHUTDOWN_COMP: |
| printf("SCTP_SHUTDOWN_COMP\n"); |
| break; |
| case SCTP_CANT_STR_ASSOC: |
| printf("SCTP_CANT_STR_ASSOC\n"); |
| break; |
| default: |
| break; |
| } |
| } |
| } /* void print_sctp_sac_state() */ |
| |
| /* Tests what type of MSG_NOTIFICATION has been received. |
| * For now this fucntion only works with SCTP_ASSOC_CHANGE |
| * types, but can be easily expanded. |
| * |
| * Will return... |
| * -1 if the msg_flags is not MSG_NOTIFICATION |
| * 0 if the MSG_NOTIFICATION type differs from the type |
| * passed into the additional variable |
| * 1 if the MSG_NOTIFICATION type matches the type |
| * passed into the additional variable |
| */ |
| int test_check_notification_type(struct msghdr *msg, |
| uint16_t sn_type, |
| uint32_t additional) { |
| |
| char *data; |
| union sctp_notification *sn; |
| |
| if (!(msg->msg_flags & MSG_NOTIFICATION)) { |
| return -1; |
| } else { |
| |
| /* Fixup for testframe. */ |
| data = (char *)msg->msg_iov[0].iov_base; |
| |
| sn = (union sctp_notification *)data; |
| |
| if (sn->sn_header.sn_type != sn_type) |
| return 0; |
| else if (sn->sn_header.sn_type == SCTP_ASSOC_CHANGE) |
| if (sn->sn_assoc_change.sac_state == additional) |
| return 1; |
| return 0; |
| } |
| } |
| |
| /* Determine the type of event and make correct adjustments to the |
| * association count |
| */ |
| int event_received(struct msghdr *inmessage, int assoc_num) { |
| |
| int error; |
| |
| printf("Event Received\n"); |
| |
| print_sctp_sac_state(inmessage); |
| |
| if (mode == SERVER) { |
| /* Test type of Event */ |
| error = test_check_notification_type(inmessage, |
| SCTP_ASSOC_CHANGE, |
| SCTP_COMM_UP); |
| if (error > 0) { |
| assoc_num++; |
| printf("Assosiation Established: count = %d.\n", |
| assoc_num); |
| } else { |
| error = test_check_notification_type(inmessage, |
| SCTP_ASSOC_CHANGE, |
| SCTP_SHUTDOWN_COMP); |
| |
| if (error > 0) { |
| assoc_num--; |
| printf("Assosiation Shutdown: count = %d.\n", |
| assoc_num); |
| } |
| } |
| } |
| return assoc_num; |
| } |
| |
| void server_mode() { |
| sockaddr_storage_t msgname; |
| int server_socket, |
| error, |
| stream; |
| int assoc_num =0; |
| struct msghdr inmessage; |
| struct iovec iov; |
| char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))]; |
| |
| |
| printf("Running in Server Mode...\n"); |
| |
| memset(&inmessage, 0, sizeof(inmessage)); |
| iov.iov_base = big_buffer; |
| iov.iov_len = REALLY_BIG; |
| inmessage.msg_iov = &iov; |
| inmessage.msg_iovlen =1; |
| inmessage.msg_control = incmsg; |
| inmessage.msg_controllen = sizeof(incmsg); |
| inmessage.msg_name = &msgname; |
| inmessage.msg_namelen = sizeof (msgname); |
| |
| stream = 1; |
| |
| server_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); |
| if (server_socket < 0) { |
| printf("Socket Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| |
| error = bind(server_socket, &server_loop.sa, sizeof(server_loop)); |
| if (error != 0 ) { |
| printf("Bind Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| |
| error = listen(server_socket, 1); |
| if (error != 0) { |
| printf("Listen Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| while (TRUE) { |
| error = recvmsg(server_socket, &inmessage, MSG_WAITALL); |
| if (error < 0) { |
| printf("Receive Failure: %s\n", |
| strerror(errno)); |
| } else { |
| if (inmessage.msg_flags & MSG_NOTIFICATION) |
| assoc_num = event_received(&inmessage, assoc_num); |
| else |
| data_received(&inmessage, error, stream, server_socket); |
| } |
| } |
| } |
| |
| void client_mode() { |
| |
| int i, error, stream, max_socket = 0; |
| uint32_t ppid = 0; |
| int client_socket[assoc_num]; |
| char *message = "Awake"; |
| fd_set rfds; |
| struct timeval tv; |
| |
| stream = 1; |
| |
| printf("Running in Client Mode...\n"); |
| |
| /* Create the sockets */ |
| for (i = 0; i < assoc_num; i++) { |
| client_socket[i] = socket(PF_INET, SOCK_SEQPACKET, |
| IPPROTO_SCTP); |
| if (client_socket[i] < 0 ){ |
| printf("Socket Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| |
| if (local_port) { |
| error = bind(client_socket[i], &client_loop.sa, |
| sizeof(client_loop)); |
| if (error < 0) { |
| printf("Bind Failure: %s\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| } |
| |
| printf("Create Socket #: %d\n", client_socket[i]); |
| |
| /* Connect to server and send initial message */ |
| error = connect(client_socket[i], &server_loop.sa, |
| sizeof(server_loop)); |
| if (error < 0){ |
| printf("Connect Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| |
| max_socket = client_socket[i]; |
| |
| ppid++; |
| |
| /* Send initial message */ |
| error = sctp_sendmsg(client_socket[i], |
| message, |
| strlen(message) + 1, |
| (struct sockaddr *)&server_loop, |
| sizeof(server_loop), |
| ppid, |
| 0, |
| stream, |
| 0, 0); |
| if (error < 0 ) { |
| printf("Send Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } |
| } |
| |
| while (TRUE){ |
| |
| /* Clear the set for select() */ |
| FD_ZERO(&rfds); |
| |
| /* Set time out values for select() */ |
| tv.tv_sec = 5; |
| tv.tv_usec = 0; |
| |
| /* Add the sockets select() will examine */ |
| for (i = 0; i < assoc_num; i++) { |
| FD_SET(client_socket[i], &rfds); |
| } |
| |
| /* Wait until there is data to be read from one of the |
| * sockets, or until the timer expires |
| */ |
| error = select(max_socket + 1, &rfds, NULL, NULL, &tv); |
| |
| if (error < 0) { |
| printf("Select Failure: %s.\n", strerror(errno)); |
| DUMP_CORE; |
| } else if (error) { |
| /* Loop through the array of sockets to find the ones |
| *that have information to be read |
| */ |
| process_ready_sockets(client_socket, assoc_num, &rfds); |
| } |
| } |
| } |
| |
| void process_ready_sockets(int client_socket[], int assoc_num, fd_set *rfds) { |
| |
| int i, stream, error; |
| struct msghdr inmessage; |
| struct iovec iov; |
| char incmsg[CMSG_SPACE(sizeof (sctp_cmsg_data_t))]; |
| sockaddr_storage_t msgname; |
| |
| /* Setup inmessage to be able to receive in incomming message */ |
| memset(&inmessage, 0, sizeof (inmessage)); |
| iov.iov_base = big_buffer; |
| iov.iov_len = REALLY_BIG; |
| inmessage.msg_iov = &iov; |
| inmessage.msg_iovlen =1; |
| inmessage.msg_control = incmsg; |
| inmessage.msg_controllen = sizeof (incmsg); |
| inmessage.msg_name = &msgname; |
| inmessage.msg_namelen = sizeof (msgname); |
| |
| stream = 1; |
| |
| for( i = 0; i < assoc_num; i++) { |
| if (FD_ISSET(client_socket[i], rfds)) { |
| error = recvmsg(client_socket[i], &inmessage, |
| MSG_WAITALL); |
| if (error < 0) |
| printf("Receive Failure: %s\n", |
| strerror(errno)); |
| else { |
| /* Test to find the type of message that was read(event/data) */ |
| if (inmessage.msg_flags & |
| MSG_NOTIFICATION) |
| event_received(&inmessage, |
| 0); |
| |
| else |
| data_received(&inmessage, error, |
| stream, |
| client_socket[i]); |
| } |
| } |
| } |
| } |
| |
| int main(int argc, char *argv[]) { |
| |
| parse_arguments(argc, argv); |
| |
| if (mode == SERVER) { |
| server_mode(); |
| } else if (mode == CLIENT){ |
| client_mode(); |
| } |
| exit(1); |
| } |
| |