| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- /*
- * $Id$
- *
- * Copyright (C) 2008 iptelorg GmbH
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- /*
- * sctp one to many
- */
- /*
- * History:
- * --------
- * 2008-08-07 initial version (andrei)
- */
- #ifdef USE_SCTP
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netinet/in_systm.h>
- #include <netinet/ip.h>
- #include <netinet/sctp.h>
- #include <errno.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include "sctp_server.h"
- #include "sctp_options.h"
- #include "globals.h"
- #include "config.h"
- #include "dprint.h"
- #include "receive.h"
- #include "mem/mem.h"
- #include "ip_addr.h"
- #include "cfg/cfg_struct.h"
- /* check if the underlying OS supports sctp
- returns 0 if yes, -1 on error */
- int sctp_check_support()
- {
- int s;
- char buf[256];
-
- s = socket(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
- if (s!=-1){
- close(s);
- if (sctp_check_compiled_sockopts(buf, sizeof(buf))!=0){
- LOG(L_WARN, "WARNING: sctp: your ser version was compiled"
- " without support for the following sctp options: %s"
- ", which might cause unforseen problems \n", buf);
- LOG(L_WARN, "WARNING: sctp: please consider recompiling ser with"
- " an upgraded sctp library version\n");
- }
- return 0;
- }
- return -1;
- }
- /* append a token to a buffer (uses space between tokens) */
- inline static void append_tok2buf(char* buf, int blen, char* tok)
- {
- char* p;
- char* end;
- int len;
-
- if (buf && blen){
- end=buf+blen;
- p=memchr(buf, 0, blen);
- if (p==0) goto error;
- if (p!=buf && p<(end-1)){
- *p=' ';
- p++;
- }
- len=MIN_int(strlen(tok), end-1-p);
- memcpy(p, tok, len);
- p[len]=0;
- }
- error:
- return;
- }
- /* check if support fot all the needed sockopts was compiled;
- an ascii list of the unsuported options is returned in buf
- returns 0 on success and -number of unsuported options on failure
- (<0 on failure)
- */
- int sctp_check_compiled_sockopts(char* buf, int size)
- {
- int err;
- err=0;
- if (buf && (size>0)) *buf=0; /* "" */
- #ifndef SCTP_FRAGMENT_INTERLEAVE
- err++;
- append_tok2buf(buf, size, "SCTP_FRAGMENT_INTERLEAVE");
- #endif
- #ifndef SCTP_PARTIAL_DELIVERY_POINT
- err++;
- append_tok2buf(buf, size, "SCTP_PARTIAL_DELIVERY_POINT");
- #endif
- #ifndef SCTP_NODELAY
- err++;
- append_tok2buf(buf, size, "SCTP_NODELAY");
- #endif
- #ifndef SCTP_DISABLE_FRAGMENTS
- err++;
- append_tok2buf(buf, size, "SCTP_DISABLE_FRAGMENTS");
- #endif
- #ifndef SCTP_AUTOCLOSE
- err++;
- append_tok2buf(buf, size, "SCTP_AUTOCLOSE");
- #endif
- #ifndef SCTP_EVENTS
- err++;
- append_tok2buf(buf, size, "SCTP_EVENTS");
- #endif
-
- return -err;
- }
- /* init all the sockaddr_union members of the socket_info struct
- returns 0 on success and -1 on error */
- inline static int sctp_init_su(struct socket_info* sock_info)
- {
- union sockaddr_union* addr;
- struct addr_info* ai;
-
- addr=&sock_info->su;
- if (init_su(addr, &sock_info->address, sock_info->port_no)<0){
- LOG(L_ERR, "ERROR: sctp_init_su: could not init sockaddr_union for"
- "primary sctp address %.*s:%d\n",
- sock_info->address_str.len, sock_info->address_str.s,
- sock_info->port_no );
- goto error;
- }
- for (ai=sock_info->addr_info_lst; ai; ai=ai->next)
- if (init_su(&ai->su, &ai->address, sock_info->port_no)<0){
- LOG(L_ERR, "ERROR: sctp_init_su: could not init"
- "backup sctp sockaddr_union for %.*s:%d\n",
- ai->address_str.len, ai->address_str.s,
- sock_info->port_no );
- goto error;
- }
- return 0;
- error:
- return -1;
- }
- /* set common (for one to many and one to one) sctp socket options
- tries to ignore non-critical errors (it will only log them), for
- improved portability (for example older linux kernel version support
- only a limited number of sctp socket options)
- returns 0 on success, -1 on error
- WARNING: please keep it sync'ed w/ sctp_check_compiled_sockopts() */
- static int sctp_init_sock_opt_common(int s)
- {
- struct sctp_event_subscribe es;
- int optval;
- socklen_t optlen;
- int sctp_err;
-
- sctp_err=0;
- /* set tos */
- optval = tos;
- if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*)&optval,sizeof(optval)) ==-1){
- LOG(L_WARN, "WARNING: sctp_init_sock_opt_common: setsockopt tos: %s\n",
- strerror(errno));
- /* continue since this is not critical */
- }
-
- /* set receive buffer: SO_RCVBUF*/
- if (sctp_options.sctp_so_rcvbuf){
- optval=sctp_options.sctp_so_rcvbuf;
- if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt:"
- " SO_RCVBUF (%d): %s\n", optval, strerror(errno));
- /* continue, non-critical */
- }
- }
-
- /* set send buffer: SO_SNDBUF */
- if (sctp_options.sctp_so_sndbuf){
- optval=sctp_options.sctp_so_sndbuf;
- if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt:"
- " SO_SNDBUF (%d): %s\n", optval, strerror(errno));
- /* continue, non-critical */
- }
- }
-
- /* disable fragments interleave (SCTP_FRAGMENT_INTERLEAVE) --
- * we don't want partial delivery, so fragment interleave must be off too
- */
- #ifdef SCTP_FRAGMENT_INTERLEAVE
- optval=0;
- if (setsockopt(s, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE ,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
- "SCTP_FRAGMENT_INTERLEAVE: %s\n", strerror(errno));
- sctp_err++;
- /* try to continue */
- }
- #else
- #warning no sctp lib support for SCTP_FRAGMENT_INTERLEAVE, consider upgrading
- #endif /* SCTP_FRAGMENT_INTERLEAVE */
-
- /* turn off partial delivery: on linux setting SCTP_PARTIAL_DELIVERY_POINT
- * to 0 or a very large number seems to be enough, however the portable
- * way to do it is to set it to the socket receive buffer size
- * (this is the maximum value allowed in the sctp api draft) */
- #ifdef SCTP_PARTIAL_DELIVERY_POINT
- optlen=sizeof(optval);
- if (getsockopt(s, SOL_SOCKET, SO_RCVBUF,
- (void*)&optval, &optlen) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: getsockopt: "
- "SO_RCVBUF: %s\n", strerror(errno));
- /* try to continue */
- optval=0;
- }
- if (setsockopt(s, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
- "SCTP_PARTIAL_DELIVERY_POINT (%d): %s\n",
- optval, strerror(errno));
- sctp_err++;
- /* try to continue */
- }
- #else
- #warning no sctp lib support for SCTP_PARTIAL_DELIVERY_POINT, consider upgrading
- #endif /* SCTP_PARTIAL_DELIVERY_POINT */
-
- /* nagle / no delay */
- #ifdef SCTP_NODELAY
- optval=1;
- if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
- "SCTP_NODELAY: %s\n", strerror(errno));
- sctp_err++;
- /* non critical, try to continue */
- }
- #else
- #warning no sctp lib support for SCTP_NODELAY, consider upgrading
- #endif /* SCTP_NODELAY */
-
- /* enable message fragmentation (SCTP_DISABLE_FRAGMENTS) (on send) */
- #ifdef SCTP_DISABLE_FRAGMENTS
- optval=0;
- if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
- "SCTP_DISABLE_FRAGMENTS: %s\n", strerror(errno));
- sctp_err++;
- /* non critical, try to continue */
- }
- #else
- #warning no sctp lib support for SCTP_DISABLE_FRAGMENTS, consider upgrading
- #endif /* SCTP_DISABLE_FRAGMENTS */
-
- /* set autoclose */
- #ifdef SCTP_AUTOCLOSE
- optval=sctp_options.sctp_autoclose;
- if (setsockopt(s, IPPROTO_SCTP, SCTP_AUTOCLOSE,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
- "SCTP_AUTOCLOSE: %s (critical)\n", strerror(errno));
- /* critical: w/o autoclose we could have sctp connection living
- forever (if the remote side doesn't close them) */
- sctp_err++;
- goto error;
- }
- #else
- #error SCTP_AUTOCLOSE not supported, please upgrade your sctp library
- #endif /* SCTP_AUTOCLOSE */
-
- memset(&es, 0, sizeof(es));
- /* SCTP_EVENTS for SCTP_SNDRCV (sctp_data_io_event) -> per message
- * information in sctp_sndrcvinfo */
- es.sctp_data_io_event=1;
- /* enable association event notifications */
- es.sctp_association_event=1; /* SCTP_ASSOC_CHANGE */
- es.sctp_address_event=1; /* enable address events notifications */
- es.sctp_send_failure_event=1; /* SCTP_SEND_FAILED */
- es.sctp_peer_error_event=1; /* SCTP_REMOTE_ERROR */
- es.sctp_shutdown_event=1; /* SCTP_SHUTDOWN_EVENT */
- es.sctp_partial_delivery_event=1; /* SCTP_PARTIAL_DELIVERY_EVENT */
- /* es.sctp_adaptation_layer_event=1; - not supported by lksctp<=1.0.6*/
- /* es.sctp_authentication_event=1; -- not supported on linux 2.6.25 */
-
- /* enable the SCTP_EVENTS */
- #ifdef SCTP_EVENTS
- if (setsockopt(s, IPPROTO_SCTP, SCTP_EVENTS, &es, sizeof(es))==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_opt_common: setsockopt: "
- "SCTP_EVENTS: %s\n", strerror(errno));
- sctp_err++;
- /* non critical, try to continue */
- }
- #else
- #warning no sctp lib support for SCTP_EVENTS, consider upgrading
- #endif /* SCTP_EVENTS */
-
- if (sctp_err){
- LOG(L_ERR, "ERROR: sctp: setting some sctp sockopts failed, "
- "consider upgrading your kernel\n");
- }
- return 0;
- error:
- return -1;
- }
- /* bind all addresses from sock (sockaddr_unions)
- returns 0 on success, .1 on error */
- static int sctp_bind_sock(struct socket_info* sock_info)
- {
- struct addr_info* ai;
- union sockaddr_union* addr;
-
- addr=&sock_info->su;
- /* bind the addresses*/
- if (bind(sock_info->socket, &addr->s, sockaddru_len(*addr))==-1){
- LOG(L_ERR, "ERROR: sctp_bind_sock: bind(%x, %p, %d) on %s: %s\n",
- sock_info->socket, &addr->s,
- (unsigned)sockaddru_len(*addr),
- sock_info->address_str.s,
- strerror(errno));
- #ifdef USE_IPV6
- if (addr->s.sa_family==AF_INET6)
- LOG(L_ERR, "ERROR: sctp_bind_sock: might be caused by using a "
- "link local address, try site local or global\n");
- #endif
- goto error;
- }
- for (ai=sock_info->addr_info_lst; ai; ai=ai->next)
- if (sctp_bindx(sock_info->socket, &ai->su.s, 1, SCTP_BINDX_ADD_ADDR)
- ==-1){
- LOG(L_ERR, "ERROR: sctp_bind_sock: sctp_bindx(%x, %.*s:%d, 1, ...)"
- " on %s:%d : [%d] %s (trying to continue)\n",
- sock_info->socket,
- ai->address_str.len, ai->address_str.s,
- sock_info->port_no,
- sock_info->address_str.s, sock_info->port_no,
- errno, strerror(errno));
- #ifdef USE_IPV6
- if (ai->su.s.sa_family==AF_INET6)
- LOG(L_ERR, "ERROR: sctp_bind_sock: might be caused by using a "
- "link local address, try site local or global\n");
- #endif
- /* try to continue, a secondary address bind failure is not
- * critical */
- }
- return 0;
- error:
- return -1;
- }
- /* init, bind & start listening on the corresp. sctp socket
- returns 0 on success, -1 on error */
- int sctp_init_sock(struct socket_info* sock_info)
- {
- union sockaddr_union* addr;
-
- sock_info->proto=PROTO_SCTP;
- addr=&sock_info->su;
- if (sctp_init_su(sock_info)!=0)
- goto error;
- sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_SEQPACKET,
- IPPROTO_SCTP);
- if (sock_info->socket==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock: socket: %s\n", strerror(errno));
- goto error;
- }
- INFO("sctp: socket %d initialized (%p)\n", sock_info->socket, sock_info);
- /* make socket non-blocking */
- #if 0
- /* recvmsg must block so use blocking sockets
- * and send with MSG_DONTWAIT */
- optval=fcntl(sock_info->socket, F_GETFL);
- if (optval==-1){
- LOG(L_ERR, "ERROR: init_sctp: fnctl failed: (%d) %s\n",
- errno, strerror(errno));
- goto error;
- }
- if (fcntl(sock_info->socket, F_SETFL, optval|O_NONBLOCK)==-1){
- LOG(L_ERR, "ERROR: init_sctp: fcntl: set non-blocking failed:"
- " (%d) %s\n", errno, strerror(errno));
- goto error;
- }
- #endif
- /* set sock opts */
- if (sctp_init_sock_opt_common(sock_info->socket)!=0)
- goto error;
- /* SCTP_EVENTS for send dried out -> present in the draft not yet
- * present in linux (might help to detect when we could send again to
- * some peer, kind of poor's man poll on write, based on received
- * SCTP_SENDER_DRY_EVENTs */
-
- if (sctp_bind_sock(sock_info)<0)
- goto error;
- if (listen(sock_info->socket, 1)<0){
- LOG(L_ERR, "ERROR: sctp_init_sock: listen(%x, 1) on %s: %s\n",
- sock_info->socket, sock_info->address_str.s,
- strerror(errno));
- goto error;
- }
- return 0;
- error:
- return -1;
- }
- #define USE_SCTP_OO
- #ifdef USE_SCTP_OO
- /* init, bind & start listening on the corresp. sctp socket, using
- sctp one-to-one mode
- returns 0 on success, -1 on error */
- int sctp_init_sock_oo(struct socket_info* sock_info)
- {
- union sockaddr_union* addr;
- int optval;
-
- sock_info->proto=PROTO_SCTP;
- addr=&sock_info->su;
- if (sctp_init_su(sock_info)!=0)
- goto error;
- sock_info->socket = socket(AF2PF(addr->s.sa_family), SOCK_STREAM,
- IPPROTO_SCTP);
- if (sock_info->socket==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_oo: socket: %s\n", strerror(errno));
- goto error;
- }
- INFO("sctp:oo socket %d initialized (%p)\n", sock_info->socket, sock_info);
- /* make socket non-blocking */
- optval=fcntl(sock_info->socket, F_GETFL);
- if (optval==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_oo: fnctl failed: (%d) %s\n",
- errno, strerror(errno));
- goto error;
- }
- if (fcntl(sock_info->socket, F_SETFL, optval|O_NONBLOCK)==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_oo: fcntl: set non-blocking failed:"
- " (%d) %s\n", errno, strerror(errno));
- goto error;
- }
-
- /* set sock opts */
- if (sctp_init_sock_opt_common(sock_info->socket)!=0)
- goto error;
-
- #ifdef SCTP_REUSE_PORT
- /* set reuse port */
- optval=1;
- if (setsockopt(sock_info->socket, IPPROTO_SCTP, SCTP_REUSE_PORT ,
- (void*)&optval, sizeof(optval)) ==-1){
- LOG(L_ERR, "ERROR: sctp_init_sock_oo: setsockopt: "
- "SCTP_REUSE_PORT: %s\n", strerror(errno));
- goto error;
- }
- #endif /* SCTP_REUSE_PORT */
-
- if (sctp_bind_sock(sock_info)<0)
- goto error;
- if (listen(sock_info->socket, 1)<0){
- LOG(L_ERR, "ERROR: sctp_init_sock_oo: listen(%x, 1) on %s: %s\n",
- sock_info->socket, sock_info->address_str.s,
- strerror(errno));
- goto error;
- }
- return 0;
- error:
- return -1;
- }
- #endif /* USE_SCTP_OO */
- static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
- struct sctp_sndrcvinfo* sndrcv_info);
- /* debugging: return a string name for SCTP_ASSOC_CHANGE state */
- static char* sctp_assoc_change_state2s(short int state)
- {
- char* s;
-
- switch(state){
- case SCTP_COMM_UP:
- s="SCTP_COMM_UP";
- break;
- case SCTP_COMM_LOST:
- s="SCTP_COMM_LOST";
- break;
- case SCTP_RESTART:
- s="SCTP_RESTART";
- break;
- case SCTP_SHUTDOWN_COMP:
- s="SCTP_SHUTDOWN_COMP";
- break;
- case SCTP_CANT_STR_ASSOC:
- s="SCTP_CANT_STR_ASSOC";
- break;
- default:
- s="UNKNOWN";
- break;
- };
- return s;
- }
- /* debugging: return a string name for a SCTP_PEER_ADDR_CHANGE state */
- static char* sctp_paddr_change_state2s(unsigned int state)
- {
- char* s;
-
- switch (state){
- case SCTP_ADDR_AVAILABLE:
- s="SCTP_ADDR_AVAILABLE";
- break;
- case SCTP_ADDR_UNREACHABLE:
- s="SCTP_ADDR_UNREACHABLE";
- break;
- case SCTP_ADDR_REMOVED:
- s="SCTP_ADDR_REMOVED";
- break;
- case SCTP_ADDR_ADDED:
- s="SCTP_ADDR_ADDED";
- break;
- case SCTP_ADDR_MADE_PRIM:
- s="SCTP_ADDR_MADE_PRIM";
- break;
- /* not supported by lksctp 1.0.6
- case SCTP_ADDR_CONFIRMED:
- s="SCTP_ADDR_CONFIRMED";
- break;
- */
- default:
- s="UNKNOWN";
- break;
- }
- return s;
- }
- /* handle SCTP_SEND_FAILED notifications: if packet marked for retries
- * retry the send (with 0 associd)
- * returns 0 on success, -1 on failure
- */
- static int sctp_handle_send_failed(struct socket_info* si,
- union sockaddr_union* su,
- char* buf, unsigned len)
- {
- union sctp_notification* snp;
- struct sctp_sndrcvinfo sinfo;
- struct dest_info dst;
- char* data;
- unsigned data_len;
- int retries;
- int ret;
-
- ret=-1;
- snp=(union sctp_notification*) buf;
- retries=snp->sn_send_failed.ssf_info.sinfo_context;
-
- /* don't retry on explicit remote error
- * (unfortunately we can't be more picky than this, we get no
- * indication in the SEND_FAILED notification for other error
- * reasons (e.g. ABORT received, INIT timeout a.s.o)
- */
- if (retries && (snp->sn_send_failed.ssf_error==0)) {
- DBG("sctp: RETRY-ing (%d)\n", retries);
- retries--;
- data=(char*)snp->sn_send_failed.ssf_data;
- data_len=snp->sn_send_failed.ssf_length -
- sizeof(struct sctp_send_failed);
-
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.sinfo_flags=SCTP_UNORDERED;
- #ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY
- if (sctp_options.sctp_send_ttl){
- sinfo.sinfo_pr_policy=SCTP_PR_SCTP_TTL;
- sinfo.sinfo_pr_value=sctp_options.sctp_send_ttl;
- }else
- sinfo.info_pr_policy=SCTP_PR_SCTP_NONE;
- #else
- sinfo.sinfo_timetolive=sctp_options.sctp_send_ttl;
- #endif
- sinfo.sinfo_context=retries;
-
- dst.to=*su;
- dst.send_sock=si;
- dst.id=0;
- dst.proto=PROTO_SCTP;
- #ifdef USE_COMP
- dst.comp=COMP_NONE;
- #endif
-
- ret=sctp_msg_send_raw(&dst, data, data_len, &sinfo);
- }
-
- return (ret>0)?0:ret;
- }
- static int sctp_handle_notification(struct socket_info* si,
- union sockaddr_union* su,
- char* buf, unsigned len)
- {
- union sctp_notification* snp;
- char su_buf[SU2A_MAX_STR_SIZE];
-
- DBG("sctp_rcv_loop: MSG_NOTIFICATION\n");
-
- #define SNOT DBG
- #define ERR_LEN_TOO_SMALL(length, val, bind_addr, from_su, text) \
- if (unlikely((length)<(val))){\
- SNOT("ERROR: sctp notification from %s on %.*s:%d: " \
- text " too short (%d bytes instead of %d bytes)\n", \
- su2a((from_su), sizeof(*(from_su))), \
- (bind_addr)->name.len, (bind_addr)->name.s, \
- (bind_addr)->port_no, (length), (val)); \
- goto error; \
- }
- if (len < sizeof(snp->sn_header)){
- LOG(L_ERR, "ERROR: sctp_handle_notification: invalid length %d "
- "on %.*s:%d, from %s\n",
- len, si->name.len, si->name.s, si->port_no,
- su2a(su, sizeof(*su)));
- goto error;
- }
- snp=(union sctp_notification*) buf;
- switch(snp->sn_header.sn_type){
- case SCTP_REMOTE_ERROR:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_remote_error), si, su,
- "SCTP_REMOTE_ERROR");
- SNOT("sctp notification from %s on %.*s:%d: SCTP_REMOTE_ERROR:"
- " %d, len %d\n, assoc. %d",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no,
- ntohs(snp->sn_remote_error.sre_error),
- ntohs(snp->sn_remote_error.sre_length),
- snp->sn_remote_error.sre_assoc_id
- );
- break;
- case SCTP_SEND_FAILED:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_send_failed), si, su,
- "SCTP_SEND_FAILED");
- SNOT("sctp notification from %s on %.*s:%d: SCTP_SEND_FAILED:"
- " error %d, assoc. %d, flags %x\n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no, snp->sn_send_failed.ssf_error,
- snp->sn_send_failed.ssf_assoc_id,
- snp->sn_send_failed.ssf_flags);
- sctp_handle_send_failed(si, su, buf, len);
- break;
- case SCTP_PEER_ADDR_CHANGE:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_paddr_change), si, su,
- "SCTP_PEER_ADDR_CHANGE");
- strcpy(su_buf, su2a((union sockaddr_union*)
- &snp->sn_paddr_change.spc_aaddr,
- sizeof(snp->sn_paddr_change.spc_aaddr)));
- SNOT("sctp notification from %s on %.*s:%d: SCTP_PEER_ADDR_CHANGE"
- ": %s: %s: assoc. %d \n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no, su_buf,
- sctp_paddr_change_state2s(snp->sn_paddr_change.spc_state),
- snp->sn_paddr_change.spc_assoc_id
- );
- break;
- case SCTP_SHUTDOWN_EVENT:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_shutdown_event), si, su,
- "SCTP_SHUTDOWN_EVENT");
- SNOT("sctp notification from %s on %.*s:%d: SCTP_SHUTDOWN_EVENT:"
- " assoc. %d\n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no, snp->sn_shutdown_event.sse_assoc_id);
- break;
- case SCTP_ASSOC_CHANGE:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_assoc_change), si, su,
- "SCTP_ASSOC_CHANGE");
- SNOT("sctp notification from %s on %.*s:%d: SCTP_ASSOC_CHANGE"
- ": %s: assoc. %d, ostreams %d, istreams %d\n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no,
- sctp_assoc_change_state2s(snp->sn_assoc_change.sac_state),
- snp->sn_assoc_change.sac_assoc_id,
- snp->sn_assoc_change.sac_outbound_streams,
- snp->sn_assoc_change.sac_inbound_streams
- );
- break;
- #ifdef SCTP_ADAPTION_INDICATION
- case SCTP_ADAPTION_INDICATION:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_adaption_event), si, su,
- "SCTP_ADAPTION_INDICATION");
- SNOT("sctp notification from %s on %.*s:%d: "
- "SCTP_ADAPTION_INDICATION \n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no);
- break;
- #endif /* SCTP_ADAPTION_INDICATION */
- case SCTP_PARTIAL_DELIVERY_EVENT:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_pdapi_event), si, su,
- "SCTP_PARTIAL_DELIVERY_EVENT");
- SNOT("sctp notification from %s on %.*s:%d: "
- "SCTP_PARTIAL_DELIVERY_EVENT: %d%s, assoc. %d\n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no, snp->sn_pdapi_event.pdapi_indication,
- (snp->sn_pdapi_event.pdapi_indication==
- SCTP_PARTIAL_DELIVERY_ABORTED)? " PD ABORTED":"",
- snp->sn_pdapi_event.pdapi_assoc_id);
- break;
- #ifdef SCTP_SENDER_DRY_EVENT /* new, not yet supported */
- case SCTP_SENDER_DRY_EVENT:
- ERR_LEN_TOO_SMALL(len, sizeof(struct sctp_sender_dry_event),
- si, su, "SCTP_SENDER_DRY_EVENT");
- SNOT("sctp notification from %s on %.*s:%d: "
- "SCTP_SENDER_DRY_EVENT on %d\n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no, snp->sn_sender_dry_event.sender_dry_assoc_id);
- break;
- #endif /* SCTP_SENDER_DRY_EVENT */
- default:
- SNOT("sctp notification from %s on %.*s:%d: UNKNOWN (%d)\n",
- su2a(su, sizeof(*su)), si->name.len, si->name.s,
- si->port_no, snp->sn_header.sn_type);
- }
- return 0;
- error:
- return -1;
- #undef ERR_LEN_TOO_SMALL
- }
- int sctp_rcv_loop()
- {
- unsigned len;
- static char buf [BUF_SIZE+1];
- char *tmp;
- struct receive_info ri;
- struct sctp_sndrcvinfo* sinfo;
- struct msghdr msg;
- struct iovec iov[1];
- struct cmsghdr* cmsg;
- /* use a larger buffer then needed in case some other ancillary info
- * is enabled */
- char cbuf[CMSG_SPACE(sizeof(*sinfo))+CMSG_SPACE(1024)];
-
- ri.bind_address=bind_address; /* this will not change */
- ri.dst_port=bind_address->port_no;
- ri.dst_ip=bind_address->address;
- ri.proto=PROTO_SCTP;
- ri.proto_reserved1=ri.proto_reserved2=0;
-
- iov[0].iov_base=buf;
- iov[0].iov_len=BUF_SIZE;
- msg.msg_iov=iov;
- msg.msg_iovlen=1;
- msg.msg_flags=0;
-
- /* initialize the config framework */
- if (cfg_child_init()) goto error;
-
- for(;;){
- msg.msg_name=&ri.src_su.s;
- msg.msg_namelen=sockaddru_len(bind_address->su);
- msg.msg_control=cbuf;
- msg.msg_controllen=sizeof(cbuf);
- len=recvmsg(bind_address->socket, &msg, 0);
- /* len=sctp_recvmsg(bind_address->socket, buf, BUF_SIZE, &ri.src_su.s,
- &msg.msg_namelen, &sinfo, &msg.msg_flags); */
- if (len==-1){
- if (errno==EAGAIN){
- DBG("sctp_rcv_loop: EAGAIN on sctp socket\n");
- continue;
- }
- LOG(L_ERR, "ERROR: sctp_rcv_loop: sctp_recvmsg on %d (%p):"
- "[%d] %s\n", bind_address->socket, bind_address,
- errno, strerror(errno));
- if ((errno==EINTR)||(errno==EWOULDBLOCK)|| (errno==ECONNREFUSED))
- continue; /* goto skip;*/
- else goto error;
- }
- if (unlikely(msg.msg_flags & MSG_NOTIFICATION)){
- /* intercept usefull notifications */
- sctp_handle_notification(bind_address, &ri.src_su, buf, len);
- continue;
- }else if (unlikely(!(msg.msg_flags & MSG_EOR))){
- LOG(L_ERR, "ERROR: sctp_rcv_loop: partial delivery not"
- "supported\n");
- continue;
- }
-
- su2ip_addr(&ri.src_ip, &ri.src_su);
- ri.src_port=su_getport(&ri.src_su);
-
- /* get ancillary data */
- for (cmsg=CMSG_FIRSTHDR(&msg); cmsg; cmsg=CMSG_NXTHDR(&msg, cmsg)){
- if (likely((cmsg->cmsg_level==IPPROTO_SCTP) &&
- ((cmsg->cmsg_type==SCTP_SNDRCV)
- #ifdef SCTP_EXT
- || (cmsg->cmsg_type==SCTP_EXTRCV)
- #endif
- ) && (cmsg->cmsg_len>=CMSG_LEN(sizeof(*sinfo)))) ){
- sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
- DBG("sctp recv: message from %s:%d stream %d ppid %x"
- " flags %x%s tsn %u" " cumtsn %u associd %d\n",
- ip_addr2a(&ri.src_ip), htons(ri.src_port),
- sinfo->sinfo_stream, sinfo->sinfo_ppid,
- sinfo->sinfo_flags,
- (sinfo->sinfo_flags&SCTP_UNORDERED)?
- " (SCTP_UNORDERED)":"",
- sinfo->sinfo_tsn, sinfo->sinfo_cumtsn,
- sinfo->sinfo_assoc_id);
- break;
- }
- }
- /* we 0-term the messages for debugging */
- buf[len]=0; /* no need to save the previous char */
- /* sanity checks */
- if (len<MIN_SCTP_PACKET) {
- tmp=ip_addr2a(&ri.src_ip);
- DBG("sctp_rcv_loop: probing packet received from %s %d\n",
- tmp, htons(ri.src_port));
- continue;
- }
- if (ri.src_port==0){
- tmp=ip_addr2a(&ri.src_ip);
- LOG(L_INFO, "sctp_rcv_loop: dropping 0 port packet from %s\n",
- tmp);
- continue;
- }
- #ifdef USE_COMP
- ri.comp=COMP_NONE;
- #endif
- /* update the local config */
- cfg_update();
- receive_msg(buf, len, &ri);
- }
- error:
- return -1;
- }
- /* send buf:len over udp to dst using sndrcv_info (uses only the to and
- * send_sock members from dst)
- * returns the numbers of bytes sent on success (>=0) and -1 on error
- */
- static int sctp_msg_send_raw(struct dest_info* dst, char* buf, unsigned len,
- struct sctp_sndrcvinfo* sndrcv_info)
- {
- int n;
- int tolen;
- struct ip_addr ip; /* used only on error, for debugging */
- struct msghdr msg;
- struct iovec iov[1];
- struct sctp_sndrcvinfo* sinfo;
- struct cmsghdr* cmsg;
- /* make sure msg_control will point to properly aligned data */
- union {
- struct cmsghdr cm;
- char cbuf[CMSG_SPACE(sizeof(*sinfo))];
- }ctrl_un;
-
- tolen=sockaddru_len(dst->to);
- iov[0].iov_base=buf;
- iov[0].iov_len=len;
- msg.msg_iov=iov;
- msg.msg_iovlen=1;
- msg.msg_name=&dst->to.s;
- msg.msg_namelen=tolen;
- msg.msg_flags=0; /* not used on send (use instead sinfo_flags) */
- msg.msg_control=ctrl_un.cbuf;
- msg.msg_controllen=sizeof(ctrl_un.cbuf);
- cmsg=CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level=IPPROTO_SCTP;
- cmsg->cmsg_type=SCTP_SNDRCV;
- cmsg->cmsg_len=CMSG_LEN(sizeof(*sinfo));
- sinfo=(struct sctp_sndrcvinfo*)CMSG_DATA(cmsg);
- *sinfo=*sndrcv_info;
- /* some systems need msg_controllen set to the actual size and not
- * something bigger (e.g. openbsd) */
- msg.msg_controllen=cmsg->cmsg_len;
- again:
- n=sendmsg(dst->send_sock->socket, &msg, MSG_DONTWAIT);
- #if 0
- n=sctp_sendmsg(dst->send_sock->socket, buf, len, &dst->to.s, tolen,
- 0 /* ppid */, SCTP_UNORDERED /* | SCTP_EOR */ /* flags */,
- 0 /* stream */, sctp_options.sctp_send_ttl /* ttl */,
- 0 /* context */);
- #endif
- if (n==-1){
- su2ip_addr(&ip, &dst->to);
- LOG(L_ERR, "ERROR: sctp_msg_send: sendmsg(sock,%p,%d,0,%s:%d,%d):"
- " %s(%d)\n", buf, len, ip_addr2a(&ip), su_getport(&dst->to),
- tolen, strerror(errno),errno);
- if (errno==EINTR) goto again;
- if (errno==EINVAL) {
- LOG(L_CRIT,"CRITICAL: invalid sendmsg parameters\n"
- "one possible reason is the server is bound to localhost and\n"
- "attempts to send to the net\n");
- }else if (errno==EAGAIN || errno==EWOULDBLOCK){
- LOG(L_ERR, "ERROR: sctp_msg_send: failed to send, send buffers"
- " full\n");
- }
- }
- return n;
- }
- /* wrapper around sctp_msg_send_raw():
- * send buf:len over udp to dst (uses only the to and send_sock members
- * from dst)
- * returns the numbers of bytes sent on success (>=0) and -1 on error
- */
- int sctp_msg_send(struct dest_info* dst, char* buf, unsigned len)
- {
- struct sctp_sndrcvinfo sinfo;
-
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.sinfo_flags=SCTP_UNORDERED;
- #ifdef HAVE_SCTP_SNDRCVINFO_PR_POLICY
- if (sctp_options.sctp_send_ttl){
- sinfo.sinfo_pr_policy=SCTP_PR_SCTP_TTL;
- sinfo.sinfo_pr_value=sctp_options.sctp_send_ttl;
- }else
- sinfo->sinfo_pr_policy=SCTP_PR_SCTP_NONE;
- #else
- sinfo.sinfo_timetolive=sctp_options.sctp_send_ttl;
- #endif
- sinfo.sinfo_context=sctp_options.sctp_send_retries;
- return sctp_msg_send_raw(dst, buf, len, &sinfo);
- }
- void destroy_sctp()
- {
- }
- #endif /* USE_SCTP */
|