/* SCTP kernel reference Implementation * (C) Copyright Fujitsu Ltd. 2008, 2009 * * The SCTP reference 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 reference 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 * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp * * Any bugs reported to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. * * Written or modified by: * Wei Yongjun */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_SEC 0 #define DEFAULT_USEC 5000 #define REALLY_BIG 65536 #define SERVER 0 #define CLIENT 1 #define NOT_DEFINED 666 #define DEBUG_NONE 0 #define DEBUG_MIN 1 #define DEBUG_MAX 2 #define ORDER_PATTERN_UNORDERED 0 #define ORDER_PATTERN_ORDERED 1 #define ORDER_PATTERN_ALTERNATE 2 #define ORDER_PATTERN_RANDOM 3 #define STREAM_PATTERN_SEQUENTIAL 0 #define STREAM_PATTERN_RANDOM 1 #define MAX_BIND_RETRYS 10 #define BIG_REPEAT 1000000 #define REPEAT 10 #define DEFAULT_MAX_WINDOW 32768 #define DEFAULT_MIN_WINDOW 1500 #define MSG_CNT 10 #define DEBUG_PRINT(level, print_this...) \ { \ if (debug_level >= level) { \ fprintf(stdout, print_this); \ fflush(stdout); \ } \ } /* DEBUG_PRINT */ char *local_host = NULL; int local_port = 0; char *remote_host = NULL; int remote_port = 0; struct sockaddr_storage s_rem, s_loc; int r_len, l_len; int size_arg = 0; int debug_level = DEBUG_NONE; int order_pattern = ORDER_PATTERN_UNORDERED; int order_state = 0; int stream_pattern = STREAM_PATTERN_SEQUENTIAL; int stream_state = 0; int repeat = REPEAT; int repeat_count = 0; int max_msgsize = DEFAULT_MAX_WINDOW; int msg_cnt = MSG_CNT; int drain = 0; int max_stream = 0; int gsk = -1; int period = 1; char *statusfile = NULL; void printstatus(int sk); void sighandler(int signo); void settimerhandle(void); void usage(char *argv0); void start_test(int role); unsigned char msg[] = "012345678901234567890123456789012345678901234567890"; /* Convenience structure to determine space needed for cmsg. */ typedef union { struct sctp_initmsg init; struct sctp_sndrcvinfo sndrcvinfo; } _sctp_cmsg_data_t; int main(int argc, char *argv[]) { int c, role = NOT_DEFINED; char *interface = NULL; struct sockaddr_in *t_addr; struct sockaddr_in6 *t_addr6; /* Parse the arguments. */ while ((c = getopt(argc, argv, ":H:L:P:h:p:c:d:lm:sx:X:o:M:Di:I:f:")) >= 0 ) { switch (c) { case 'H': local_host = optarg; break; case 'P': local_port = atoi(optarg); break; case 'h': remote_host = optarg; break; case 'p': remote_port = atoi(optarg); break; case 'l': if (role != NOT_DEFINED) { printf("%s: only -s or -l\n", argv[0]); usage(argv[0]); exit(1); } role = SERVER; break; case 's': if (role != NOT_DEFINED) { printf("%s: only -s or -l\n", argv[0]); usage(argv[0]); exit(1); } role = CLIENT; break; case 'D': drain = 1; break; case 'd': debug_level = atoi(optarg); if (debug_level < DEBUG_NONE || debug_level > DEBUG_MAX) { usage(argv[0]); exit(1); } break; case 'I': period = atoi(optarg); if (period < 0) { usage(argv[0]); exit(1); } break; case 'x': repeat = atoi(optarg); if (!repeat) { repeat = BIG_REPEAT; } break; case 'X': msg_cnt = atoi(optarg); if ((msg_cnt <= 0) || (msg_cnt > MSG_CNT)) { usage(argv[0]); exit(1); } break; case 'c': size_arg = atoi(optarg); if (size_arg < 0) { usage(argv[0]); exit(1); } break; case 'o': order_pattern = atoi(optarg); if (order_pattern < ORDER_PATTERN_UNORDERED || order_pattern > ORDER_PATTERN_RANDOM ) { usage(argv[0]); exit(1); } break; case 'M': max_stream = atoi(optarg); if (max_stream < 0 || max_stream >= (1<<16)) { usage(argv[0]); exit(1); } break; case 'm': max_msgsize = atoi(optarg); break; case 'i': interface = optarg; break; case 'f': statusfile = optarg; break; case '?': default: usage(argv[0]); exit(0); } } /* while() */ if (NOT_DEFINED == role) { usage(argv[0]); exit(1); } if (SERVER == role && NULL == local_host && remote_host != NULL) { fprintf(stderr, "%s: Server needs local address, " "not remote address\n", argv[0]); usage(argv[0]); exit(1); } if (CLIENT == role && NULL == remote_host) { fprintf(stderr, "%s: Client needs at least remote address " "& port\n", argv[0]); usage(argv[0]); exit(1); } if (optind < argc) { fprintf(stderr, "%s: non-option arguments are illegal: ", argv[0]); while (optind < argc) fprintf(stderr, "%s ", argv[optind++]); fprintf (stderr, "\n"); usage(argv[0]); exit(1); } if (remote_host != NULL && remote_port != 0) { struct addrinfo *res; int error; char *host_s, *serv_s; if ((host_s = malloc(NI_MAXHOST)) == NULL) { fprintf(stderr, "\n*** host_s malloc failed!!! ***\n"); exit(1); } if ((serv_s = malloc(NI_MAXSERV)) == NULL) { fprintf(stderr, "\n*** serv_s malloc failed!!! ***\n"); exit(1); } error = getaddrinfo(remote_host, 0, NULL, &res); if (error) { printf("%s.\n", gai_strerror(error)); usage(argv[0]); exit(1); } switch (res->ai_family) { case AF_INET: t_addr = (struct sockaddr_in *)&s_rem; memcpy(t_addr, res->ai_addr, res->ai_addrlen); t_addr->sin_family = res->ai_family; t_addr->sin_port = htons(remote_port); r_len = res->ai_addrlen; #ifdef __FreeBSD__ t_addr->sin_len = r_len; #endif break; case AF_INET6: t_addr6 = (struct sockaddr_in6 *)&s_rem; memcpy(t_addr6, res->ai_addr, res->ai_addrlen); t_addr6->sin6_family = res->ai_family; t_addr6->sin6_port = htons(remote_port); if (interface) t_addr6->sin6_scope_id = if_nametoindex(interface); r_len = res->ai_addrlen; #ifdef __FreeBSD__ t_addr6->sin6_len = r_len; #endif break; } getnameinfo((struct sockaddr *)&s_rem, r_len, host_s, NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST); DEBUG_PRINT(DEBUG_MAX, "remote:addr=%s, port=%s, family=%d\n", host_s, serv_s, res->ai_family); freeaddrinfo(res); } if (local_host != NULL) { struct addrinfo *res; int error; char *host_s, *serv_s; struct sockaddr_in *t_addr; struct sockaddr_in6 *t_addr6; if ((host_s = malloc(NI_MAXHOST)) == NULL) { fprintf(stderr, "\n*** host_s malloc failed!!! ***\n"); exit(1); } if ((serv_s = malloc(NI_MAXSERV)) == NULL) { fprintf(stderr, "\n*** serv_s malloc failed!!! ***\n"); exit(1); } if (strcmp(local_host, "0") == 0) local_host = "0.0.0.0"; error = getaddrinfo(local_host, 0, NULL, &res); if (error) { printf("%s.\n", gai_strerror(error)); usage(argv[0]); exit(1); } switch (res->ai_family) { case AF_INET: t_addr = (struct sockaddr_in *)&s_loc; memcpy(t_addr, res->ai_addr, res->ai_addrlen); t_addr->sin_family = res->ai_family; t_addr->sin_port = htons(local_port); l_len = res->ai_addrlen; #ifdef __FreeBSD__ t_addr->sin_len = l_len; #endif break; case AF_INET6: t_addr6 = (struct sockaddr_in6 *)&s_loc; memcpy(t_addr6, res->ai_addr, res->ai_addrlen); t_addr6->sin6_family = res->ai_family; t_addr6->sin6_port = htons(local_port); if (interface) t_addr6->sin6_scope_id = if_nametoindex(interface); l_len = res->ai_addrlen; #ifdef __FreeBSD__ t_addr6->sin6_len = l_len; #endif break; } error = getnameinfo((struct sockaddr *)&s_loc, l_len, host_s, NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST); if (error) printf("%s..\n", gai_strerror(error)); DEBUG_PRINT(DEBUG_MAX, "local:addr=%s, port=%s, family=%d\n", host_s, serv_s, res->ai_family); freeaddrinfo(res); } /* Let the testing begin. */ start_test(role); return 0; } int bind_r(int sk, struct sockaddr_storage *saddr) { int error = 0, i = 0; char *host_s, *serv_s; if ((host_s = malloc(NI_MAXHOST)) == NULL) { fprintf(stderr, "\n\t\t*** host_s malloc failed!!! ***\n"); exit(1); } if ((serv_s = malloc(NI_MAXSERV)) == NULL) { fprintf(stderr, "\n\t\t*** serv_s malloc failed!!! ***\n"); exit(1); } do { if (i > 0) sleep(1); /* sleep a while before new try... */ error = getnameinfo((struct sockaddr *)saddr, l_len, host_s, NI_MAXHOST, serv_s, NI_MAXSERV, NI_NUMERICHOST); if (error) printf("%s\n", gai_strerror(error)); DEBUG_PRINT(DEBUG_MIN, "\tbind(sk=%d, [a:%s,p:%s]) -- attempt %d/%d\n", sk, host_s, serv_s, i+1, MAX_BIND_RETRYS); error = bind(sk, (struct sockaddr *)saddr, l_len); if (error != 0) { if( errno != EADDRINUSE ) { fprintf(stderr, "\n\n\t\t***bind: can " "not bind to %s:%s: %s ****\n", host_s, serv_s, strerror(errno)); exit(1); } } i++; if (i >= MAX_BIND_RETRYS) { fprintf(stderr, "Maximum bind() attempts. " "Die now...\n\n"); exit(1); } } while (error < 0 && i < MAX_BIND_RETRYS); return 0; } /* bind_r() */ int listen_r(int sk, int listen_count) { int error = 0; DEBUG_PRINT(DEBUG_MIN, "\tlisten(sk=%d,backlog=%d)\n", sk, listen_count); /* Mark sk as being able to accept new associations */ error = listen(sk, 1); if (error != 0) { fprintf(stderr, "\n\n\t\t*** listen: %s ***\n\n\n", strerror(errno)); exit(1); } return 0; } /* listen_r() */ int accept_r(int sk){ socklen_t len = 0; DEBUG_PRINT(DEBUG_MIN, "\taccept(sk=%d)\n", sk); gsk = accept(sk, NULL, &len); if (gsk < 0) { fprintf(stderr, "\n\n\t\t*** accept: %s ***\n\n\n", strerror(errno)); exit(1); } return 0; } /* accept_r() */ int connect_r(int sk, const struct sockaddr *serv_addr, socklen_t addrlen) { int error = 0; DEBUG_PRINT(DEBUG_MIN, "\tconnect(sk=%d)\n", sk); /* Mark sk as being able to accept new associations */ error = connect(sk, serv_addr, addrlen); if (error != 0) { fprintf(stderr, "\n\n\t\t*** connect: %s ***\n\n\n", strerror(errno)); exit(1); } gsk = sk; return 0; } /* connect_r() */ int close_r(int sk) { int error = 0; DEBUG_PRINT(DEBUG_MIN, "\tclose(sk=%d)\n",sk); error = close(sk); if (error != 0) { fprintf(stderr, "\n\n\t\t*** close: %s ***\n\n", strerror(errno)); exit(1); } fflush(stdout); return 0; } /* close_r() */ int receive_r(int sk) { int error = 0; char incmsg[CMSG_SPACE(sizeof(_sctp_cmsg_data_t))]; struct iovec iov; struct msghdr inmessage; /* Initialize inmessage with enough space for DATA... */ memset(&inmessage, 0, sizeof(inmessage)); if ((iov.iov_base = malloc(REALLY_BIG)) == NULL) { fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n"); exit(1); } iov.iov_len = REALLY_BIG; inmessage.msg_iov = &iov; inmessage.msg_iovlen = 1; /* or a control message. */ inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); /* Get the messages sent */ while (1) { DEBUG_PRINT(DEBUG_MIN, "\trecvmsg(sk=%d) ", sk); error = recvmsg(sk, &inmessage, MSG_WAITALL); if (error < 0 && errno != EAGAIN) { fprintf(stderr, "\n\t\t*** recvmsg: %s ***\n\n", strerror(errno)); fflush(stdout); close(sk); free(iov.iov_base); exit(1); } else if (error == 0) { printf("\n\t\trecvmsg() returned 0 !!!!\n"); fflush(stdout); } if(MSG_NOTIFICATION & inmessage.msg_flags) continue; /* got a notification... */ inmessage.msg_control = incmsg; inmessage.msg_controllen = sizeof(incmsg); iov.iov_len = REALLY_BIG; break; } free(iov.iov_base); return 0; } /* receive_r () */ void server(int sk) { int i; if (max_msgsize > DEFAULT_MAX_WINDOW) { if (setsockopt(sk, IPPROTO_SCTP, SO_RCVBUF, &max_msgsize, sizeof(max_msgsize)) < 0) { perror("setsockopt(SO_RCVBUF)"); exit(1); } } for (i = 0; i < msg_cnt; i++) { receive_r(sk); DEBUG_PRINT(DEBUG_MIN, "count %d\n", i+1); } } /* server() */ void * build_msg(int len) { int i = len - 1; int n; char *msg_buf, *p; msg_buf = malloc(len); if (NULL == msg_buf) { fprintf(stderr, "\n\t\t*** malloc not enough memory!!! ***\n"); exit(1); } p = msg_buf; do { n = ((i > 50)?50:i); memcpy(p, msg, ((i > 50)?50:i)); p += n; i -= n; } while (i > 0); msg_buf[len-1] = '\0'; return(msg_buf); } /* build_msg() */ int send_r(int sk, int stream, int order, int send_size, int assoc_i) { int error = 0; struct msghdr outmsg; struct iovec iov; char *message = NULL; int msglen = 0; char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; struct cmsghdr *cmsg; struct sctp_sndrcvinfo *sinfo; if (send_size > 0) { message = build_msg(send_size); msglen = strlen(message) + 1; iov.iov_base = message; iov.iov_len = msglen; } else { exit(1); } outmsg.msg_name = &s_rem; outmsg.msg_namelen = sizeof(struct sockaddr_storage); outmsg.msg_iov = &iov; outmsg.msg_iovlen = 1; outmsg.msg_control = outcmsg; outmsg.msg_controllen = sizeof(outcmsg); outmsg.msg_flags = 0; cmsg = CMSG_FIRSTHDR(&outmsg); cmsg->cmsg_level = IPPROTO_SCTP; cmsg->cmsg_type = SCTP_SNDRCV; cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); outmsg.msg_controllen = cmsg->cmsg_len; sinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg); memset(sinfo, 0, sizeof(struct sctp_sndrcvinfo)); sinfo->sinfo_ppid = rand(); sinfo->sinfo_stream = stream; sinfo->sinfo_flags = 0; if (!order) sinfo->sinfo_flags = SCTP_UNORDERED; DEBUG_PRINT(DEBUG_MIN, "\tsendmsg(sk=%d, assoc=%d) %4d bytes.\n", sk, assoc_i, send_size); DEBUG_PRINT(DEBUG_MAX, "\t SNDRCV"); if (DEBUG_MAX == debug_level) { printf("(stream=%u ", sinfo->sinfo_stream); printf("flags=0x%x ", sinfo->sinfo_flags); printf("ppid=%u)\n", sinfo->sinfo_ppid); } /* Send to our neighbor. */ error = sendmsg(sk, &outmsg, MSG_WAITALL); if (error != msglen) { fprintf(stderr, "\n\t\t*** sendmsg: %s ***\n\n", strerror(errno)); fflush(stdout); exit(1); } if (send_size > 0) free(message); return 0; } /* send_r() */ int next_order(int state, int pattern) { switch (pattern){ case ORDER_PATTERN_UNORDERED: state = 0; break; case ORDER_PATTERN_ORDERED: state = 1; break; case ORDER_PATTERN_ALTERNATE: state = state ? 0 : 1; break; case ORDER_PATTERN_RANDOM: state = rand() % 2; break; } return state; } int next_stream(int state, int pattern) { switch (pattern){ case STREAM_PATTERN_RANDOM: state = rand() % max_stream; break; case STREAM_PATTERN_SEQUENTIAL: state = state + 1; if (state >= max_stream) state = 0; break; } return state; } int next_msg_size(int msg_cnt) { int msg_size; if (size_arg) { msg_size = size_arg; } else { msg_size = (rand() % max_msgsize) + 1; } return msg_size; } /* next_msg_size() */ void client(int sk) { int msg_size; int i; for (i = 0; i < msg_cnt; i++) { msg_size = next_msg_size(i); order_state = next_order(order_state, order_pattern); stream_state = next_stream(stream_state, stream_pattern); if (send_r(sk, stream_state, order_state, msg_size, 0) < 0) { close(sk); break; } /* The sender is echoing so do discard the echoed data. */ if (drain && ((i + 1) % period == 0)) { receive_r(sk); } } } /* client() */ void start_test(int role) { int sk, pid, ret; int i = 0; DEBUG_PRINT(DEBUG_NONE, "\nStarting tests...\n"); repeat_count = repeat; DEBUG_PRINT(DEBUG_MIN, "\tsocket(SOCK_STREAM, IPPROTO_SCTP)"); if ((sk = socket(s_loc.ss_family, SOCK_STREAM, IPPROTO_SCTP)) < 0 ) { fprintf(stderr, "\n\n\t\t*** socket: failed to create" " socket: %s ***\n", strerror(errno)); exit(1); } DEBUG_PRINT(DEBUG_MIN, " -> sk=%d\n", sk); bind_r(sk, &s_loc); if (role == SERVER) { listen_r(sk, 1); accept_r(sk); } else { if (max_stream > 0) { struct sctp_initmsg initmsg; memset(&initmsg, 0, sizeof(initmsg)); initmsg.sinit_num_ostreams = max_stream; initmsg.sinit_max_instreams = max_stream; initmsg.sinit_max_attempts = 3; ret = setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg)); if (ret < 0) { perror("setsockopt(SCTP_INITMSG)"); exit(0); } } connect_r(sk, (struct sockaddr *)&s_rem, r_len); } if ((pid = fork()) == 0) { settimerhandle(); printstatus(gsk); while(1); } else { if (!debug_level) { printf(" "); } for(i = 0; i < repeat_count; i++) { if (role == SERVER) { DEBUG_PRINT(DEBUG_NONE, "Server: Receiving packets.(%d/%d)\n", i+1, repeat_count); server(gsk); } else { DEBUG_PRINT(DEBUG_NONE, "Client: Sending packets.(%d/%d)\n", i+1, repeat_count); client(sk); } fflush(stdout); } if (role == SERVER) close_r(gsk); close_r(sk); } } /* start_test() */ void settimerhandle(void) { struct sigaction act; struct itimerval interval; act.sa_handler = sighandler; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGPROF, &act, NULL); interval.it_value.tv_sec = DEFAULT_SEC; interval.it_value.tv_usec = DEFAULT_USEC; interval.it_interval = interval.it_value; setitimer(ITIMER_PROF, &interval, NULL); } void usage(char *argv0) { fprintf(stderr, "\nusage:\n"); fprintf(stderr, " server:\n"); fprintf(stderr, " %8s -H local-addr -P local-port -l [-d level] [-x]\n" "\t [-L num-ports] [-S num-ports]\n" "\t [-a assoc-pattern]\n" "\t [-i interface]\n" "\t [-f status-file]\n", argv0); fprintf(stderr, "\n"); fprintf(stderr, " client:\n"); fprintf(stderr, " %8s -H local-addr -P local-port -h remote-addr\n" "\t -p remote-port -s [-c case ] [-d level]\n" "\t [-x repeat] [-o order-pattern] ream-pattern]\n" "\t [-M max-stream]\n" "\t [-m max-msgsize]\n" "\t [-L num-ports] [-S num-ports]\n" "\t [-i interface]\n" "\t [-f status-file]\n", argv0); fprintf(stderr, "\n"); fprintf(stderr, "\t-c value = Packets of specifed size.\n"); fprintf(stderr, "\t-m msgsize(1500-65515, default value 32768)\n"); fprintf(stderr, "\t-x number of repeats\n"); fprintf(stderr, "\t-X number of messages\n"); fprintf(stderr, "\t-o order-pattern\n"); fprintf(stderr, "\t 0 = all unordered(default) \n"); fprintf(stderr, "\t 1 = all ordered \n"); fprintf(stderr, "\t 2 = alternating \n"); fprintf(stderr, "\t 3 = random\n"); fprintf(stderr, "\t-M max-stream (default value 0)\n"); fprintf(stderr, "\t-D drain. If in client mode do a read following send.\n"); fprintf(stderr, "\t-I receive after times of send, default value 1.\n"); fprintf(stderr, "\n"); fflush(stderr); } /* usage() */ void sighandler(int signo) { DEBUG_PRINT(DEBUG_MAX, "timeout sig\n"); printstatus(gsk); } char* get_sstat_state(int state) { switch(state) { case SCTP_EMPTY: return "EMPTY"; case SCTP_CLOSED: return "CLOSED"; case SCTP_COOKIE_WAIT: return "COOKIE_WAIT"; case SCTP_COOKIE_ECHOED: return "COOKIE_ECHOED"; case SCTP_ESTABLISHED: return "ESTABLISHED"; case SCTP_SHUTDOWN_PENDING: return "SHUTDOWN_PENDING"; case SCTP_SHUTDOWN_SENT: return "SHUTDOWN_SENT"; case SCTP_SHUTDOWN_RECEIVED: return "SHUTDOWN_RECEIVED"; case SCTP_SHUTDOWN_ACK_SENT: return "SHUTDOWN_ACK_SENT"; default: return "UNKNOW"; } } void printstatus(int sk) { static int cwnd = 0; static int count = 0; struct sctp_status status; socklen_t optlen; FILE * fp; const char *state_to_str[] = { [SCTP_INACTIVE] = "INACTIVE", [SCTP_PF] = "PF", [SCTP_ACTIVE] = "ACTIVE", [SCTP_UNCONFIRMED] = "UNCONFIRMED", }; optlen = sizeof(struct sctp_status); if(getsockopt(sk, IPPROTO_SCTP, SCTP_STATUS, &status, &optlen) < 0) { fprintf(stderr, "Error getting status: %s.\n", strerror(errno)); exit(1); } if (statusfile != NULL) { if (count == 0) unlink(statusfile); if((fp = fopen(statusfile, "a+")) == NULL) { perror("fopen"); exit(1); } } else fp = stdout; if (count == 0) fprintf(fp, "NO. ASSOC-ID STATE RWND UNACKDATA PENDDATA INSTRMS OUTSTRMS " "FRAG-POINT SPINFO-STATE SPINFO-CWDN SPINFO-SRTT SPINFO-RTO SPINFO-MTU\n"); if (cwnd != status.sstat_primary.spinfo_cwnd) { count++; fprintf(fp, "%-3d %-8d %-17s %-8d %-9d %-8d %-7d %-8d %-10d %-12s %-11d %-11d %-10d %d\n", count, status.sstat_assoc_id, get_sstat_state(status.sstat_state), status.sstat_rwnd, status.sstat_unackdata, status.sstat_penddata, status.sstat_instrms, status.sstat_outstrms, status.sstat_fragmentation_point, state_to_str[status.sstat_primary.spinfo_state], status.sstat_primary.spinfo_cwnd, status.sstat_primary.spinfo_srtt, status.sstat_primary.spinfo_rto, status.sstat_primary.spinfo_mtu); } cwnd = status.sstat_primary.spinfo_cwnd; fflush(fp); if (fp != stdout) fclose(fp); if (status.sstat_primary.spinfo_state != SCTP_ACTIVE) { close_r(sk); exit(1); } }