/*****************************************************************************/
/******sctp test tool standard v1.0 scutzxb@sina.com 2016.11.18***************/
/*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <errno.h>
#include <stddef.h>
#include <sys/epoll.h>
#include <sched.h>
#include <arpa/inet.h>
#define TOOL_VERSION "1.0"
#define MAX_BUFF_SIZE 65525
#define DEF_BUFF_SIZE 150
#define EPOLLEVENTS 65536
#define MAX_CMD_LEN 100
#define MAX_CMD_DISC_LEN 100
#define SCTP_GET_PARA 1
#define SCTP_SET_PARA 2
#define PERFORMANCE_TEST_START "performance test start"
#define PERFORMANCE_TEST_END "performance test end"
#define DATA_PERFORMANCE_TEST_START "data sending performance test start"
#define DATA_PERFORMANCE_TEST_END "data sending performance test end"
#define O_NONBLOCK 00004000
#define F_GETFL 3 /* get file->f_flags */
#define F_SETFL 4 /* set file->f_flags */
#define DEBUG_PRINT(fd, fmt,args...) {if(!g_performance_test && !g_data_performance_test && !get_fd_pt(fd)) printf(fmt, ##args);}
unsigned char g_local_ip_1[50] = {0};
unsigned char g_local_ip_2[50] = {0};
unsigned short g_local_port = 0;
unsigned char g_remote_ip_1[50] = {0};
unsigned char g_remote_ip_2[50] = {0};
unsigned short g_remote_port = 0;
unsigned char g_test_v4 = 0;
unsigned char g_test_v6 = 0;
unsigned char g_multi_homing = 0;
unsigned char g_client_server = 0;
unsigned char g_performance_test =0;
unsigned char g_data_performance_test =0;
unsigned char g_connect_mod = 0; /* 0: connect; 1: setsockopt */
unsigned char g_pmtu = 1; /* 0: disable pmtu; 1: enable pmtu */
int g_max_conn_num = 65535;
int g_message_len = 150;
int g_data_pt_time = 10; /* 10 seconds */
unsigned long g_message_num = 65536;
unsigned long g_message_received = 0;
unsigned long g_message_total_size = 0;
int g_failure_cnt = 0;
int g_success_cnt = 0;
int g_hbinterval = 1000;
int g_sackdelay = 110; /* 0: disable sackdelay */
int g_rto_init = 200;
int g_rto_min = 150;
int g_rto_max = 200;
int g_first_fd = 0;
time_t g_first;
time_t g_last;
int g_fd_count = 0;
int g_epfd;
static bool inline add_to_epoll(int fd)
{
struct epoll_event ev;
int ret;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = fd;
ret = epoll_ctl(g_epfd, EPOLL_CTL_ADD, fd, &ev);
if (ret < 0) {
printf("[EPOLL_CTL_ADD error] %d:%s\n", errno, strerror(errno));
return false;
}
return true;
}
static bool inline del_from_epoll(int fd)
{
struct epoll_event ev;
int ret;
memset(&ev, 0, sizeof(ev));
ev.data.fd = fd;
ret = epoll_ctl(g_epfd, EPOLL_CTL_DEL, fd, &ev);
if (ret < 0) {
printf("[EPOLL_CTL_DEL error] fd = %d, %d:%s\n", fd, errno, strerror(errno));
return false;
}
return true;
}
#define MAX_FD 65536
#define MASK_FD 65535
#define MAX_CONFLICT 3
#define UNUSED_SK_TYPE 0
#define LISTEN_SK_TYPE 1
#define CONN_SK_TYPE 2
#define REMOVE_SK_TYPE 3
struct fd_list {
int fd;
unsigned char type;
unsigned char state;
unsigned char performance_test;
} g_fd_list[MAX_FD][MAX_CONFLICT];
pthread_mutex_t fd_list_lock;
enum sk_state {
SK_STATE_CLOSED = 0,
SK_STATE_LISTENING,
SK_STATE_ACCEPTED,
SK_STATE_CONNECTING,
SK_STATE_COMM_UP,
SK_STATE_MAX
};
struct sk_state_description {
char state;
char description[MAX_CMD_LEN];
} g_sk_state_list[SK_STATE_MAX] = {
{SK_STATE_CLOSED, "closed"},
{SK_STATE_LISTENING, "listening"},
{SK_STATE_ACCEPTED, "accepted"},
{SK_STATE_CONNECTING, "connecting"},
{SK_STATE_COMM_UP, "established"}
};
static void inline add_to_fd_list_unlock(int fd, unsigned char type, unsigned char state)
{
int i, key;
key = MASK_FD&fd;
for (i = 0; i < MAX_CONFLICT && g_fd_list[key][i].fd != 0 && g_fd_list[key][i].fd != fd; i++) {
}
if (i < MAX_CONFLICT && g_fd_list[key][i].type == REMOVE_SK_TYPE) {
g_fd_list[key][i].type = UNUSED_SK_TYPE;
return;
}
if (i < MAX_CONFLICT && g_fd_list[key][i].fd == 0) {
g_fd_list[key][i].fd = fd;
g_fd_list[key][i].type = type;
g_fd_list[key][i].state = state;
g_fd_list[key][i].performance_test = g_performance_test;
g_fd_count++;
} else if (g_fd_list[key][i].fd == fd) {
/* update_fd_list() may be called before add_to_fd_list() */
g_fd_list[key][i].type = type;
} else {
printf("[add_to_fd_list] too many conflict fds, key = %d\n", key);
}
}
static void inline add_to_fd_list(int fd, unsigned char type, unsigned char state)
{
pthread_mutex_lock(&fd_list_lock);
add_to_fd_list_unlock(fd, type, state);
pthread_mutex_unlock(&fd_list_lock);
}
static void inline update_fd_state(int fd, unsigned char state)
{
int i, key;
key = MASK_FD&fd;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_CONFLICT && g_fd_list[key][i].fd != fd; i++) {
}
if (i < MAX_CONFLICT && g_fd_list[key][i].fd == fd) {
g_fd_list[key][i].state = state;
} else {
/* update_fd_list() may be called before add_to_fd_list() */
add_to_fd_list_unlock(fd, 0, state);
}
pthread_mutex_unlock(&fd_list_lock);
}
static void inline remove_from_fd_list(int fd)
{
int i, key;
key = MASK_FD&fd;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_CONFLICT && g_fd_list[key][i].fd != fd; i++) {
}
if (i < MAX_CONFLICT && g_fd_list[key][i].fd == fd) {
g_fd_list[key][i].fd = 0;
g_fd_list[key][i].type = UNUSED_SK_TYPE;
g_fd_list[key][i].state = 0;
g_fd_count--;
} else {
for (i = 0; i < MAX_CONFLICT && g_fd_list[key][i].fd != 0; i++) {
}
if (i < MAX_CONFLICT && g_fd_list[key][i].fd == 0) {
g_fd_list[key][i].type = REMOVE_SK_TYPE;
}
}
pthread_mutex_unlock(&fd_list_lock);
}
unsigned char inline get_fd_type(int fd)
{
int i, key;
key = MASK_FD&fd;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_CONFLICT && g_fd_list[key][i].fd != fd; i++) {
}
if (i < MAX_CONFLICT && g_fd_list[key][i].fd == fd) {
pthread_mutex_unlock(&fd_list_lock);
return g_fd_list[key][i].type;
}
pthread_mutex_unlock(&fd_list_lock);
return UNUSED_SK_TYPE;
}
unsigned char inline get_fd_pt(int fd)
{
int i, key;
key = MASK_FD&fd;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_CONFLICT && g_fd_list[key][i].fd != fd; i++) {
}
if (i < MAX_CONFLICT && g_fd_list[key][i].fd == fd) {
pthread_mutex_unlock(&fd_list_lock);
return g_fd_list[key][i].performance_test;
}
pthread_mutex_unlock(&fd_list_lock);
return 0;
}
static void inline clean_fd(int fd)
{
if (get_fd_type(fd) == CONN_SK_TYPE)
del_from_epoll(fd);
remove_from_fd_list(fd);
close(fd);
}
static void inline clean_all_cnn_fds()
{
int i, j;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_FD; i++) {
for (j = 0; j < MAX_CONFLICT; j++) {
if (g_fd_list[i][j].type != UNUSED_SK_TYPE) {
if (g_fd_list[i][j].type == CONN_SK_TYPE) {
del_from_epoll(g_fd_list[i][j].fd);
close(g_fd_list[i][j].fd);
memset(&g_fd_list[i][j], 0, sizeof(struct fd_list));
g_fd_count--;
}
}
}
}
pthread_mutex_unlock(&fd_list_lock);
}
static void inline update_all_cnn_fds()
{
int i, j;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_FD; i++) {
for (j = 0; j < MAX_CONFLICT; j++) {
if (g_fd_list[i][j].type != UNUSED_SK_TYPE) {
if (g_fd_list[i][j].type == CONN_SK_TYPE) {
g_fd_list[i][j].performance_test = 1;
}
}
}
}
pthread_mutex_unlock(&fd_list_lock);
}
static void inline print_fd_list()
{
int i, j;
pthread_mutex_lock(&fd_list_lock);
for (i = 0; i < MAX_FD; i++) {
for (j = 0; j < MAX_CONFLICT; j++) {
if (g_fd_list[i][j].fd != 0) {
printf("[fd list] fd = %d, style = %s, state = %s\n",
g_fd_list[i][j].fd,
(g_fd_list[i][j].type == LISTEN_SK_TYPE)?"listen":"connect",
g_sk_state_list[g_fd_list[i][j].state].description);
}
}
}
pthread_mutex_unlock(&fd_list_lock);
printf("[fd list] total = %d\n", g_fd_count);
}
bool ch_to_i(unsigned char *d, char *s)
{
bool ret = true;
if (s[0] >= 'A' && s[0] <= 'Z')
*d = (s[0] - 'A' + 10) << 4;
else if (s[0] >= 'a' && s[0] <= 'z')
*d = (s[0] - 'a' + 10) << 4;
else if (s[0] >= '0' && s[0] &l