blob: db8c9777956acf6dbc880a1578d06cc592688676 [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/signal.h>
#include "pdapi_req.h"
FILE *sum_out=NULL;
int verbose = 0;
struct requests *base=NULL;
struct asoc_read_log {
sctp_assoc_t assoc_id;
int sz;
int flags;
};
#define READ_LOG_SIZE 50000
struct asoc_read_log rdlog[READ_LOG_SIZE];
static int rdlog_at=0;
static int rdlog_wrap=0;
static unsigned int total_msgs=0;
int chars_out=0;
struct requests *
find_assoc(sctp_assoc_t id)
{
struct requests *who;
who = base;
while(who) {
if(who->assoc_id == id){
break;
}
who = who->next;
}
return(who);
}
void
sum_it_out(uint8_t *data, int size)
{
int i;
for(i=0; i<size; i++) {
fprintf(sum_out, "%2.2x ", data[i]);
chars_out++;
if(chars_out == 16){
fprintf(sum_out, "\n");
chars_out = 0;
}
}
}
int
audit_a_msg (struct requests *who)
{
struct data_block *blk, *end_blk, *dat=NULL, *req=NULL;
struct pdapi_request *msg, *end;
int cnt_data=0, cnt_end=0, tot_size, calc_size=0;
uint32_t base_crc = 0xffffffff, passed_sum, final_sum;
ushort ssn_req, ssn_data, ssn_end;
int ret=1;
if(who->first == NULL) {
return (1);
}
msg = (struct pdapi_request *)who->first->data;
if(msg->request != PDAPI_REQUEST_MESSAGE) {
/* not a request at the head? */
ret = 0;
goto clean_it;
} else {
req = who->first;;
tot_size = ntohl(msg->msg.size);
}
ssn_req = who->first->info.sinfo_ssn;
ssn_data = ssn_req + 1;
ssn_end = ssn_data + 1;
/* now do we have all the ssn's */
blk = who->first->next;
while(blk) {
if(blk->info.sinfo_ssn == ssn_data) {
if(dat == NULL) {
dat = blk;
}
cnt_data++;
} else if (blk->info.sinfo_ssn == ssn_end) {
cnt_end++;
end = (struct pdapi_request *)blk->data;
passed_sum = end->msg.checksum;
end_blk = blk;
break;
}
blk = blk->next;
}
if(cnt_data && cnt_end) {
/* we have at least ONE complete message */
/* get rid of request */
blk = dat;
if(blk->info.sinfo_ssn != ssn_data) {
printf("Out of order\n");
ret = 0;
goto clean_it;
}
msg = (struct pdapi_request *)blk->data;
if(msg->request != PDAPI_DATA_MESSAGE) {
printf("Data garbled -- not request data type\n");
ret = 0;
goto clean_it;
}
/* csum the first msg */
calc_size = blk->sz - sizeof(struct pdapi_request) + sizeof(int);
if(sum_out) {
sum_it_out(msg->msg.data, calc_size);
}
base_crc = update_crc32(base_crc, msg->msg.data, calc_size);
blk = blk->next;
while(blk && (blk != end_blk)) {
base_crc = update_crc32(base_crc, blk->data, blk->sz);
if(sum_out) {
sum_it_out(blk->data, blk->sz);
}
calc_size += blk->sz;
blk = blk->next;
}
final_sum = sctp_csum_finalize(base_crc);
if(sum_out) {
fprintf(sum_out, "\n");
fflush(sum_out);
}
msg = (struct pdapi_request *)end_blk->data;
if (msg->request != PDAPI_END_MESSAGE) {
printf("Last msg not END?\n");
passed_sum = 0;
} else {
passed_sum = msg->msg.checksum;
}
if(calc_size != tot_size) {
printf("Message size was supposed to be %d but saw %d\n",
tot_size, calc_size);
}
if(passed_sum != final_sum) {
printf("Checksum mis-match should be %x but is %x\n",
(u_int)passed_sum, (u_int)final_sum);
}
total_msgs++;
if ((total_msgs % 10000) == 0) {
printf("Processed %d messages\n", total_msgs);
}
/* clean up time */
clean_it:
who->msg_cnt -= 3;
while ((who->first != end_blk) && (who->first != NULL)) {
blk = who->first;
who->first = blk->next;
free(blk);
}
if(who->first == end_blk) {
who->first = end_blk->next;
free(end_blk);
}
if(who->first == NULL)
who->tail = NULL;
}
return (ret);
}
void
pdapi_addasoc( struct sockaddr_in *from, struct sctp_assoc_change *asoc)
{
struct requests *who;
who = malloc(sizeof(struct requests));
if(who == NULL) {
perror("out of memory");
abort();
}
memset(who, 0, sizeof(struct requests));
printf("adding assocation %x\n", asoc->sac_assoc_id);
who->assoc_id = asoc->sac_assoc_id;
who->msg_cnt = 0;
who->who = *from;
who->prev = who->next = NULL;
who->first = NULL;
who->tail = NULL;
if(base == NULL) {
base = who;
} else {
who->next = base;
who->next->prev = who;
base = who;
}
}
int
pdapi_clean_all(struct requests *who)
{
struct data_block *blk,*nxt;
int cnt=0;
blk = who->first;
while(blk) {
nxt = blk->next;
blk->next = NULL;
free(blk);
blk = nxt;
cnt++;
}
return (cnt);
}
void
pdapi_delasoc(sctp_assoc_t id)
{
struct requests *who;
who = find_assoc(id);
if(who) {
if(who->next) {
who->next->prev = who->prev;
}
if(who->prev) {
who->prev->next = who->next;
} else {
base = who->next;
}
who->next = NULL;
who->prev = NULL;
audit_a_msg(who);
pdapi_clean_all(who);
who->first = who->tail = NULL;
free(who);
}
}
void
pdapi_process_data(unsigned char *buffer,
ssize_t len,
struct sctp_sndrcvinfo *sinfo,
struct sockaddr_in *from,
int flags)
{
/* find the assoc */
/* add the data to the list */
/* if MSG_EOR then call the audit function */
struct data_block *blk;
struct requests *who;
if(sinfo->sinfo_assoc_id == 0) {
printf("Zero'ed assoc id\n");
abort();
}
who = find_assoc(sinfo->sinfo_assoc_id);
if(who == NULL) {
printf("Huh, can't find asoc %x\n", (u_int)sinfo->sinfo_assoc_id);
abort();
}
blk = malloc(sizeof(struct data_block) + len);
if (blk == NULL) {
printf("Can't allocate a block of size %d + %d\n", (int)len, (int)sizeof(struct data_block));
abort();
}
memset(blk, 0, sizeof(struct data_block));
blk->next = NULL;
memcpy(&blk->info, sinfo, sizeof(struct sctp_sndrcvinfo));
blk->sz = len;
memcpy(blk->data, buffer, len);
if(who->first != NULL) {
if(who->tail) {
who->tail->next = blk;
who->tail = blk;
} else {
/* huh? */
abort();
}
} else {
who->first = blk;
who->tail = blk;
}
if(flags & MSG_EOR) {
who->msg_cnt++;
blk->last = 1;
if ((who->msg_cnt % 3) == 0) {
(void)audit_a_msg (who);
}
}
}
static void
pdapi_abortrecption(struct sctp_pdapi_event *pdapi)
{
/* What do we do here? */
struct requests *who;
who = find_assoc(pdapi->pdapi_assoc_id);
if(who == NULL)
return;
pdapi_clean_all(who);
who->first = who->tail = NULL;
who->msg_cnt = 0;
}
void
pdapi_notification(unsigned char *buffer,
ssize_t len,
struct sctp_sndrcvinfo *sinfo,
struct sockaddr_in *from)
{
struct sctp_tlv *sn_header;
sn_header = (struct sctp_tlv *)buffer;
struct sctp_shutdown_event *shut;
struct sctp_assoc_change *asoc;
struct sctp_pdapi_event *pdapi;
switch (sn_header->sn_type) {
case SCTP_ASSOC_CHANGE:
asoc = (struct sctp_assoc_change *)sn_header;
if (asoc->sac_state == SCTP_COMM_UP) {
pdapi_addasoc(from, asoc);
} else if (asoc->sac_state == SCTP_COMM_LOST) {
printf("Comm lost id:%x del assoc\n",
asoc->sac_assoc_id);
pdapi_delasoc(asoc->sac_assoc_id);
}
break;
case SCTP_PARTIAL_DELIVERY_EVENT:
pdapi = (struct sctp_pdapi_event *)sn_header;
pdapi_abortrecption(pdapi);
break;
case SCTP_SHUTDOWN_EVENT:
shut = (struct sctp_shutdown_event *)sn_header;
printf("Shutdown assoc id:%x del assoc\n",
shut->sse_assoc_id);
pdapi_delasoc(shut->sse_assoc_id);
break;
case SCTP_REMOTE_ERROR:
case SCTP_SEND_FAILED:
case SCTP_ADAPTATION_INDICATION:
case SCTP_STREAM_RESET_EVENT:
case SCTP_PEER_ADDR_CHANGE:
default:
printf("Un-handled event type %d?\n",
sn_header->sn_type);
break;
}
}
void
pdapi_process_msg(unsigned char *buffer,
ssize_t len,
struct sctp_sndrcvinfo *sinfo,
struct sockaddr_in *from,
int flags)
{
if(verbose){
printf("Process a message of %d bytes, assoc:%x flags:%x\n",
(int)len, (u_int)sinfo->sinfo_assoc_id, (u_int)flags);
}
if(flags & MSG_NOTIFICATION) {
pdapi_notification(buffer, len, sinfo, from);
} else {
pdapi_process_data(buffer, len, sinfo, from, flags);
}
}
uint8_t buffer[PDAPI_DATA_BLOCK_SIZE];
int
main(int argc, char **argv)
{
int i, fd, flags=0;
u_int16_t port=0;
int level=SCTP_FRAG_LEVEL_1;
int val;
size_t len;
socklen_t slen;
socklen_t fromlen;
struct sctp_sndrcvinfo sndrcv;
struct sockaddr_in bindto,got,from;
struct sctp_event_subscribe event;
memset(&bindto,0,sizeof(bindto));
bindto.sin_family = AF_INET;
while((i= getopt(argc,argv,"p:vl:S:B:")) != EOF){
switch(i){
case 'S':
sum_out = fopen(optarg, "w+");
printf("Putting sum log out to %s\n", optarg);
break;
case 'l':
val = strtol(optarg, NULL, 0);
if ((val < SCTP_FRAG_LEVEL_0) ||
(val > SCTP_FRAG_LEVEL_2)) {
printf("Sorry level must be %d >= %d <= %d\n",
SCTP_FRAG_LEVEL_0,
val,
SCTP_FRAG_LEVEL_2);
return (-1);
}
break;
case 'B':
if (inet_pton(AF_INET, optarg, &bindto.sin_addr)) {
bindto.sin_family = AF_INET;
bindto.sin_len = sizeof(bindto);
} else {
printf("Cannot decode bindto address - using INADDR_ANY\n");
memset(&bindto,0,sizeof(bindto));
bindto.sin_family = AF_INET;
}
break;
case 'v':
verbose = 1;
break;
case 'p':
port = (u_int16_t)strtol(optarg,NULL,0);
break;
default:
break;
};
}
signal(SIGPIPE,SIG_IGN);
fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
if(fd == -1){
printf("can't open socket:%d\n",errno);
return(-1);
}
if (setsockopt(fd, IPPROTO_SCTP,
SCTP_FRAGMENT_INTERLEAVE,
&level, sizeof(level)) != 0) {
printf("Can't set FRAGMENT_INTERLEAVE socket option! err:%d\n", errno);
return (-1);
}
slen = sizeof(bindto);
bindto.sin_len = sizeof(bindto);
bindto.sin_port = htons(port);
if(bind(fd,(struct sockaddr *)&bindto, slen) < 0){
printf("can't bind a socket:%d\n",errno);
close(fd);
return(-1);
}
slen = sizeof(got);
if(getsockname(fd, (struct sockaddr *)&got, &slen) < 0){
printf("get sockname failed err:%d\n",errno);
close(fd);
return(-1);
}
if(port){
if(bindto.sin_port && (got.sin_port != bindto.sin_port)){
printf("Warning:could not get your port got %d instead\n",
ntohs(got.sin_port));
}
}
printf("Server listens on port %d\n",
ntohs(got.sin_port));
errno = 0;
/* enable all event notifications */
event.sctp_data_io_event = 1;
event.sctp_association_event = 1;
event.sctp_address_event = 0;
event.sctp_send_failure_event = 0;
event.sctp_peer_error_event = 0;
event.sctp_shutdown_event = 1;
event.sctp_partial_delivery_event = 1;
event.sctp_adaptation_layer_event = 0;
event.sctp_stream_reset_event = 0;
if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) {
printf("Can't do SET_EVENTS socket option! err:%d\n", errno);
return (-1);
}
if(listen(fd,1) == -1){
printf("Can't listen err:%d\n",errno);
return (-1);
}
while(1) {
fromlen = sizeof(from);
memset(&sndrcv, 0, sizeof(sndrcv));
flags = 0;
len = sctp_recvmsg(fd, buffer, (size_t)PDAPI_DATA_BLOCK_SIZE,
(struct sockaddr *)&from, &fromlen,
&sndrcv, &flags);
rdlog[rdlog_at].sz = len;
rdlog[rdlog_at].flags = flags;
rdlog[rdlog_at].assoc_id = sndrcv.sinfo_assoc_id;
rdlog_at++;
if(rdlog_at >= READ_LOG_SIZE) {
rdlog_at = 0;
rdlog_wrap++;
}
if(len > 0) {
pdapi_process_msg(buffer, len, &sndrcv, &from, flags);
}
}
}