Skip to content

Instantly share code, notes, and snippets.

@frsyuki
Created August 15, 2009 05:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frsyuki/168279 to your computer and use it in GitHub Desktop.
Save frsyuki/168279 to your computer and use it in GitHub Desktop.
/* ./sctpsrv | grep -v "sndrcv"
assoc change: assoc=2, state=comm-up, error=0, outstr=10 instr=10
peer addr change: assoc=2, addr=192.168.20.2 state=confirmed, error=2
peer addr change: assoc=2, addr=192.168.1.2 state=confirmed, error=2
peer addr change: assoc=2, addr=192.168.0.2 state=confirmed, error=2
shutdown event: assoc=2
assoc change: assoc=2, state=shutdown-comp, error=0, outstr=0 instr=0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/errno.h>
#include <netinet/sctp.h>
#include <sys/epoll.h>
static int pexit(const char* msg)
{
perror(msg);
exit(1);
}
int main(void)
{
int ret;
// init socket
int sk = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
if(sk < 0) { pexit("socket"); }
ret = fcntl(sk, F_SETFL, O_NONBLOCK);
if(ret < 0) { pexit("fcntl O_NONBLOCK"); }
// init epoll
int epfd = epoll_create(1);
if(epfd < 0) { pexit("epoll_create"); }
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sk;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sk, &ev);
if(ret < 0) { pexit("epoll_ctl ADD"); }
// bind
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(5678);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sk, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0) { pexit("bind"); }
// listen is required with SOCK_SEQPACKET
ret = listen(sk, 1024);
if(ret < 0) { pexit("listen"); }
// init event notifications
struct sctp_event_subscribe sevent;
memset(&sevent, 0, sizeof(sevent));
sevent.sctp_data_io_event = 1;
sevent.sctp_association_event = 1;
sevent.sctp_address_event = 1;
sevent.sctp_send_failure_event = 1;
sevent.sctp_peer_error_event = 1;
sevent.sctp_shutdown_event = 1;
sevent.sctp_partial_delivery_event = 1;
sevent.sctp_adaptation_layer_event = 1;
ret = setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &sevent, sizeof(sevent));
if(ret < 0) { pexit("setsockopt"); }
// init buffer
const size_t bufsz = 1024*1024*10;
void* buf = malloc(bufsz);
struct iovec iov;
iov.iov_base = buf;
iov.iov_len = bufsz;
char msgctl[CMSG_SPACE(sizeof(sctp_cmsg_data_t))];
struct msghdr msgsrc;
memset(&msgsrc, 0, sizeof(msgsrc));
msgsrc.msg_iov = &iov;
msgsrc.msg_iovlen = 1;
msgsrc.msg_control = msgctl;
msgsrc.msg_controllen = sizeof(msgctl);
while(1) {
struct msghdr msg = msgsrc;
// wait for events
ret = epoll_wait(epfd, &ev, 1, -1);
if(ret < 0) { pexit("epoll_wait"); }
ssize_t sz = recvmsg(sk, &msg, MSG_WAITALL);
if(sz <= 0) {
if(errno == EAGAIN) {
printf("EAGAIN\n");
continue;
}
pexit("recvmsg");
}
if(msg.msg_flags & MSG_NOTIFICATION) {
union sctp_notification* sn = buf;
switch (sn->sn_header.sn_type) {
case SCTP_ASSOC_CHANGE: {
struct sctp_assoc_change* sac = &sn->sn_assoc_change;
printf("assoc change: assoc=%d, state=%s, error=%hu, outstr=%hu instr=%hu\n",
sac->sac_assoc_id,
sac->sac_state == SCTP_COMM_UP ? "comm-up" :
sac->sac_state == SCTP_COMM_LOST ? "comm-lost" :
sac->sac_state == SCTP_RESTART ? "restart" :
sac->sac_state == SCTP_SHUTDOWN_COMP ? "shutdown-comp" :
sac->sac_state == SCTP_CANT_STR_ASSOC ? "can't-str-assoc" :
"unknown",
sac->sac_error, sac->sac_outbound_streams, sac->sac_inbound_streams);
} break;
case SCTP_PEER_ADDR_CHANGE: {
struct sctp_paddr_change* spc = &sn->sn_paddr_change;
char addrbuf[INET6_ADDRSTRLEN];
if (spc->spc_aaddr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in *)&spc->spc_aaddr;
inet_ntop(AF_INET, &sin->sin_addr, addrbuf, sizeof(addrbuf));
} else {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&spc->spc_aaddr;
inet_ntop(AF_INET6, &sin6->sin6_addr, addrbuf, sizeof(addrbuf));
}
printf("peer addr change: assoc=%d, addr=%s state=%s, error=%d\n",
spc->spc_assoc_id, addrbuf,
spc->spc_state == SCTP_ADDR_AVAILABLE ? "available" :
spc->spc_state == SCTP_ADDR_UNREACHABLE ? "unreachable" :
spc->spc_state == SCTP_ADDR_REMOVED ? "removed" :
spc->spc_state == SCTP_ADDR_ADDED ? "added" :
spc->spc_state == SCTP_ADDR_MADE_PRIM ? "made-prim" :
spc->spc_state == SCTP_ADDR_CONFIRMED ? "confirmed" :
"unknown",
spc->spc_error);
} break;
case SCTP_SEND_FAILED: {
struct sctp_send_failed* ssf = &sn->sn_send_failed;
printf("send failed: assoc=%d, error=%u, context=%u\n",
ssf->ssf_assoc_id, ssf->ssf_error, ssf->ssf_info.sinfo_context);
} break;
case SCTP_REMOTE_ERROR: {
struct sctp_remote_error* sre = &sn->sn_remote_error;
printf("remote error: assoc=%d, error=%hu\n",
sre->sre_assoc_id, sre->sre_error);
} break;
case SCTP_SHUTDOWN_EVENT: {
struct sctp_shutdown_event* sse = &sn->sn_shutdown_event;
printf("shutdown event: assoc=%d\n",
sse->sse_assoc_id);
} break;
case SCTP_PARTIAL_DELIVERY_EVENT: {
struct sctp_pdapi_event* pdapi = &sn->sn_pdapi_event;
printf("partial delivery event: assoc=%d, inidication=%u\n",
pdapi->pdapi_assoc_id, pdapi->pdapi_indication);
} break;
case SCTP_ADAPTATION_INDICATION: {
struct sctp_adaptation_event* sai = &sn->sn_adaptation_event;
printf("adaptation indication: assoc=%d, adaptation=%u\n",
sai->sai_assoc_id, sai->sai_adaptation_ind);
} break;
case SCTP_AUTHENTICATION_INDICATION: {
struct sctp_authkey_event* auth = &sn->sn_authkey_event;
printf("authentication inidication: assoc=%d, key=%hu, altkey=%hu\n, inidication=%u\n",
auth->auth_assoc_id, auth->auth_keynumber, auth->auth_altkeynumber, auth->auth_indication);
} break;
default:
printf("unknown notification: %hu\n", sn->sn_header.sn_type);
break;
}
} else {
struct cmsghdr *cmsg;
for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
sctp_cmsg_data_t* cdata = (sctp_cmsg_data_t*)CMSG_DATA(cmsg);
if(cmsg->cmsg_level != IPPROTO_SCTP) { continue; }
switch(cmsg->cmsg_type) {
case SCTP_SNDRCV: {
struct sctp_sndrcvinfo* sndrcv = &cdata->sndrcv;
printf("sndrcv: assoc=%d, stream=%hu, ssn=%hu, flags=%hu, ppid=%u, context=%u, timetolive=%u, tsn=%u, cumtsn=%u\n",
sndrcv->sinfo_assoc_id, sndrcv->sinfo_stream, sndrcv->sinfo_ssn, sndrcv->sinfo_flags, sndrcv->sinfo_ppid, sndrcv->sinfo_context, sndrcv->sinfo_timetolive, sndrcv->sinfo_tsn, sndrcv->sinfo_cumtsn);
} break;
// This structure is not used for recvmsg()
//case SCTP_INIT: {
// struct sctp_initmsg* sinit = &cdata->init;
// printf("init msg: num_ostr=%hu, max_istr=%hu, max_attempts=%hu, max_init_timeo=%hu\n",
// sinit->sinit_num_ostreams, sinit->sinit_max_attempts, sinit->sinit_max_attempts, sinit->sinit_max_init_timeo);
//} break;
default:
printf("unknown cmsg: %hu\n", cmsg->cmsg_type);
break;
}
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment