Created
August 15, 2009 05:41
-
-
Save frsyuki/168279 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ./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