Last active
June 24, 2024 09:52
-
-
Save paigeadelethompson/e916f95607fbf193ecafb08295ecab68 to your computer and use it in GitHub Desktop.
Really simple usrsctp SCTP library example (with UDP encapsulation and without, instructions in the comments)
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
// cc -g3 -I /usr/local/include -l usrsctp -L /usr/local/lib -l pthread server.c -o server | |
// [email protected] | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <sys/time.h> | |
#include <netinet/in.h> | |
#include <arpa/inet.h> | |
#include <errno.h> | |
#include <pthread.h> | |
#include <unistd.h> | |
#include <usrsctp.h> | |
#define LOCAL_ADDRESS "127.0.0.1" | |
#define LOCAL_UDP_ENCAPS_PORT 19819 | |
#define REMOTE_ADDRESS "127.0.0.1" | |
#define REMOTE_UDP_ENCAPS_PORT 19818 | |
#define REMOTE_SCTP_PORT 19818 | |
#define LOCAL_SCTP_PORT 19819 | |
#define DEFAULT_TARGET stdout; | |
#define DEBUG 1 | |
int done = 0; | |
static FILE *debug_target = NULL; | |
static int | |
receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, | |
size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info); | |
void | |
debug_printf_stack(const char *format, ...); | |
void | |
handle_notification(union sctp_notification *notif, size_t n); | |
static void | |
handle_remote_error_event(struct sctp_remote_error *sre); | |
static void | |
handle_stream_change_event(struct sctp_stream_change_event *strchg); | |
static void | |
handle_stream_change_event(struct sctp_stream_change_event *strchg); | |
static void | |
handle_stream_reset_event(struct sctp_stream_reset_event *strrst); | |
static void | |
handle_shutdown_event(struct sctp_shutdown_event *sse); | |
static void | |
handle_adaptation_indication(struct sctp_adaptation_event *sai); | |
void | |
debug_set_target(FILE *fp); | |
void | |
debug_printf(const char *format, ...); | |
int | |
main(int argc, char **argv) { | |
struct socket *sock; | |
struct sockaddr *addr, *addrs; | |
struct sockaddr_in addr4; | |
struct sockaddr_in6 addr6; | |
struct sctp_udpencaps encaps; | |
struct sctpstat stat; | |
struct sctp_event event; | |
uint16_t event_types[] = {SCTP_ASSOC_CHANGE, | |
SCTP_PEER_ADDR_CHANGE, | |
SCTP_SEND_FAILED_EVENT}; | |
char buffer[80]; | |
unsigned int i; | |
int n; | |
usrsctp_init(LOCAL_UDP_ENCAPS_PORT, NULL, debug_printf_stack); | |
if(DEBUG != 0) | |
usrsctp_sysctl_set_sctp_debug_on(0xFFFFFFFF); | |
else | |
usrsctp_sysctl_set_sctp_debug_on(0); | |
usrsctp_sysctl_set_sctp_blackhole(2); | |
usrsctp_sysctl_set_sctp_no_csum_on_loopback(0); | |
if ((sock = usrsctp_socket(AF_INET6, SOCK_STREAM, IPPROTO_SCTP, receive_cb, NULL, 0, NULL)) == NULL) { | |
perror("usrsctp_socket"); | |
} | |
memset(&event, 0, sizeof(event)); | |
event.se_assoc_id = SCTP_ALL_ASSOC; | |
event.se_on = 1; | |
for (i = 0; i < sizeof(event_types)/sizeof(uint16_t); i++) { | |
event.se_type = event_types[i]; | |
if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) < 0) { | |
perror("setsockopt SCTP_EVENT"); | |
} | |
} | |
memset((void *)&addr6, 0, sizeof(struct sockaddr_in6)); | |
addr6.sin6_len = sizeof(struct sockaddr_in6); | |
addr6.sin6_family = AF_INET6; | |
addr6.sin6_port = htons(LOCAL_SCTP_PORT); | |
addr6.sin6_addr = in6addr_any; | |
if (usrsctp_bind(sock, (struct sockaddr *)&addr6, sizeof(struct sockaddr_in6)) < 0) { | |
perror("bind"); | |
} | |
if(LOCAL_UDP_ENCAPS_PORT > 0) { | |
memset(&encaps, 0, sizeof(struct sctp_udpencaps)); | |
encaps.sue_address.ss_family = AF_INET6; | |
encaps.sue_port = htons(REMOTE_UDP_ENCAPS_PORT); | |
if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_REMOTE_UDP_ENCAPS_PORT, (const void*)&encaps, (socklen_t)sizeof(struct sctp_udpencaps)) < 0) { | |
perror("setsockopt"); | |
} | |
} | |
memset((void *)&addr4, 0, sizeof(struct sockaddr_in)); | |
memset((void *)&addr6, 0, sizeof(struct sockaddr_in6)); | |
addr4.sin_len = sizeof(struct sockaddr_in); | |
addr6.sin6_len = sizeof(struct sockaddr_in6); | |
addr4.sin_family = AF_INET; | |
addr6.sin6_family = AF_INET6; | |
addr4.sin_port = htons(REMOTE_SCTP_PORT); | |
addr6.sin6_port = htons(REMOTE_SCTP_PORT); | |
if (inet_pton(AF_INET6, REMOTE_ADDRESS, &addr6.sin6_addr) == 1) { | |
if (usrsctp_connect(sock, (struct sockaddr *)&addr6, sizeof(struct sockaddr_in6)) < 0) { | |
perror("usrsctp_connect"); | |
} | |
} else if (inet_pton(AF_INET, REMOTE_ADDRESS, &addr4.sin_addr) == 1) { | |
if (usrsctp_connect(sock, (struct sockaddr *)&addr4, sizeof(struct sockaddr_in)) < 0) { | |
perror("usrsctp_connect"); | |
} | |
} else { | |
printf("Illegal destination address.\n"); | |
} | |
if ((n = usrsctp_getladdrs(sock, 0, &addrs)) < 0) { | |
perror("usrsctp_getladdrs"); | |
} else { | |
addr = addrs; | |
printf("Local addresses: "); | |
for (i = 0; i < (unsigned int)n; i++) { | |
if (i > 0) { | |
printf("%s", ", "); | |
} | |
switch (addr->sa_family) { | |
case AF_INET: { | |
struct sockaddr_in *sin; | |
char buf[INET_ADDRSTRLEN]; | |
const char *name; | |
sin = (struct sockaddr_in *)addr; | |
name = inet_ntop(AF_INET, &sin->sin_addr, buf, INET_ADDRSTRLEN); | |
printf("%s", name); | |
addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in)); | |
break; | |
} | |
case AF_INET6: { | |
struct sockaddr_in6 *sin6; | |
char buf[INET6_ADDRSTRLEN]; | |
const char *name; | |
sin6 = (struct sockaddr_in6 *)addr; | |
name = inet_ntop(AF_INET6, &sin6->sin6_addr, buf, INET6_ADDRSTRLEN); | |
printf("%s", name); | |
addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in6)); | |
break; | |
} | |
default: | |
break; | |
} | |
addr = (struct sockaddr *)((caddr_t)addr + addr->sa_len); | |
} | |
printf(".\n"); | |
usrsctp_freeladdrs(addrs); | |
} | |
if ((n = usrsctp_getpaddrs(sock, 0, &addrs)) < 0) { | |
perror("usrsctp_getpaddrs"); | |
} else { | |
addr = addrs; | |
printf("Peer addresses: "); | |
for (i = 0; i < (unsigned int)n; i++) { | |
if (i > 0) { | |
printf("%s", ", "); | |
} | |
switch (addr->sa_family) { | |
case AF_INET: { | |
struct sockaddr_in *sin; | |
char buf[INET_ADDRSTRLEN]; | |
const char *name; | |
sin = (struct sockaddr_in *)addr; | |
name = inet_ntop(AF_INET, &sin->sin_addr, buf, INET_ADDRSTRLEN); | |
printf("%s", name); | |
addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in)); | |
break; | |
} | |
case AF_INET6: { | |
struct sockaddr_in6 *sin6; | |
char buf[INET6_ADDRSTRLEN]; | |
const char *name; | |
sin6 = (struct sockaddr_in6 *)addr; | |
name = inet_ntop(AF_INET6, &sin6->sin6_addr, buf, INET6_ADDRSTRLEN); | |
printf("%s", name); | |
addr = (struct sockaddr *)((caddr_t)addr + sizeof(struct sockaddr_in6)); | |
break; | |
} | |
default: | |
break; | |
} | |
addr = (struct sockaddr *)((caddr_t)addr + addr->sa_len); | |
} | |
printf(".\n"); | |
usrsctp_freepaddrs(addrs); | |
} | |
while ((fgets(buffer, sizeof(buffer), stdin) != NULL) && !done) { | |
usrsctp_sendv(sock, buffer, strlen(buffer), NULL, 0, NULL, 0, SCTP_SENDV_NOINFO, 0); | |
} | |
if (!done) { | |
if (usrsctp_shutdown(sock, SHUT_WR) < 0) { | |
perror("usrsctp_shutdown"); | |
} | |
} | |
while (!done) { | |
sleep(1); | |
} | |
usrsctp_get_stat(&stat); | |
printf("Number of packets (sent/received): (%u/%u).\n", | |
stat.sctps_outpackets, stat.sctps_inpackets); | |
while (usrsctp_finish() != 0) { | |
sleep(1); | |
} | |
return(0); | |
} | |
static int | |
receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, | |
size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info) { | |
if (data == NULL) { | |
done = 1; | |
usrsctp_close(sock); | |
} else { | |
if (flags & MSG_NOTIFICATION) { | |
handle_notification((union sctp_notification *)data, datalen); | |
} else { | |
if (write(fileno(stdout), data, datalen) < 0) { | |
perror("write"); | |
} | |
} | |
free(data); | |
} | |
return (1); | |
} | |
void | |
debug_set_target(FILE *fp) { | |
debug_target = fp; | |
} | |
void | |
debug_printf_clean(const char *format, ...) { | |
char charbuf[1024]; | |
va_list ap; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
va_start(ap, format); | |
if (vsnprintf(charbuf, 1024, format, ap) < 0) { | |
charbuf[0] = '\0'; | |
} | |
va_end(ap); | |
fprintf(debug_target, "%s", charbuf); | |
fflush(debug_target); | |
} | |
void | |
debug_printf(const char *format, ...) { | |
va_list ap; | |
char charbuf[1024]; | |
static struct timeval time_main; | |
struct timeval time_now; | |
struct timeval time_delta; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
if (time_main.tv_sec == 0 && time_main.tv_usec == 0) { | |
gettimeofday(&time_main, NULL); | |
} | |
gettimeofday(&time_now, NULL); | |
timersub(&time_now, &time_main, &time_delta); | |
va_start(ap, format); | |
if (vsnprintf(charbuf, 1024, format, ap) < 0) { | |
charbuf[0] = '\0'; | |
} | |
va_end(ap); | |
fprintf(debug_target, "[P][%u.%03u] %s", (unsigned int) time_delta.tv_sec, (unsigned int) time_delta.tv_usec / 1000, charbuf); | |
fflush(debug_target); | |
} | |
void | |
debug_printf_stack(const char *format, ...) { | |
va_list ap; | |
char charbuf[1024]; | |
static struct timeval time_main; | |
struct timeval time_now; | |
struct timeval time_delta; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
if (time_main.tv_sec == 0 && time_main.tv_usec == 0) { | |
gettimeofday(&time_main, NULL); | |
} | |
gettimeofday(&time_now, NULL); | |
timersub(&time_now, &time_main, &time_delta); | |
va_start(ap, format); | |
if (vsnprintf(charbuf, 1024, format, ap) < 0) { | |
charbuf[0] = '\0'; | |
} | |
va_end(ap); | |
fprintf(debug_target, "[S][%u.%03u] %s", (unsigned int) time_delta.tv_sec, (unsigned int) time_delta.tv_usec / 1000, charbuf); | |
fflush(debug_target); | |
} | |
static void | |
handle_association_change_event(struct sctp_assoc_change *sac) { | |
unsigned int i, n; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
fprintf(debug_target, "Association change "); | |
switch (sac->sac_state) { | |
case SCTP_COMM_UP: | |
fprintf(debug_target, "SCTP_COMM_UP"); | |
break; | |
case SCTP_COMM_LOST: | |
fprintf(debug_target, "SCTP_COMM_LOST"); | |
break; | |
case SCTP_RESTART: | |
fprintf(debug_target, "SCTP_RESTART"); | |
break; | |
case SCTP_SHUTDOWN_COMP: | |
fprintf(debug_target, "SCTP_SHUTDOWN_COMP"); | |
break; | |
case SCTP_CANT_STR_ASSOC: | |
fprintf(debug_target, "SCTP_CANT_STR_ASSOC"); | |
break; | |
default: | |
fprintf(debug_target, "UNKNOWN"); | |
break; | |
} | |
fprintf(debug_target, ", streams (in/out) = (%u/%u)", | |
sac->sac_inbound_streams, sac->sac_outbound_streams); | |
n = sac->sac_length - sizeof(struct sctp_assoc_change); | |
if (((sac->sac_state == SCTP_COMM_UP) || | |
(sac->sac_state == SCTP_RESTART)) && (n > 0)) { | |
fprintf(debug_target, ", supports"); | |
for (i = 0; i < n; i++) { | |
switch (sac->sac_info[i]) { | |
case SCTP_ASSOC_SUPPORTS_PR: | |
fprintf(debug_target, " PR"); | |
break; | |
case SCTP_ASSOC_SUPPORTS_AUTH: | |
fprintf(debug_target, " AUTH"); | |
break; | |
case SCTP_ASSOC_SUPPORTS_ASCONF: | |
fprintf(debug_target, " ASCONF"); | |
break; | |
case SCTP_ASSOC_SUPPORTS_MULTIBUF: | |
fprintf(debug_target, " MULTIBUF"); | |
break; | |
case SCTP_ASSOC_SUPPORTS_RE_CONFIG: | |
fprintf(debug_target, " RE-CONFIG"); | |
break; | |
case SCTP_ASSOC_SUPPORTS_INTERLEAVING: | |
fprintf(debug_target, " INTERLEAVING"); | |
break; | |
default: | |
fprintf(debug_target, " UNKNOWN(0x%02x)", sac->sac_info[i]); | |
break; | |
} | |
} | |
} else if (((sac->sac_state == SCTP_COMM_LOST) || | |
(sac->sac_state == SCTP_CANT_STR_ASSOC)) && (n > 0)) { | |
fprintf(debug_target, ", ABORT ="); | |
for (i = 0; i < n; i++) { | |
fprintf(debug_target, " 0x%02x", sac->sac_info[i]); | |
} | |
} | |
fprintf(debug_target, ".\n"); | |
return; | |
} | |
static void | |
handle_peer_address_change_event(struct sctp_paddr_change *spc) { | |
char addr_buf[INET6_ADDRSTRLEN]; | |
const char *addr; | |
struct sockaddr_in *sin; | |
struct sockaddr_in6 *sin6; | |
struct sockaddr_conn *sconn; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
switch (spc->spc_aaddr.ss_family) { | |
case AF_INET: | |
sin = (struct sockaddr_in *)&spc->spc_aaddr; | |
addr = inet_ntop(AF_INET, &sin->sin_addr, addr_buf, INET_ADDRSTRLEN); | |
break; | |
case AF_INET6: | |
sin6 = (struct sockaddr_in6 *)&spc->spc_aaddr; | |
addr = inet_ntop(AF_INET6, &sin6->sin6_addr, addr_buf, INET6_ADDRSTRLEN); | |
break; | |
case AF_CONN: | |
sconn = (struct sockaddr_conn *)&spc->spc_aaddr; | |
if (snprintf(addr_buf, INET6_ADDRSTRLEN, "%p", sconn->sconn_addr) < 0) { | |
addr_buf[0] = '\0'; | |
} | |
addr = addr_buf; | |
break; | |
default: | |
if (snprintf(addr_buf, INET6_ADDRSTRLEN, "Unknown family %d", spc->spc_aaddr.ss_family) < 0) { | |
addr_buf[0] = '\0'; | |
} | |
addr = addr_buf; | |
break; | |
} | |
fprintf(debug_target, "Peer address %s is now ", addr); | |
switch (spc->spc_state) { | |
case SCTP_ADDR_AVAILABLE: | |
fprintf(debug_target, "SCTP_ADDR_AVAILABLE"); | |
break; | |
case SCTP_ADDR_UNREACHABLE: | |
fprintf(debug_target, "SCTP_ADDR_UNREACHABLE"); | |
break; | |
case SCTP_ADDR_REMOVED: | |
fprintf(debug_target, "SCTP_ADDR_REMOVED"); | |
break; | |
case SCTP_ADDR_ADDED: | |
fprintf(debug_target, "SCTP_ADDR_ADDED"); | |
break; | |
case SCTP_ADDR_MADE_PRIM: | |
fprintf(debug_target, "SCTP_ADDR_MADE_PRIM"); | |
break; | |
case SCTP_ADDR_CONFIRMED: | |
fprintf(debug_target, "SCTP_ADDR_CONFIRMED"); | |
break; | |
default: | |
fprintf(debug_target, "UNKNOWN"); | |
break; | |
} | |
fprintf(debug_target, " (error = 0x%08x).\n", spc->spc_error); | |
return; | |
} | |
static void | |
handle_send_failed_event(struct sctp_send_failed_event *ssfe) { | |
size_t i, n; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
if (ssfe->ssfe_flags & SCTP_DATA_UNSENT) { | |
fprintf(debug_target, "Unsent "); | |
} | |
if (ssfe->ssfe_flags & SCTP_DATA_SENT) { | |
fprintf(debug_target, "Sent "); | |
} | |
if (ssfe->ssfe_flags & ~(SCTP_DATA_SENT | SCTP_DATA_UNSENT)) { | |
fprintf(debug_target, "(flags = %x) ", ssfe->ssfe_flags); | |
} | |
fprintf(debug_target, "message with PPID = %u, SID = %u, flags: 0x%04x due to error = 0x%08x", | |
(uint32_t)ntohl(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid, | |
ssfe->ssfe_info.snd_flags, ssfe->ssfe_error); | |
n = ssfe->ssfe_length - sizeof(struct sctp_send_failed_event); | |
for (i = 0; i < n; i++) { | |
fprintf(debug_target, " 0x%02x", ssfe->ssfe_data[i]); | |
} | |
fprintf(debug_target, ".\n"); | |
return; | |
} | |
static void | |
handle_adaptation_indication(struct sctp_adaptation_event *sai) { | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
fprintf(debug_target, "Adaptation indication: %x.\n", sai-> sai_adaptation_ind); | |
return; | |
} | |
static void | |
handle_shutdown_event(struct sctp_shutdown_event *sse) | |
{ | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
fprintf(debug_target, "Shutdown event.\n"); | |
/* XXX: notify all channels. */ | |
return; | |
} | |
static void | |
handle_stream_reset_event(struct sctp_stream_reset_event *strrst) { | |
uint32_t n, i; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
n = (strrst->strreset_length - sizeof(struct sctp_stream_reset_event)) / sizeof(uint16_t); | |
fprintf(debug_target, "Stream reset event: flags = %x, ", strrst->strreset_flags); | |
if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) { | |
if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { | |
fprintf(debug_target, "incoming/"); | |
} | |
fprintf(debug_target, "incoming "); | |
} | |
if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) { | |
fprintf(debug_target, "outgoing "); | |
} | |
fprintf(debug_target, "stream ids = "); | |
for (i = 0; i < n; i++) { | |
if (i > 0) { | |
fprintf(debug_target, ", "); | |
} | |
fprintf(debug_target, "%d", strrst->strreset_stream_list[i]); | |
} | |
fprintf(debug_target, ".\n"); | |
return; | |
} | |
static void | |
handle_stream_change_event(struct sctp_stream_change_event *strchg) { | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
fprintf(debug_target, "Stream change event: streams (in/out) = (%u/%u), flags = %x.\n", | |
strchg->strchange_instrms, strchg->strchange_outstrms, strchg->strchange_flags); | |
return; | |
} | |
static void | |
handle_remote_error_event(struct sctp_remote_error *sre) { | |
size_t i, n; | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
n = sre->sre_length - sizeof(struct sctp_remote_error); | |
fprintf(debug_target, "Remote Error (error = 0x%04x): ", sre->sre_error); | |
for (i = 0; i < n; i++) { | |
fprintf(debug_target, " 0x%02x", sre-> sre_data[i]); | |
} | |
fprintf(debug_target, ".\n"); | |
return; | |
} | |
void | |
handle_notification(union sctp_notification *notif, size_t n) { | |
if (notif->sn_header.sn_length != (uint32_t)n) { | |
return; | |
} | |
if (debug_target == NULL) { | |
debug_target = DEFAULT_TARGET; | |
} | |
fprintf(debug_target, "handle_notification : "); | |
switch (notif->sn_header.sn_type) { | |
case SCTP_ASSOC_CHANGE: | |
fprintf(debug_target, "SCTP_ASSOC_CHANGE\n"); | |
handle_association_change_event(&(notif->sn_assoc_change)); | |
break; | |
case SCTP_PEER_ADDR_CHANGE: | |
fprintf(debug_target, "SCTP_PEER_ADDR_CHANGE\n"); | |
handle_peer_address_change_event(&(notif->sn_paddr_change)); | |
break; | |
case SCTP_REMOTE_ERROR: | |
fprintf(debug_target, "SCTP_REMOTE_ERROR\n"); | |
handle_remote_error_event(&(notif->sn_remote_error)); | |
break; | |
case SCTP_SHUTDOWN_EVENT: | |
fprintf(debug_target, "SCTP_SHUTDOWN_EVENT\n"); | |
handle_shutdown_event(&(notif->sn_shutdown_event)); | |
break; | |
case SCTP_ADAPTATION_INDICATION: | |
fprintf(debug_target, "SCTP_ADAPTATION_INDICATION\n"); | |
handle_adaptation_indication(&(notif->sn_adaptation_event)); | |
break; | |
case SCTP_PARTIAL_DELIVERY_EVENT: | |
fprintf(debug_target, "SCTP_PARTIAL_DELIVERY_EVENT\n"); | |
break; | |
case SCTP_AUTHENTICATION_EVENT: | |
fprintf(debug_target, "SCTP_AUTHENTICATION_EVENT\n"); | |
break; | |
case SCTP_SENDER_DRY_EVENT: | |
fprintf(debug_target, "SCTP_SENDER_DRY_EVENT\n"); | |
break; | |
case SCTP_NOTIFICATIONS_STOPPED_EVENT: | |
fprintf(debug_target, "SCTP_NOTIFICATIONS_STOPPED_EVENT\n"); | |
break; | |
case SCTP_SEND_FAILED_EVENT: | |
fprintf(debug_target, "SCTP_SEND_FAILED_EVENT\n"); | |
handle_send_failed_event(&(notif->sn_send_failed_event)); | |
break; | |
case SCTP_STREAM_RESET_EVENT: | |
fprintf(debug_target, "SCTP_STREAM_RESET_EVENT\n"); | |
handle_stream_reset_event(&(notif->sn_strreset_event)); | |
break; | |
case SCTP_ASSOC_RESET_EVENT: | |
fprintf(debug_target, "SCTP_ASSOC_RESET_EVENT\n"); | |
break; | |
case SCTP_STREAM_CHANGE_EVENT: | |
fprintf(debug_target, "SCTP_STREAM_CHANGE_EVENT\n"); | |
handle_stream_change_event(&(notif->sn_strchange_event)); | |
break; | |
default: | |
break; | |
} | |
} |
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
// cc -g3 -I /usr/local/include -l usrsctp -L /usr/local/lib -l pthread client.c -o client | |
// [email protected] | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <sys/types.h> | |
#include <usrsctp.h> | |
#define DEBUG 0 | |
#define UDP_ENCAPS_PORT 19818 | |
#define SCTP_LISTEN_PORT 19818 | |
#define BUFFER_SIZE 10240 | |
#define LISTEN_ADDRESS "0.0.0.0" | |
#define timersub(tvp, uvp, vvp) \ | |
do { \ | |
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ | |
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ | |
if ((vvp)->tv_usec < 0) { \ | |
(vvp)->tv_sec--; \ | |
(vvp)->tv_usec += 1000000; \ | |
} \ | |
} while (0) | |
void | |
debug_printf_stack(const char *format, ...); | |
static int | |
receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, | |
size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info); | |
int | |
main(int argc, char **argv) { | |
struct socket *sock; | |
struct sockaddr_in addr; | |
struct sctp_udpencaps encaps; | |
struct sctp_event event; | |
uint16_t event_types[] = {SCTP_ASSOC_CHANGE, | |
SCTP_PEER_ADDR_CHANGE, | |
SCTP_REMOTE_ERROR, | |
SCTP_SHUTDOWN_EVENT, | |
SCTP_ADAPTATION_INDICATION, | |
SCTP_PARTIAL_DELIVERY_EVENT}; | |
unsigned int i; | |
struct sctp_assoc_value av; | |
const int on = 1; | |
ssize_t n; | |
int flags; | |
socklen_t from_len; | |
char buffer[BUFFER_SIZE]; | |
char name[INET_ADDRSTRLEN]; | |
socklen_t infolen; | |
struct sctp_rcvinfo rcv_info; | |
unsigned int infotype; | |
usrsctp_init(UDP_ENCAPS_PORT, NULL, debug_printf_stack); | |
if(DEBUG != 0) | |
usrsctp_sysctl_set_sctp_debug_on(0xFFFFFFFF); | |
else | |
usrsctp_sysctl_set_sctp_debug_on(0); | |
usrsctp_sysctl_set_sctp_blackhole(2); | |
usrsctp_sysctl_set_sctp_no_csum_on_loopback(0); | |
if ((sock = usrsctp_socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, receive_cb, NULL, 0, NULL)) == NULL) { | |
perror("usrsctp_socket"); | |
} | |
memset(&av, 0, sizeof(struct sctp_assoc_value)); | |
av.assoc_id = SCTP_ALL_ASSOC; | |
av.assoc_value = 47; | |
if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_CONTEXT, (const void*)&av, (socklen_t)sizeof(struct sctp_assoc_value)) < 0) { | |
perror("usrsctp_setsockopt SCTP_CONTEXT"); | |
} | |
if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(int)) < 0) { | |
perror("usrsctp_setsockopt SCTP_RECVRCVINFO"); | |
} | |
if (UDP_ENCAPS_PORT > 0) { | |
memset(&encaps, 0, sizeof(struct sctp_udpencaps)); | |
encaps.sue_address.ss_family = AF_INET; | |
encaps.sue_port = UDP_ENCAPS_PORT; | |
if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_REMOTE_UDP_ENCAPS_PORT, | |
(const void*)&encaps, (socklen_t)sizeof(struct sctp_udpencaps)) < 0) { | |
perror("usrsctp_setsockopt SCTP_REMOTE_UDP_ENCAPS_PORT"); | |
} | |
} | |
memset(&event, 0, sizeof(event)); | |
event.se_assoc_id = SCTP_FUTURE_ASSOC; | |
event.se_on = 1; | |
for (i = 0; i < (unsigned int)(sizeof(event_types)/sizeof(uint16_t)); i++) { | |
event.se_type = event_types[i]; | |
if (usrsctp_setsockopt(sock, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(struct sctp_event)) < 0) { | |
perror("usrsctp_setsockopt SCTP_EVENT"); | |
} | |
} | |
memset((void *)&addr, 0, sizeof(struct sockaddr_in)); | |
addr.sin_len = sizeof(struct sockaddr_in); | |
addr.sin_family = AF_INET; | |
addr.sin_port = htons(SCTP_LISTEN_PORT); | |
inet_aton(LISTEN_ADDRESS, &addr.sin_addr.s_addr); | |
if (usrsctp_bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { | |
perror("usrsctp_bind"); | |
} | |
if (usrsctp_listen(sock, 1) < 0) { | |
perror("usrsctp_listen"); | |
} | |
while (1) { | |
sleep(1); | |
} | |
usrsctp_close(sock); | |
while (usrsctp_finish() != 0) { | |
sleep(1); | |
} | |
return (0); | |
} | |
static int | |
receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, | |
size_t datalen, struct sctp_rcvinfo rcv, int flags, void *ulp_info) { | |
if(flags & MSG_NOTIFICATION) | |
return 0; | |
else | |
printf("[R]: %s\n", (char*)data); | |
return 0; | |
} | |
void | |
debug_printf_stack(const char *format, ...) { | |
va_list ap; | |
char charbuf[1024]; | |
static struct timeval time_main; | |
struct timeval time_now; | |
struct timeval time_delta; | |
if (time_main.tv_sec == 0 && time_main.tv_usec == 0) { | |
gettimeofday(&time_main, NULL); | |
} | |
gettimeofday(&time_now, NULL); | |
timersub(&time_now, &time_main, &time_delta); | |
va_start(ap, format); | |
if (vsnprintf(charbuf, 1024, format, ap) < 0) { | |
charbuf[0] = '\0'; | |
} | |
va_end(ap); | |
printf("[S][%u.%03u] %s", (unsigned int) time_delta.tv_sec, (unsigned int) time_delta.tv_usec / 1000, charbuf); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is just meant to be a simple client/server example using the SCTP protocol with the usrsctp library. I found the programs/examples made available in the library frustratingly difficult to sift through and parse out because they don't make a hell of a lot of sense unless you know what's going on.
By default this examples use UDP encapsulation, for native SCTP, set:
server:
#define UDP_ENCAPS_PORT 0
client:
#define LOCAL_UDP_ENCAPS_PORT 0
and use
sudo ./server
as well assudo ./client
for a confirmation, run
sudo tcpdump -vvv -n -e -ttt -i lo0 sctp -X