blob: 6759c0ec87d4c2a94ac3a26d02be450a857d3db1 [file] [log] [blame]
Austin Schuh8d0a2852019-12-28 22:54:28 -08001/* SCTP kernel Implementation
2 * (C) Copyright IBM Corp. 2003
3 *
4 * The SCTP implementation is free software;
5 * you can redistribute it and/or modify it under the terms of
6 * the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * The SCTP implementation is distributed in the hope that it
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * ************************
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU CC; see the file COPYING. If not, write to
18 * the Free Software Foundation, 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
21 * Please send any bug reports or fixes you make to the
22 * email address(es):
23 * lksctp developers <lksctp-developers@lists.sourceforge.net>
24 *
25 * Or submit a bug report through the following website:
26 * http://www.sf.net/projects/lksctp
27 *
28 * Any bugs reported given to us we will try to fix... any fixes shared will
29 * be incorporated into the next SCTP release.
30 *
31 * Written or modified by:
32 * Ryan Layer <rmlayer@us.ibm.com>
33 */
34
35#include <stdio.h>
36#include <unistd.h>
37#include <stdlib.h>
38#include <string.h>
39#include <fcntl.h>
40#include <sys/types.h>
41#include <sys/socket.h>
42#include <sys/uio.h>
43#include <netinet/in.h>
44#include <errno.h>
45#include <netinet/sctp.h>
46#include <sctputil.h>
47#include <netdb.h>
48#include <getopt.h>
49
50char *TCID = __FILE__;
51int TST_TOTAL = 1;
52int TST_CNT = 0;
53
54#define MAXHOSTNAME 64
55
56#define MAXCLIENTNUM 10000
57
58#define TRUE 1
59
60#define SERVER 1
61#define CLIENT 0
62#define NOT_DEFINED -1
63
64int mode = NOT_DEFINED;
65
66int assoc_num,
67 remote_port,
68 local_port;
69int active = 0;
70
71char *local_host = NULL;
72char *remote_host = NULL;
73sockaddr_storage_t client_loop,
74 server_loop;
75struct hostent *hst;
76char big_buffer[REALLY_BIG];
77
78void usage(char *argv0);
79void parse_arguments(int argc, char*argv[]);
80void data_received(struct msghdr *inmessage, int len, int stream,
81 int server_socket);
82int event_received(struct msghdr *inmessage, int assoc_num);
83void process_ready_sockets(int client_socket[], int assoc_num, fd_set *rfds);
84void server_mode(void);
85void client_mode(void);
86
87/* Print the syntax/usage */
88void usage(char *argv0)
89{
90 printf("usage: %s -H localhost -P localport -l|c [-h remotehost]\n"
91 "\t\t[-p remoteport] [-a] [-n <cnt>]\n"
92 " -H\t\tspecify a local address.\n"
93 " -P\t\tspecify the local port number to be used\n"
94 " -l\t\trun in server mode.\n"
95 " -c\t\trun in client mode.\n"
96 " -h\t\tspecify the peer address.\n"
97 " -p\t\tspecify the port number for the peer address.\n"
98 " -a\t\tactively generate traffic with the server.\n"
99 " -n\t\tspecify the number of associations to create.\n",
100 argv0);
101}
102
103/* Parse command line options */
104void parse_arguments(int argc, char*argv[]) {
105 int c;
106
107 while ((c = getopt(argc, argv, ":H:P:ach:ln:p:")) >= 0) {
108 switch (c) {
109 case 'H':
110 local_host = optarg;
111 break;
112 case 'P':
113 local_port = atoi(optarg);
114 break;
115 case 'c':
116 if (mode == NOT_DEFINED)
117 mode = CLIENT;
118 else {
119 usage(argv[0]);
120 exit(0);
121 }
122 break;
123 case 'a':
124 active = 1;
125 break;
126 case 'h':
127 remote_host = optarg;
128 break;
129 case 'l':
130 if (mode == NOT_DEFINED)
131 mode = SERVER;
132 else {
133 usage(argv[0]);
134 exit(0);
135 }
136 break;
137 case 'n':
138 assoc_num = atoi(optarg);
139 break;
140 case 'p':
141 remote_port = atoi(optarg);
142 break;
143 default:
144 usage(argv[0]);
145 exit(0);
146 }
147 } /* while() */
148
149 if (mode == CLIENT) {
150 if (assoc_num) {
151 if (assoc_num > MAXCLIENTNUM) {
152 printf("The number of associations indicated "
153 "is greater than the");
154 printf("max number of associations "
155 "allowed(%d).", MAXCLIENTNUM);
156 usage(argv[0]);
157 exit(0);
158 }
159 } else
160 assoc_num = 1;
161
162 if (remote_host && remote_port) {
163 hst = gethostbyname(remote_host);
164
165 memcpy(&server_loop.v4.sin_addr, hst->h_addr_list[0],
166 sizeof(server_loop.v4.sin_addr));
167
168 server_loop.v4.sin_family = AF_INET;
169server_loop.v4.sin_port = htons(remote_port);
170 } else {
171 printf("Remote host and remote port must be defined "
172 "in client mode\n");
173 usage(argv[0]);
174 exit(0);
175 }
176
177 if (local_host) {
178 hst = gethostbyname(local_host);
179
180 memcpy(&client_loop.v4.sin_addr, hst->h_addr_list[0],
181 sizeof(client_loop.v4.sin_addr));
182 } else
183 client_loop.v4.sin_addr.s_addr = INADDR_ANY;
184
185 if (local_port)
186 client_loop.v4.sin_port = htons(local_port);
187 else
188 client_loop.v4.sin_port = 0;
189
190 client_loop.v4.sin_family = AF_INET;
191 } else if (mode == SERVER) {
192 if (active) {
193 printf("This option if for client use only");
194 usage(argv[0]);
195 exit(0);
196 }
197
198 if (remote_host || remote_port) {
199 printf("Remote values not needed in server mode.\n");
200 usage(argv[0]);
201 exit(0);
202 }
203
204 if (local_host) {
205 hst = gethostbyname(local_host);
206
207 memcpy(&server_loop.v4.sin_addr, hst->h_addr_list[0],
208 sizeof(server_loop.v4.sin_addr));
209 } else
210 server_loop.v4.sin_addr.s_addr = INADDR_ANY;
211
212 if (local_port)
213 server_loop.v4.sin_port = htons(local_port);
214 else {
215 printf("Specify a local port in server mode.\n");
216 usage(argv[0]);
217 exit(0);
218 }
219
220 server_loop.v4.sin_family = AF_INET;
221 } else {
222 printf("Must assisgn a client or server mode.\n");
223 usage(argv[0]);
224 exit(0);
225 }
226} /* parse_arguments() */
227
228/* Handle data received */
229void data_received(struct msghdr *inmessage, int len, int stream, int socket) {
230
231 int ppid, error;
232 char *ping = "PING";
233
234 if (mode == SERVER) {
235 ppid = rand();
236
237 error = sctp_sendmsg(socket,
238 inmessage->msg_iov->iov_base,
239 len,
240 (struct sockaddr *)inmessage->msg_name,
241 inmessage->msg_namelen,
242 ppid,
243 0,
244 stream,
245 0, 0);
246
247 if (error < 0) {
248 printf("Send Failure: %s.\n", strerror(errno));
249 DUMP_CORE;
250 }
251 } else {
252 ppid = rand();
253
254 printf("Data Received by socket #: %d.\n", socket);
255 printf("\tMessage = %s\n",
256 (char *)inmessage->msg_iov->iov_base);
257
258 if (active) {
259 error = sctp_sendmsg(socket, ping, strlen(ping) + 1,
260 (struct sockaddr *)&server_loop,
261 sizeof(server_loop), ppid, 0,
262 stream, 0, 0);
263 if (error < 0) {
264 printf("Send Failure: %s.\n",
265 strerror(errno));
266 DUMP_CORE;
267 }
268 }
269 }
270}
271
272/* This will print what type of SCTP_ASSOC_CHANGE state that was received */
273void print_sctp_sac_state(struct msghdr *msg) {
274
275 char *data;
276 union sctp_notification *sn;
277
278 if (msg->msg_flags & MSG_NOTIFICATION) {
279 data = (char *)msg->msg_iov[0].iov_base;
280
281 sn = (union sctp_notification *)data;
282
283 switch (sn->sn_assoc_change.sac_state) {
284 case SCTP_COMM_UP:
285 printf("SCTP_COMM_UP\n");
286 break;
287 case SCTP_COMM_LOST:
288 printf("SCTP_COMM_LOST\n");
289 break;
290 case SCTP_RESTART:
291 printf("SCTP_RESTART");
292 break;
293 case SCTP_SHUTDOWN_COMP:
294 printf("SCTP_SHUTDOWN_COMP\n");
295 break;
296 case SCTP_CANT_STR_ASSOC:
297 printf("SCTP_CANT_STR_ASSOC\n");
298 break;
299 default:
300 break;
301 }
302 }
303} /* void print_sctp_sac_state() */
304
305/* Tests what type of MSG_NOTIFICATION has been received.
306* For now this fucntion only works with SCTP_ASSOC_CHANGE
307* types, but can be easily expanded.
308*
309* Will return...
310* -1 if the msg_flags is not MSG_NOTIFICATION
311* 0 if the MSG_NOTIFICATION type differs from the type
312* passed into the additional variable
313* 1 if the MSG_NOTIFICATION type matches the type
314* passed into the additional variable
315*/
316int test_check_notification_type(struct msghdr *msg,
317 uint16_t sn_type,
318 uint32_t additional) {
319
320 char *data;
321 union sctp_notification *sn;
322
323 if (!(msg->msg_flags & MSG_NOTIFICATION)) {
324 return -1;
325 } else {
326
327 /* Fixup for testframe. */
328 data = (char *)msg->msg_iov[0].iov_base;
329
330 sn = (union sctp_notification *)data;
331
332 if (sn->sn_header.sn_type != sn_type)
333 return 0;
334 else if (sn->sn_header.sn_type == SCTP_ASSOC_CHANGE)
335 if (sn->sn_assoc_change.sac_state == additional)
336 return 1;
337 return 0;
338 }
339}
340
341/* Determine the type of event and make correct adjustments to the
342* association count
343*/
344int event_received(struct msghdr *inmessage, int assoc_num) {
345
346 int error;
347
348 printf("Event Received\n");
349
350 print_sctp_sac_state(inmessage);
351
352 if (mode == SERVER) {
353 /* Test type of Event */
354 error = test_check_notification_type(inmessage,
355 SCTP_ASSOC_CHANGE,
356 SCTP_COMM_UP);
357 if (error > 0) {
358 assoc_num++;
359 printf("Assosiation Established: count = %d.\n",
360 assoc_num);
361 } else {
362 error = test_check_notification_type(inmessage,
363 SCTP_ASSOC_CHANGE,
364 SCTP_SHUTDOWN_COMP);
365
366 if (error > 0) {
367 assoc_num--;
368 printf("Assosiation Shutdown: count = %d.\n",
369 assoc_num);
370 }
371 }
372 }
373 return assoc_num;
374}
375
376void server_mode() {
377 sockaddr_storage_t msgname;
378 int server_socket,
379 error,
380 stream;
381 int assoc_num =0;
382 struct msghdr inmessage;
383 struct iovec iov;
384 char incmsg[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
385
386
387 printf("Running in Server Mode...\n");
388
389 memset(&inmessage, 0, sizeof(inmessage));
390 iov.iov_base = big_buffer;
391 iov.iov_len = REALLY_BIG;
392 inmessage.msg_iov = &iov;
393 inmessage.msg_iovlen =1;
394 inmessage.msg_control = incmsg;
395 inmessage.msg_controllen = sizeof(incmsg);
396 inmessage.msg_name = &msgname;
397 inmessage.msg_namelen = sizeof (msgname);
398
399 stream = 1;
400
401 server_socket = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
402 if (server_socket < 0) {
403 printf("Socket Failure: %s.\n", strerror(errno));
404 DUMP_CORE;
405 }
406
407 error = bind(server_socket, &server_loop.sa, sizeof(server_loop));
408 if (error != 0 ) {
409 printf("Bind Failure: %s.\n", strerror(errno));
410 DUMP_CORE;
411 }
412
413 error = listen(server_socket, 1);
414 if (error != 0) {
415 printf("Listen Failure: %s.\n", strerror(errno));
416 DUMP_CORE;
417 }
418 while (TRUE) {
419 error = recvmsg(server_socket, &inmessage, MSG_WAITALL);
420 if (error < 0) {
421 printf("Receive Failure: %s\n",
422 strerror(errno));
423 } else {
424 if (inmessage.msg_flags & MSG_NOTIFICATION)
425 assoc_num = event_received(&inmessage, assoc_num);
426 else
427 data_received(&inmessage, error, stream, server_socket);
428 }
429 }
430}
431
432void client_mode() {
433
434 int i, error, stream, max_socket = 0;
435 uint32_t ppid = 0;
436 int client_socket[assoc_num];
437 char *message = "Awake";
438 fd_set rfds;
439 struct timeval tv;
440
441 stream = 1;
442
443 printf("Running in Client Mode...\n");
444
445 /* Create the sockets */
446 for (i = 0; i < assoc_num; i++) {
447 client_socket[i] = socket(PF_INET, SOCK_SEQPACKET,
448 IPPROTO_SCTP);
449 if (client_socket[i] < 0 ){
450 printf("Socket Failure: %s.\n", strerror(errno));
451 DUMP_CORE;
452 }
453
454 if (local_port) {
455 error = bind(client_socket[i], &client_loop.sa,
456 sizeof(client_loop));
457 if (error < 0) {
458 printf("Bind Failure: %s\n", strerror(errno));
459 DUMP_CORE;
460 }
461 }
462
463 printf("Create Socket #: %d\n", client_socket[i]);
464
465 /* Connect to server and send initial message */
466 error = connect(client_socket[i], &server_loop.sa,
467 sizeof(server_loop));
468 if (error < 0){
469 printf("Connect Failure: %s.\n", strerror(errno));
470 DUMP_CORE;
471 }
472
473 max_socket = client_socket[i];
474
475 ppid++;
476
477 /* Send initial message */
478 error = sctp_sendmsg(client_socket[i],
479 message,
480 strlen(message) + 1,
481 (struct sockaddr *)&server_loop,
482 sizeof(server_loop),
483 ppid,
484 0,
485 stream,
486 0, 0);
487 if (error < 0 ) {
488 printf("Send Failure: %s.\n", strerror(errno));
489 DUMP_CORE;
490 }
491 }
492
493 while (TRUE){
494
495 /* Clear the set for select() */
496 FD_ZERO(&rfds);
497
498 /* Set time out values for select() */
499 tv.tv_sec = 5;
500 tv.tv_usec = 0;
501
502 /* Add the sockets select() will examine */
503 for (i = 0; i < assoc_num; i++) {
504 FD_SET(client_socket[i], &rfds);
505 }
506
507 /* Wait until there is data to be read from one of the
508 * sockets, or until the timer expires
509 */
510 error = select(max_socket + 1, &rfds, NULL, NULL, &tv);
511
512 if (error < 0) {
513 printf("Select Failure: %s.\n", strerror(errno));
514 DUMP_CORE;
515 } else if (error) {
516 /* Loop through the array of sockets to find the ones
517 *that have information to be read
518 */
519 process_ready_sockets(client_socket, assoc_num, &rfds);
520 }
521 }
522}
523
524void process_ready_sockets(int client_socket[], int assoc_num, fd_set *rfds) {
525
526 int i, stream, error;
527 struct msghdr inmessage;
528 struct iovec iov;
529 char incmsg[CMSG_SPACE(sizeof (sctp_cmsg_data_t))];
530 sockaddr_storage_t msgname;
531
532 /* Setup inmessage to be able to receive in incomming message */
533 memset(&inmessage, 0, sizeof (inmessage));
534 iov.iov_base = big_buffer;
535 iov.iov_len = REALLY_BIG;
536 inmessage.msg_iov = &iov;
537 inmessage.msg_iovlen =1;
538 inmessage.msg_control = incmsg;
539 inmessage.msg_controllen = sizeof (incmsg);
540 inmessage.msg_name = &msgname;
541 inmessage.msg_namelen = sizeof (msgname);
542
543 stream = 1;
544
545 for( i = 0; i < assoc_num; i++) {
546 if (FD_ISSET(client_socket[i], rfds)) {
547 error = recvmsg(client_socket[i], &inmessage,
548 MSG_WAITALL);
549 if (error < 0)
550 printf("Receive Failure: %s\n",
551 strerror(errno));
552 else {
553 /* Test to find the type of message that was read(event/data) */
554 if (inmessage.msg_flags &
555 MSG_NOTIFICATION)
556 event_received(&inmessage,
557 0);
558
559 else
560 data_received(&inmessage, error,
561 stream,
562 client_socket[i]);
563 }
564 }
565 }
566}
567
568int main(int argc, char *argv[]) {
569
570 parse_arguments(argc, argv);
571
572 if (mode == SERVER) {
573 server_mode();
574 } else if (mode == CLIENT){
575 client_mode();
576 }
577 exit(1);
578}
579