This section details two uses of SCTP sockets.
Example 7-17 SCTP Echo Client/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. */ /* * IPv4 echo client. */ /* To enable socket features used for SCTP socket. */ #define _XPG4_2 #define __EXTENSIONS__ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <netinet/sctp.h> #include <errno.h> #define BUFLEN 2048 #define SERVER_PORT 5000 #define MAX_STREAM 64 static void usagBe(char *a0) { fprintf(stderr, "Usage: %s <server address>\n", a0); } static void print_notif(char *buf) { union sctp_notification *snp; struct sctp_assoc_change *sac; snp = (union sctp_notification *)buf; /* We only subscribe the association change event. */ if (snp->sn_header.sn_type != SCTP_ASSOC_CHANGE) { fprintf(stderr, "unexpected notification type: %d\n", snp->sn_header.sn_type); exit(1); } sac = &snp->sn_assoc_change; printf("[ Receive assocication change event: state = %hu," error = %hu," instr = %hu, outstr = %hu ]\n", sac->sac_state, sac->sac_error,sac->sac_inbound_streams, sac->sac_outbound_streams); } /* * Read from the network. */ static void readit(void *vfdp) { int fd; ssize_t n; char buf[BUFLEN]; struct iovec iov[1]; int flags; socklen_t info_len; uint_t info_type; struct sctp_rcvinfo info; union sctp_notification *snp; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); fd = *(int *)vfdp; /* Initialize the iov for receiving */ memset(buf, 0, BUFLEN); iov->iov_base = buf; iov->iov_len = BUFLEN; info_len = sizeof (info); info_type = 0; flags = 0; while ((n = sctp_recvv(fd, iov, 1, NULL, NULL, &info, &info_len,&info_type, &flags)) > 0) { /* Intercept notifications here */ if (flags & MSG_NOTIFICATION) { print_notif(buf); continue; } /* The message should be accompanied by sctp_rcvinfo. */ if (info_type != SCTP_RECVV_RCVINFO) { fprintf(stderr, "no sctp_rcvinfo attached\n"); exit(1); } printf("[ Receive echo (%u bytes): stream = %hu, ssn = %hu," "tsn = %hu, flags = %hx, ppid = %u ]\n", n, info.rcv_sid, info.rcv_ssn, info.rcv_tsn, info.rcv_flags, info.rcv_ppid); flags = 0; info_len = sizeof (info); } if (n < 0) { perror("sctp_recvv"); exit(1); } close(fd); exit(0); } static void echo(struct sockaddr_in *addrs, int addrcnt) { int fd; uchar_t buf[BUFLEN]; ssize_t n; int perr; pthread_t tid; struct iovec iov[1]; int ret, on; struct sctp_sndinfo info; struct sctp_initmsg initmsg; struct sctp_event event; /* Create a one-one SCTP socket */ if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) { perror("socket"); exit(1); } /* * We are interested in association change events and we want * to get sctp_rcvinfo in each receive. */ event.se_assoc_id = 0; /* Ignored for one-one SCTP socket */ event.se_type = SCTP_ASSOC_CHANGE; event.se_on = 1; ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof (event)); if (ret < 0) { perror("setsockopt SCTP_EVENT"); exit(1); } on = 1; ret = setsockopt(fd, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof (on)); if (ret < 0) { perror("setsockopt SCTP_RECVRCVINFO"); exit(1); } /* * Set the SCTP stream parameters to tell the other side when * setting up the association. */ memset(&initmsg, 0, sizeof (struct sctp_initmsg)); initmsg.sinit_num_ostreams = MAX_STREAM; initmsg.sinit_max_instreams = MAX_STREAM; initmsg.sinit_max_attempts = MAX_STREAM; ret = setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,sizeof (struct sctp_initmsg)); if (ret < 0) { perror("setsockopt SCTP_INITMSG"); exit(1); } /* Now connect to the peer. */ if (sctp_connectx(fd, (struct sockaddr *)addrs, addrcnt, NULL) == -1) { perror("sctp_connectx"); exit(1); } /* Initialize the struct sctp_sndinfo for sending. */ memset(&info, 0, sizeof (info)); /* Start sending to stream 0. */ info.snd_sid = 0; /* * Note that the server is expected to echo back the snd_ppid value. * So we don't need to do any conversion here. But if the server needs * to understand this value, we need to do a htonl() on it so that the * server side can do a ntohl() to convert it back to the host byte * order. */ info.snd_ppid = 0; /* Create a thread to receive network traffic. */ perr = pthread_create(&tid, NULL, (void *(*)(void *))readit, &fd); if (perr != 0) { fprintf(stderr, "pthread_create: %d\n", perr); exit(1); } iov->iov_base = buf; /* Read from stdin and then send to the echo server. */ while ((n = read(fileno(stdin), buf, BUFLEN)) > 0) { iov->iov_len = n; if (sctp_sendv(fd, iov, 1, NULL, 0, &info, sizeof (info), SCTP_SENDV_SNDINFO, 0) < 0) { perror("sctp_sendv"); exit(1); } /* Send the next message to a different stream. */ info.snd_sid = (info.snd_sid + 1) % MAX_STREAM; info.snd_ppid++; } pthread_cancel(tid); close(fd); } static struct sockaddr_in * setup_addrs(const char *name, int *addrcnt) { int num_addrs, i; int error; struct hostent *hp; struct sockaddr_in *addrs; hp = getipnodebyname(name, AF_INET, AI_DEFAULT, &error); if (hp == NULL) { fprintf(stderr, "host %s not found\n", name); return (NULL); } for (num_addrs = 0; hp->h_addr_list[num_addrs] != NULL; num_addrs++) ; addrs = malloc((num_addrs) * sizeof (*addrs)); if (addrs == NULL) { fprintf(stderr, "cannot allocate address list\n"); return (NULL); } for (i = 0; i < num_addrs; i++) { addrs[i].sin_family = AF_INET; addrs[i].sin_addr.s_addr = *(ipaddr_t *)hp->h_addr_list[i]; addrs[i].sin_port = htons(SERVER_PORT); } *addrcnt = num_addrs; return (addrs); } int main(int argc, char **argv) { struct sockaddr_in *addrs; int addrcnt; if (argc < 2) { usage(*argv); exit(1); } /* Find the host to connect to. */ if ((addrs = setup_addrs(argv[1], &addrcnt)) == NULL) exit(1); echo(addrs, addrcnt); return (0); }Example 7-18 SCTP Echo Server
/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. */ /* * IPv4 echo server */ /* To enable socket features used for SCTP socket. */ #define _XPG4_2 #define __EXTENSIONS__ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <netinet/sctp.h> #include <netdb.h> #define BUFLEN 1024 #define SERVER_PORT 5000 #define MAX_STREAM 64 /* * Given an event notification, print out what it is. */ static void handle_event(void *buf) { struct sctp_assoc_change *sac; struct sctp_send_failed_event *ssfe; struct sctp_paddr_change *spc; struct sctp_remote_error *sre; union sctp_notification *snp; char addrbuf[INET6_ADDRSTRLEN]; const char *ap; struct sockaddr_in *sin; snp = buf; switch (snp->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: sac = &snp->sn_assoc_change; printf(">>> assoc_change: state=%hu, error=%hu, instr=%hu " "outstr=%hu\n", sac->sac_state, sac->sac_error, sac->sac_inbound_streams, sac->sac_outbound_streams); break; case SCTP_SEND_FAILED_EVENT: ssfe = &snp->sn_send_failed_event; printf(">>> sendfailed: len=%hu err=%d\n", ssfe->ssfe_length, ssfe->ssfe_error); break; case SCTP_PEER_ADDR_CHANGE: spc = &snp->sn_paddr_change; if (spc->spc_aaddr.ss_family != AF_INET) { fprintf(stderr, "getmsg: unexpected family %d\n",spc->spc_aaddr.ss_family); exit(1); } else { sin = (struct sockaddr_in *)&spc->spc_aaddr; ap = inet_ntop(AF_INET, &sin->sin_addr, addrbuf,INET6_ADDRSTRLEN); } printf(">>> intf_change: %s state=%d, error=%d\n", ap, spc->spc_state, spc->spc_error); break; case SCTP_REMOTE_ERROR: sre = &snp->sn_remote_error; printf(">>> remote_error: err=%hu len=%hu\n", ntohs(sre->sre_error), ntohs(sre->sre_length)); break; case SCTP_SHUTDOWN_EVENT: printf(">>> shutdown event\n"); break; default: printf(">>> unexpected type: %hu\n", snp->sn_header.sn_type); break; } } /* * Receive a message from the network. */ static ssize_t getmsg(int fd, struct iovec *iov, struct sctp_rcvinfo *info, int *flags) { ssize_t tot = 0, nr; size_t buflen; socklen_t info_len; uint_t info_type; char *buf; buf = iov->iov_base; buflen = iov->iov_len; /* Loop until a whole message is received. */ for (;;) { info_len = sizeof (*info); memset(info, 0, sizeof (*info)); *flags = 0; nr = sctp_recvv(fd, iov, 1, NULL, NULL, info, &info_len, &info_type, flags); if (nr <= 0) { /* EOF or error */ iov->iov_base = buf; return (nr); } tot += nr; /* Whole message/notification is received, return it. */ if (*flags & MSG_EOR || *flags & MSG_NOTIFICATION) { iov->iov_base = buf; /* Buffer may be realloc(). Return the new size. */ iov->iov_len = buflen; return (tot); } /* Only sctp_rcvinfo is expected. */ if (info_type != SCTP_RECVV_RCVINFO) { fprintf(stderr, "unexpected info received: %d\n", info_type); iov->iov_base = buf; return (-1); } /* Maybe we need a bigger buffer, do realloc(). */ if (buflen == tot) { buf = realloc(buf, buflen * 2); if (buf == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } buflen *= 2; } /* Set the next read offset */ iov->iov_base = buf + tot; iov->iov_len = buflen - tot; } } /* * The echo server. */ static void echo(int fd) { ssize_t nr; size_t buflen; int flags; struct iovec iov[1]; struct sctp_rcvinfo rinfo; struct sctp_sndinfo sinfo; if ((iov->iov_base = malloc(BUFLEN)) == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } iov->iov_len = BUFLEN; memset(&sinfo, 0, sizeof (sinfo)); /* Wait for something to echo */ while ((nr = getmsg(fd, iov, &rinfo, &flags)) > 0) { /* Intercept notifications here */ if (flags & SCTP_NOTIFICATION) { handle_event(iov->iov_base); continue; } printf(">>> got %u bytes on stream %hu: ", nr, rinfo.rcv_sid); fflush(stdout); write(fileno(stdout), iov->iov_base, nr); fflush(stdout); /* The buffer may be realloc(), so get the new size. */ buflen = iov->iov_len; /* * Echo the message back using the incoming info. * * Note that rcv_sid is in host byte order. But rcv_ppid is * what is stored by the peer. If both sides wnat to use this * value for communication (interpreting it on both sides), * the sender needs to do htonl() when setting snd_ppid. And * the receiver side needs to do ntohl() to convert rcv_ppid * back to the host byte order. */ sinfo.snd_sid = rinfo.rcv_sid; sinfo.snd_ppid = rinfo.rcv_ppid; iov->iov_len = nr; if (sctp_sendv(fd, iov, 1, NULL, 0, &sinfo, sizeof (sinfo), SCTP_SENDV_SNDINFO, 0) < 0) { fprintf(stderr, "sctp_sendv\n"); exit(1); } /* Restore the original buffer size. */ iov->iov_len = buflen; } free(iov->iov_base); close(fd); } static void subscribe_event(int fd, uint16_t event) { struct sctp_event ev; int ret; ev.se_assoc_id = 0; /* Ignored for one-one SCTP socket */ ev.se_type = event; ev.se_on = 1; ret = setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &ev, sizeof (ev)); if (ret < 0) { fprintf(stderr, "%s: setsockopt SCTP_EVENT: %d\n",strerror(errno), event); exit(1); } } /* List of events we are interested in. */ static uint16_t event_interested[] = { SCTP_ASSOC_CHANGE, SCTP_SEND_FAILED_EVENT, SCTP_PEER_ADDR_CHANGE, SCTP_REMOTE_ERROR, SCTP_SHUTDOWN_EVENT }; int main(void) { int lfd; int cfd; int onoff; int i; struct sockaddr_in sin[1]; struct sctp_initmsg initmsg; if ((lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) == -1) { perror("socket"); exit(1); } sin->sin_family = AF_INET; sin->sin_port = htons(SERVER_PORT); sin->sin_addr.s_addr = INADDR_ANY; if (bind(lfd, (struct sockaddr *)sin, sizeof (*sin)) == -1) { perror("bind"); exit(1); } if (listen(lfd, 1) == -1) { perror("listen"); exit(1); } (void) memset(&initmsg, 0, sizeof (struct sctp_initmsg)); initmsg.sinit_num_ostreams = MAX_STREAM; initmsg.sinit_max_instreams = MAX_STREAM; initmsg.sinit_max_attempts = MAX_STREAM; if (setsockopt(lfd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof (struct sctp_initmsg)) < 0) { perror("SCTP_INITMSG"); exit(1); } /* Subscribe to events. */ for (i = 0; i < sizeof (event_interested) / sizeof (uint16_t); i++) subscribe_event(lfd, event_interested[i]); /* Wait for new associations */ for (;;) { if ((cfd = accept(lfd, NULL, 0)) == -1) { perror("accept"); exit(1); } /* Subcribe to interesting events for the new association. */ for (i = 0; i < sizeof (event_interested) / sizeof (int); i++) subscribe_event(cfd, event_interested[i]); /* We want sctp_rcvinfo in each receive. */ onoff = 1; i = setsockopt(cfd, IPPROTO_SCTP, SCTP_RECVRCVINFO, &onoff, sizeof (onoff)); if (i < 0) { perror("setsockopt SCTP_RECVRCVINFO"); close(cfd); continue; } /* Echo back any and all data */ echo(cfd); } }