blob: 6759c0ec87d4c2a94ac3a26d02be450a857d3db1 [file] [log] [blame]
/* 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);
}