Mercurial > hg > waaad
view waaad/peer-sctp.c @ 338:be43a4344cf9
sctp_adaptation_layer_event not supported on some platform, and this code was not needed anyway. Reported by Chris Brody.
author | Sebastien Decugis <sdecugis@nict.go.jp> |
---|---|
date | Wed, 01 Apr 2009 11:17:11 +0900 |
parents | 436975f6ef4d |
children | e86dba02630a |
line wrap: on
line source
/********************************************************************************************************* * Software License Agreement (BSD License) * * Author: Sebastien Decugis <sdecugis@nict.go.jp> * * * * Copyright (c) 2008, WIDE Project and NICT * * All rights reserved. * * * * Redistribution and use of this software in source and binary forms, with or without modification, are * * permitted provided that the following conditions are met: * * * * * Redistributions of source code must retain the above * * copyright notice, this list of conditions and the * * following disclaimer. * * * * * Redistributions in binary form must reproduce the above * * copyright notice, this list of conditions and the * * following disclaimer in the documentation and/or other * * materials provided with the distribution. * * * * * Neither the name of the WIDE Project or NICT nor the * * names of its contributors may be used to endorse or * * promote products derived from this software without * * specific prior written permission of WIDE Project and * * NICT. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *********************************************************************************************************/ /* Peers facility. * * This file contains the code to handle SCTP connections to a peer. * */ #include "waaad-internal.h" #include "peer-internal.h" #include <netinet/sctp.h> #include <sys/uio.h> #include <unistd.h> /* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */ #define CMSG_BUF_LEN 1024 /*******************************************************************************************************/ /* local functions */ static int _sctp_setsockopt_pre(int sk, uint16_t nstreams) { #ifdef DEBUG_SCTP socklen_t sz; #endif /* DEBUG_SCTP */ TRACE_ENTRY( "%d %hd", sk, nstreams); CHECK_PARAMS( (sk > 0) && nstreams ); /* Subscribe to some notifications */ { struct sctp_event_subscribe event; memset(&event, 0, sizeof(event)); event.sctp_data_io_event = 1; /* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */ event.sctp_association_event = 0; /* new or closed associations (mostly for one-to-many style sockets) */ event.sctp_address_event = 1; /* address changes */ event.sctp_send_failure_event = 1; /* delivery failures */ event.sctp_peer_error_event = 1; /* remote peer sends an error */ event.sctp_shutdown_event = 1; /* peer has sent a SHUTDOWN */ event.sctp_partial_delivery_event = 1; /* a partial delivery is aborted, probably indicating the connection is being shutdown */ // event.sctp_adaptation_layer_event = 0; /* adaptation layer notifications */ // event.sctp_authentication_event = 0; /* when new key is made active */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) ); #ifdef DEBUG_SCTP #if 0 sz = sizeof(event); CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) ); if (sz != sizeof(event)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event)); return ENOTSUP; } #endif /* 0 */ TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_data_io_event : %hhu", event.sctp_data_io_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_association_event : %hhu", event.sctp_association_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_address_event : %hhu", event.sctp_address_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_send_failure_event : %hhu", event.sctp_send_failure_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_peer_error_event : %hhu", event.sctp_peer_error_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_shutdown_event : %hhu", event.sctp_shutdown_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event); TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event); // TRACE_DEBUG(FULL, "SCTP_EVENTS sctp_authentication_event : %hhu", event.sctp_authentication_event); #endif /* DEBUG_SCTP */ } /* Set the INIT parameters, such as number of streams */ { struct sctp_initmsg init; memset(&init, 0, sizeof(init)); #ifdef DEBUG_SCTP sz = sizeof(init); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz) ); if (sz != sizeof(init)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_num_ostreams : %hu", init.sinit_num_ostreams); TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_instreams : %hu", init.sinit_max_instreams); TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_attempts : %hu", init.sinit_max_attempts); TRACE_DEBUG(FULL, "Def SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo); #endif /* DEBUG_SCTP */ /* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters */ init.sinit_num_ostreams = nstreams; /* desired number of outgoing streams */ init.sinit_max_init_timeo = CNX_TIMEOUT * 1000; /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz) ); TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_num_ostreams : %hu", init.sinit_num_ostreams); TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_instreams : %hu", init.sinit_max_instreams); TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_attempts : %hu", init.sinit_max_attempts); TRACE_DEBUG(FULL, "New SCTP_INITMSG sinit_max_init_timeo : %hu", init.sinit_max_init_timeo); #endif /* DEBUG_SCTP */ } /* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */ #ifdef SCTP_DISABLE_FRAGMENTS { int nofrag; #ifdef DEBUG_SCTP sz = sizeof(nofrag); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz) ); if (sz != sizeof(nofrag)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false"); #endif /* DEBUG_SCTP */ nofrag = 0; /* We turn ON the fragmentation */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz) ); TRACE_DEBUG(FULL, "New SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false"); #endif /* DEBUG_SCTP */ } #else /* SCTP_DISABLE_FRAGMENTS */ # error "TLS requires support of SCTP_DISABLE_FRAGMENTS" #endif /* SCTP_DISABLE_FRAGMENTS */ /* Set the RETRANSMIT parameters */ #ifdef SCTP_RTOINFO { struct sctp_rtoinfo rtoinfo; memset(&rtoinfo, 0, sizeof(rtoinfo)); #ifdef DEBUG_SCTP sz = sizeof(rtoinfo); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz) ); if (sz != sizeof(rtoinfo)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial); TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_max : %u", rtoinfo.srto_max); TRACE_DEBUG(FULL, "Def SCTP_RTOINFO srto_min : %u", rtoinfo.srto_min); #endif /* DEBUG_SCTP */ rtoinfo.srto_max = g_pconf->twtimer * 500; /* Maximum retransmit timer (in ms) (set to Tw / 2) */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz) ); TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_initial : %u", rtoinfo.srto_initial); TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_max : %u", rtoinfo.srto_max); TRACE_DEBUG(FULL, "New SCTP_RTOINFO srto_min : %u", rtoinfo.srto_min); #endif /* DEBUG_SCTP */ } #else /* SCTP_RTOINFO */ TRACE_DEBUG(FULL, "Skipping SCTP_RTOINFO"); #endif /* SCTP_RTOINFO */ /* Set the ASSOCIATION parameters */ #ifdef SCTP_ASSOCINFO { struct sctp_assocparams assoc; memset(&assoc, 0, sizeof(assoc)); #ifdef DEBUG_SCTP sz = sizeof(assoc); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz) ); if (sz != sizeof(assoc)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_asocmaxrxt : %hu", assoc.sasoc_asocmaxrxt); TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations); TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_peer_rwnd : %u" , assoc.sasoc_peer_rwnd); TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_local_rwnd : %u" , assoc.sasoc_local_rwnd); TRACE_DEBUG(FULL, "Def SCTP_ASSOCINFO sasoc_cookie_life : %u" , assoc.sasoc_cookie_life); #endif /* DEBUG_SCTP */ assoc.sasoc_asocmaxrxt = 5; /* Maximum retransmission attempts: we want fast detection of errors */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz) ); TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_asocmaxrxt : %hu", assoc.sasoc_asocmaxrxt); TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations); TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_peer_rwnd : %u" , assoc.sasoc_peer_rwnd); TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_local_rwnd : %u" , assoc.sasoc_local_rwnd); TRACE_DEBUG(FULL, "New SCTP_ASSOCINFO sasoc_cookie_life : %u" , assoc.sasoc_cookie_life); #endif /* DEBUG_SCTP */ } #else /* SCTP_ASSOCINFO */ TRACE_DEBUG(FULL, "Skipping SCTP_ASSOCINFO"); #endif /* SCTP_ASSOCINFO */ /* The SO_LINGER option will be re-set if we want to perform SCTP ABORT */ #ifdef SO_LINGER { struct linger linger; memset(&linger, 0, sizeof(linger)); #ifdef DEBUG_SCTP sz = sizeof(linger); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz) ); if (sz != sizeof(linger)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SO_LINGER l_onoff : %d", linger.l_onoff); TRACE_DEBUG(FULL, "Def SO_LINGER l_linger : %d", linger.l_linger); #endif /* DEBUG_SCTP */ linger.l_onoff = 0; /* Do not activate the linger */ linger.l_linger = 0; /* Return immediately when closing (=> abort) */ /* Set the option */ CHECK_SYS( setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz) ); TRACE_DEBUG(FULL, "New SO_LINGER l_onoff : %d", linger.l_onoff); TRACE_DEBUG(FULL, "New SO_LINGER l_linger : %d", linger.l_linger); #endif /* DEBUG_SCTP */ } #else /* SO_LINGER */ TRACE_DEBUG(FULL, "Skipping SO_LINGER"); #endif /* SO_LINGER */ /* Set the NODELAY option (Nagle-like algorithm) */ #ifdef SCTP_NODELAY { int nodelay; #ifdef DEBUG_SCTP sz = sizeof(nodelay); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz) ); if (sz != sizeof(nodelay)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_NODELAY value : %s", nodelay ? "true" : "false"); #endif /* DEBUG_SCTP */ nodelay = 0; /* We turn ON the Nagle algorithm (probably the default already) */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz) ); TRACE_DEBUG(FULL, "New SCTP_NODELAY value : %s", nodelay ? "true" : "false"); #endif /* DEBUG_SCTP */ } #else /* SCTP_NODELAY */ TRACE_DEBUG(FULL, "Skipping SCTP_NODELAY"); #endif /* SCTP_NODELAY */ /* Set the interleaving option */ #ifdef SCTP_FRAGMENT_INTERLEAVE { int interleave; #ifdef DEBUG_SCTP sz = sizeof(interleave); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz) ); if (sz != sizeof(interleave)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_FRAGMENT_INTERLEAVE value : %d", interleave); #endif /* DEBUG_SCTP */ #if 0 interleave = 2; /* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */ #else /* 0 */ interleave = 1; /* hmmm actually, we are no more capable of handling this... */ #endif /* 0 */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz) ); TRACE_DEBUG(FULL, "New SCTP_FRAGMENT_INTERLEAVE value : %d", interleave); #endif /* DEBUG_SCTP */ } #else /* SCTP_FRAGMENT_INTERLEAVE */ TRACE_DEBUG(FULL, "Skipping SCTP_FRAGMENT_INTERLEAVE"); #endif /* SCTP_FRAGMENT_INTERLEAVE */ /* Set the v4 mapped addresses option */ #ifdef SCTP_I_WANT_MAPPED_V4_ADDR { int v4mapped; #ifdef DEBUG_SCTP sz = sizeof(v4mapped); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz) ); if (sz != sizeof(v4mapped)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false"); #endif /* DEBUG_SCTP */ v4mapped = 0; /* We don't want v4 mapped addresses */ v4mapped = 1; /* but we have to, otherwise the bind fails in linux currently ... (Ok, It'd be better to replace with an autoconf test!) */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz) ); TRACE_DEBUG(FULL, "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false"); #endif /* DEBUG_SCTP */ } #else /* SCTP_I_WANT_MAPPED_V4_ADDR */ TRACE_DEBUG(FULL, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR"); #endif /* SCTP_I_WANT_MAPPED_V4_ADDR */ /* Other settable options (draft-ietf-tsvwg-sctpsocket-17): SO_RCVBUF size of receiver window SO_SNDBUF size of pending data to send SCTP_AUTOCLOSE for one-to-many only SCTP_SET_PEER_PRIMARY_ADDR ask remote peer to use this local address as primary SCTP_PRIMARY_ADDR use this address as primary locally SCTP_ADAPTATION_LAYER set adaptation layer indication SCTP_PEER_ADDR_PARAMS control heartbeat per peer address SCTP_DEFAULT_SEND_PARAM parameters for the sendto() call SCTP_MAXSEG max size of fragmented segments -- bound to PMTU SCTP_AUTH_CHUNK request authentication of some type of chunk SCTP_HMAC_IDENT authentication algorithms SCTP_AUTH_KEY set a shared key SCTP_AUTH_ACTIVE_KEY set the active key SCTP_AUTH_DELETE_KEY remove a key SCTP_AUTH_DEACTIVATE_KEY will not use that key anymore SCTP_DELAYED_SACK control delayed acks SCTP_PARTIAL_DELIVERY_POINT control partial delivery size SCTP_USE_EXT_RCVINFO use extended receive info structure (information about the next message if available) SCTP_MAX_BURST number of packets that can be burst emitted SCTP_CONTEXT save a context information along with the association. SCTP_EXPLICIT_EOR enable sending one message across several send calls SCTP_REUSE_PORT share one listening port with several sockets read-only options: SCTP_STATUS retrieve info such as number of streams, pending packets, state, ... SCTP_GET_PEER_ADDR_INFO get information about a specific peer address of the association. SCTP_PEER_AUTH_CHUNKS list of chunks the remote peer wants authenticated SCTP_LOCAL_AUTH_CHUNKS list of chunks the local peer wants authenticated SCTP_GET_ASSOC_NUMBER number of associations in a one-to-many socket SCTP_GET_ASSOC_ID_LIST list of these associations */ return 0; } static int _sctp_setsockopt_post(int sk, int bound_all) { #ifdef DEBUG_SCTP socklen_t sz; #endif /* DEBUG_SCTP */ TRACE_ENTRY( "%d", sk); CHECK_PARAMS( (sk > 0) ); /* Set the ASCONF option */ #ifdef SCTP_AUTO_ASCONF if (bound_all) { int asconf; #ifdef DEBUG_SCTP sz = sizeof(asconf); /* Read socket defaults */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz) ); if (sz != sizeof(asconf)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf)); return ENOTSUP; } TRACE_DEBUG(FULL, "Def SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false"); #endif /* DEBUG_SCTP */ asconf = 1; /* allow automatic use of added or removed addresses in the association (for bound-all sockets) */ /* Set the option to the socket */ CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf)) ); #ifdef DEBUG_SCTP /* Check new values */ CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz) ); TRACE_DEBUG(FULL, "New SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false"); #endif /* DEBUG_SCTP */ } #else /* SCTP_AUTO_ASCONF */ TRACE_DEBUG(FULL, "Skipping SCTP_AUTO_ASCONF"); #endif /* SCTP_AUTO_ASCONF */ return 0; } /* Read the list of bound addresses from a socket and save it to g_pconf->local_addr_sec_sa. Note that the local addresses (127.0.0.1, ::1) are not saved. */ static int _sctp_getboundaddrs(int sk) { int cnt, i = 0; sSA * list = NULL; sSA * sa; sSS * ss; ASSERT(g_pconf->local_addr_sec_sa == NULL); /* retrieve the addresses */ CHECK_SYS( cnt = sctp_getladdrs(sk, 0, &list) ); if (cnt == 0) { TRACE_DEBUG(INFO, "Invalid parameter: socket was not bound"); return EINVAL; } /* Now allocate an array of sSS to save the list */ CHECK_MALLOC( g_pconf->local_addr_sec_sa = calloc(cnt + 1, sizeof(sSS)) ); /* and finally copy the SA information -- we filter out the local addresses */ ss = g_pconf->local_addr_sec_sa; sa = list; while ( i < cnt ) { switch ( sa->sa_family ) { case AF_INET: { sSA4 * sin = (sSA4 *)sa; if (ntohl(sin->sin_addr.s_addr) != INADDR_LOOPBACK) { ((sSA4 *)ss)->sin_family = AF_INET; ((sSA4 *)ss)->sin_addr.s_addr = sin->sin_addr.s_addr; ss++; } /* update the loop pointers */ sa = (sSA *)(sin + 1); i++; } break; case AF_INET6: { sSA6 * sin6 = (sSA6 *)sa; /* Special case: mapped addresses -> unmap it. */ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { in_addr_t v4m = IN6_ADDR_V4UNMAP( &sin6->sin6_addr ); if (ntohl(v4m) != INADDR_LOOPBACK) { ((sSA4 *)ss)->sin_family = AF_INET; ((sSA4 *)ss)->sin_addr.s_addr = v4m; ss++; } } else { /* maybe we should also remove link-local addresses??? */ if ( ! IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) ) { ((sSA6 *)ss)->sin6_family = AF_INET6; memcpy(&((sSA6 *)ss)->sin6_addr, &sin6->sin6_addr, 16); ss++; } } /* update the loop pointers */ sa = (sSA *)(sin6 + 1); i++; } break; default: TRACE_DEBUG(INFO, "Unexpected AF returned by sctp_getladdrs: %d", sa->sa_family); return ENOTSUP; } } /* And now, free the list and return */ sctp_freeladdrs(list); return 0; } /*******************************************************************************************************/ /* Exported functions */ /* * FUNCTION: _peer_sctp_create_server * * PARAMETERS: * sock : (out) On return, the identifier of the bound socket is stored here. * * DESCRIPTION: * Creates a listening SCTP bound socket. * It uses the data from configuration (local_addr_pri_sa, ...) to configure the server. * * RETURN VALUE: * 0 : The socket has been created, bound, and is listening for connections. * !0 : an error occurred (standard errno are returned, such as ENOTSUP) */ int _peer_sctp_create_server( int * sock ) { int af; int sk; int bound_all = 0; sSS * ss; TRACE_ENTRY("%p", sock); CHECK_PARAMS( sock ); if (g_pconf->disable_inet6) af = AF_INET; else af = AF_INET6; /* Create the socket */ CHECK_SYS( sk = socket(af, SOCK_STREAM, IPPROTO_SCTP) ); /* Set the number of streams and other common options to the socket */ CHECK_FCT( _sctp_setsockopt_pre(sk, g_pconf->sctp_streams) ); /* Now bind to all the requested addresses if specified, or default set otherwise */ if ((g_pconf->local_addr_pri_sa == NULL) && (g_pconf->local_addr_sec_sa == NULL)) { /* No address passed in configuration file, use a default set then save it */ sSA4 sin; TRACE_DEBUG(FULL, "Binding server socket to default system addresses"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(g_pconf->local_port); sin.sin_addr.s_addr = INADDR_ANY; CHECK_SYS( bind(sk, (sSA *)&sin, sizeof(sin)) ); /* And now save the list of addresses to which we are bound */ bound_all = 1; CHECK_FCT( _sctp_getboundaddrs(sk) ); } else { /* we have the list of addresses to bind to, use sctp_bindx */ int cnt = 0; TRACE_DEBUG(FULL, "Binding server socket to addresses specified in the configuration"); /* first count the addresses */ ss = g_pconf->local_addr_pri_sa; if (ss != NULL) { while (ss->ss_family != 0) { if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6)) cnt++; ss++; } } ss = g_pconf->local_addr_sec_sa; if (ss != NULL) { while (ss->ss_family != 0) { if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6)) cnt++; ss++; } } TRACE_DEBUG(FULL, "Counted %d addresses", cnt); /* Some bugs are remaining bellow this point, probably in SCTP implementation: [DBG] ../../waaad/peer-sctp.c:874 ( _peer_sctp_create_server): Calling sctp_bindx with the following array: [DBG] ../../waaad/peer-sctp.c:875 ( _peer_sctp_create_server): IPv6: ::ffff:133.243.146.201 [DBG] ../../waaad/peer-sctp.c:875 ( _peer_sctp_create_server): IPv6: ::ffff:10.0.0.1 [DBG] ../../waaad/peer-sctp.c:901 ( _peer_sctp_create_server): SCTP server bound on IPv6: ::ffff:133.243.146.201 [DBG] ../../waaad/peer-sctp.c:901 ( _peer_sctp_create_server): SCTP server bound on IPv6: ::ffff:0.0.0.0 */ /* Now allocate the array for the function and fill with the addresses */ if (g_pconf->disable_inet6) { sSA4 * sar; int recnt = 0; CHECK_MALLOC( sar = calloc(cnt, sizeof(sSA4)) ); ss = g_pconf->local_addr_pri_sa; if (ss != NULL) { while (ss->ss_family != 0) { if (ss->ss_family == AF_INET) { sar[recnt].sin_family = AF_INET; sar[recnt].sin_port = htons(g_pconf->local_port); sar[recnt].sin_addr.s_addr = ((sSA4 *)ss)->sin_addr.s_addr; recnt++; } ss++; } } ss = g_pconf->local_addr_sec_sa; if (ss != NULL) { while (ss->ss_family != 0) { if (ss->ss_family == AF_INET) { sar[recnt].sin_family = AF_INET; sar[recnt].sin_port = htons(g_pconf->local_port); sar[recnt].sin_addr.s_addr = ((sSA4 *)ss)->sin_addr.s_addr; recnt++; } ss++; } } TRACE_DEBUG(FULL, "Calling sctp_bindx with the following array:"); sSA_DUMP_ARRAY(FULL, " ", sar, recnt); CHECK_SYS( sctp_bindx(sk, (sSA *)sar, recnt, SCTP_BINDX_ADD_ADDR) ); } else { sSA6 * sar; int recnt = 0; CHECK_MALLOC( sar = calloc(cnt, sizeof(sSA6)) ); ss = g_pconf->local_addr_pri_sa; if (ss != NULL) { while (ss->ss_family != 0) { if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6)) { sar[recnt].sin6_family = AF_INET6; sar[recnt].sin6_port = htons(g_pconf->local_port); if (ss->ss_family == AF_INET) { /* map the address to IPv6 */ IN6_ADDR_V4MAP( &sar[recnt].sin6_addr.s6_addr, ((sSA4 *)ss)->sin_addr.s_addr ); } else { /* directly copy the address */ memcpy(&sar[recnt].sin6_addr.s6_addr, &(((sSA6 *)ss)->sin6_addr.s6_addr), 16); } recnt++; } ss++; } } ss = g_pconf->local_addr_sec_sa; if (ss != NULL) { while (ss->ss_family != 0) { if ((ss->ss_family == AF_INET) || (ss->ss_family == AF_INET6)) { sar[recnt].sin6_family = AF_INET6; sar[recnt].sin6_port = htons(g_pconf->local_port); if (ss->ss_family == AF_INET) { /* map the address to IPv6 */ IN6_ADDR_V4MAP( &sar[recnt].sin6_addr.s6_addr, ((sSA4 *)ss)->sin_addr.s_addr ); } else { /* directly copy the address */ memcpy(&sar[recnt].sin6_addr.s6_addr, &(((sSA6 *)ss)->sin6_addr.s6_addr), 16); } recnt++; } ss++; } } TRACE_DEBUG(FULL, "Calling sctp_bindx with the following array:"); sSA_DUMP_ARRAY(FULL, " ", sar, recnt); CHECK_SYS( sctp_bindx(sk, (sSA *)sar, recnt, SCTP_BINDX_ADD_ADDR) ); } } /* Ok, the server is bound */ #ifdef DEBUG_SCTP /* Debug: show all local listening addresses */ { sSA *sa; int sz; CHECK_SYS( sz = sctp_getladdrs(sk, 0, &sa) ); sSA_DUMP_ARRAY(FULL, "SCTP server bound on ", sa, sz); sctp_freeladdrs(sa); /* Now dump the primary and secondary lists from the config */ sSS_DUMP_ARRAY(FULL, "Dump of g_pconf->local_addr_pri_sa: ", g_pconf->local_addr_pri_sa); sSS_DUMP_ARRAY(FULL, "Dump of g_pconf->local_addr_sec_sa: ", g_pconf->local_addr_sec_sa); TRACE_DEBUG(FULL, "SCTP socket created: %d", sk); } #endif /* DEBUG_SCTP */ CHECK_FCT( _sctp_setsockopt_post(sk, bound_all) ); *sock = sk; return 0; } /* * FUNCTION: _peer_sctp_accept * * PARAMETERS: * sock : The listening server socket from which to accept clients. * new_sd : on return, the new socket for this client. * ostreams: the number of outgoing streams. * istreams: the number of incoming streams. * * DESCRIPTION: * Accepts an incoming connection and get the number of bi-lateral streams. * * RETURN VALUE: * 0 : A new client has arrived. * !0 : an error occurred (standard errno are returned) */ int _peer_sctp_accept( int sock, int * new_sd, uint16_t *ostreams, uint16_t *istreams ) { int skcli; struct sctp_status status; socklen_t sz = sizeof(status); TRACE_ENTRY("%d %p %p %p", sock, new_sd, ostreams, istreams); /* Check params */ CHECK_PARAMS( new_sd && ostreams && istreams ); /* Now wait for a client connection */ TRACE_DEBUG(FULL, "Waiting for client connection..."); CHECK_SYS( skcli = accept(sock, NULL, NULL) ); /* We now have a client connected, get some information... */ /* Read the association parameters */ memset(&status, 0, sizeof(status)); CHECK_SYS( getsockopt(skcli, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) ); if (sz != sizeof(status)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(status)); return ENOTSUP; } #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_state : %i" , status.sstat_state); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_rwnd : %u" , status.sstat_rwnd); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_unackdata : %hu", status.sstat_unackdata); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_penddata : %hu", status.sstat_penddata); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_instrms : %hu", status.sstat_instrms); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_outstrms : %hu", status.sstat_outstrms); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_fragmentation_point : %u" , status.sstat_fragmentation_point); sSA_DUMP( FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_address : ", &status.sstat_primary.spinfo_address ); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_state : %d" , status.sstat_primary.spinfo_state); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_cwnd : %u" , status.sstat_primary.spinfo_cwnd); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_srtt : %u" , status.sstat_primary.spinfo_srtt); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_rto : %u" , status.sstat_primary.spinfo_rto); TRACE_DEBUG(FULL, "Accepted Cli SCTP_STATUS sstat_primary.spinfo_mtu : %u" , status.sstat_primary.spinfo_mtu); #endif /* DEBUG_SCTP */ *ostreams = status.sstat_outstrms; *istreams = status.sstat_instrms; *new_sd = skcli; return 0; } /* * FUNCTION: _peer_sctp_connect * * PARAMETERS: * addr : primary address of the server, that is passed to connect() function. * addrlen : length of addr structure. * sock : (out) The client socket is stored here on return. * ostreams: (in/out) the number of outbound streams (desired / negociated). * istreams: (out) the number of inbound streams. * * DESCRIPTION: * Try and connect to a server and retrieve the number of streams in each direction. * * RETURN VALUE: * 0 : The client is connected. * !0 : an error occurred (standard errno are returned) */ int _peer_sctp_connect( const sSA *addr, socklen_t addrlen, int *sock, uint16_t *ostreams, uint16_t *istreams ) { int af; int sk; struct sctp_status status; socklen_t sz = sizeof(status); TRACE_ENTRY("%p %d %p %p %p", addr, addrlen, sock, ostreams, istreams); /* Check params */ CHECK_PARAMS( sock && addr && ostreams && istreams ); if (g_pconf->disable_inet6) af = AF_INET; else af = AF_INET6; /* Create the socket */ CHECK_SYS( sk = socket(af, SOCK_STREAM, IPPROTO_SCTP) ); /* Set the number of streams and other common options to the socket */ CHECK_FCT( _sctp_setsockopt_pre(sk, *ostreams) ); sSA_DUMP( FULL, "Connecting to SCTP server: ", addr ); CHECK_SYS( connect(sk, addr, addrlen) ); /* Now that we are connected, retrieve the number of streams */ CHECK_FCT( _sctp_setsockopt_post(sk, 1) ); /* Read the association parameters */ memset(&status, 0, sizeof(status)); CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) ); if (sz != sizeof(status)) { TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, sizeof(status)); return ENOTSUP; } #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_state : %i" , status.sstat_state); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_rwnd : %u" , status.sstat_rwnd); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_unackdata : %hu", status.sstat_unackdata); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_penddata : %hu", status.sstat_penddata); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_instrms : %hu", status.sstat_instrms); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_outstrms : %hu", status.sstat_outstrms); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_fragmentation_point : %u" , status.sstat_fragmentation_point); sSA_DUMP( FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_address : ", &status.sstat_primary.spinfo_address ); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_state : %d" , status.sstat_primary.spinfo_state); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_cwnd : %u" , status.sstat_primary.spinfo_cwnd); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_srtt : %u" , status.sstat_primary.spinfo_srtt); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_rto : %u" , status.sstat_primary.spinfo_rto); TRACE_DEBUG(FULL, "Connected server SCTP_STATUS sstat_primary.spinfo_mtu : %u" , status.sstat_primary.spinfo_mtu); #endif /* DEBUG_SCTP */ *ostreams = status.sstat_outstrms; *istreams = status.sstat_instrms; *sock = sk; return 0; } /* * FUNCTION: _peer_sctp_send * * PARAMETERS: * peer : The peer to which to send a message. * streamid: The stream on which the message must be sent. * data : pointer to the data that must be sent. * len : length of the data buffer. * * DESCRIPTION: * Send some data over an SCTP association on a given stream. * The return values and error codes are the same as sendmsg(). * If streamid is '-1' on function entry, a valid stream is picked * and set on function return. This can be use to automatically rotate * the streams on which messages are sent. * * RETURN VALUE: * -1 : error (errno is set) * 0 : the socket has been close. * >0 : number of bytes sent. */ ssize_t _peer_sctp_send(sec_session_t * sess, int *streamid, const void * data, size_t len) { struct msghdr mhdr; struct iovec iov; struct { struct cmsghdr hdr; struct sctp_sndrcvinfo sndrcv; } anci; _peer_t *peer; TRACE_ENTRY("%p %p(%d) %p %d", sess, streamid, streamid?*streamid:0, data, len); /* Check parameters */ CHECK_PARAMS_DO( sess && VALIDATE_PEER(sess->peer) && streamid && data && len, { errno = EINVAL; return -1; } ); peer = _P(sess->peer); /* We do not check the peer state now, the sending will fail anyway if connection is closed or invalid... */ if ((*streamid < 0) || (*streamid >= peer->p_ostr)) { if (peer->p_ostr > 1) /* we don't lock, only this thread is supposed to modify this value... and it's not a problem otherwise. */ peer->p_curstr = (peer->p_curstr + 1) % peer->p_ostr; *streamid = (int) peer->p_curstr; } memset(&mhdr, 0, sizeof(mhdr)); memset(&iov, 0, sizeof(iov)); memset(&anci, 0, sizeof(anci)); /* IO Vector: message data */ iov.iov_base = (unsigned char *)data; iov.iov_len = len; /* Anciliary data: specify SCTP stream */ anci.hdr.cmsg_len = sizeof(anci); anci.hdr.cmsg_level = IPPROTO_SCTP; anci.hdr.cmsg_type = SCTP_SNDRCV; anci.sndrcv.sinfo_stream = (uint16_t) *streamid; /* note : we can store some data also in .sinfo_ppid (for remote peer) and .sinfo_context (for errors) */ /* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */ mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = &anci; mhdr.msg_controllen = sizeof(anci); #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Sending SCTP packet (%d bytes on stream %d) to '%s'", len, *streamid, peer->p_diamid ? peer->p_diamid : "(unknown)"); #endif /* DEBUG_SCTP */ return sendmsg(*(int*)(sess->conn), &mhdr, 0); } /* * FUNCTION: _peer_sctp_recv * * PARAMETERS: * peer : The peer from which to receive the next message. * streamid: the stream id on which data is received. * data : a buffer containing the received data. * len : the size of the data buffer * * DESCRIPTION: * Receive data and notifications on a SCTP socket. * When notifications or errors are received, the corresponding event is sent to the peer. * The data buffer is allocated and must be freed after use. * The return values and error codes are the same as recvmsg(). * * RETURN VALUE: * -1 : an error occurred * 0 : shutdown is in progress. * >0 : length of data written in the data buffer ( also is *len ). */ ssize_t _peer_sctp_recv(sec_session_t * sess, uint16_t * streamid, void ** data, size_t *len) { ssize_t ret = 0; int iret = 0; struct msghdr mhdr; char ancidata[ CMSG_BUF_LEN ]; /* Prepare to receive lot of anciliary data ... this probably may be shrinked */ struct cmsghdr *hdr; struct sctp_sndrcvinfo *sndrcv; struct iovec iov; size_t sz = sysconf(_SC_PAGESIZE); _peer_t *peer; TRACE_ENTRY("%p %p %p %p", sess, streamid, data, len); /* Check parameters */ CHECK_PARAMS_DO( sess && VALIDATE_PEER(sess->peer) && streamid && data && len, { errno = EINVAL; return -1; } ); peer = _P(sess->peer); /* Initialize our values */ *streamid = -1; /* stream on which the data is received */ *data = NULL; /* pointer to the received data */ *len = 0; /* amount of data already received */ /* Allocate initial buffer; we use one page which should be enough in almost all cases -- and not waste too much memory. */ CHECK_MALLOC_DO( *data = malloc(sz), { errno = ENOMEM; return -1; } ); /* We free this buffer if we are cancelled during this call */ pthread_cleanup_push( cleanup_buffer_i, data ); nextmsg: memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = &ancidata; mhdr.msg_controllen = sizeof(ancidata); /* We will loop while all data is not received. */ incomplete: /* the new data will be received following the preceding */ memset(&iov, 0, sizeof(iov)); iov.iov_base = ((unsigned char *)*data) + *len ; iov.iov_len = sz - *len; ret = recvmsg(*(int*)(sess->conn), &mhdr, 0); if (ret == 0) { /* Socket is closed */ TRACE_DEBUG(INFO, "recvmsg returned %d, errno: %s", ret, strerror(errno)); CHECK_FCT_DO( _peer_events_send(peer, PEVENT_DISCONNECTED, NULL), /* nothing */ ); free(*data); goto out; } if (ret < 0) { TRACE_DEBUG(INFO, "recvmsg returned %d, errno: %s", ret, strerror(errno)); free(*data); goto out; } /* we have received new data */ *len += ret; ret = -1; /* loop if we have not received a full message yet */ if ( (mhdr.msg_flags & MSG_EOR) == 0 ) { /* if buffer is full */ if (*len == sz) { /* add another memory page */ sz += sysconf(_SC_PAGESIZE); /* enlarge the buffer to receive more data */ CHECK_MALLOC_DO( *data = realloc(*data, sz), goto out ); } goto incomplete; } /* Now we have a complete message in *data */ /* Handle the case where we received a notification */ if (mhdr.msg_flags & MSG_NOTIFICATION) { union sctp_notification * notif = (union sctp_notification *) *data; switch (notif->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification"); TRACE_DEBUG(FULL, " state : %hu", notif->sn_assoc_change.sac_state); TRACE_DEBUG(FULL, " error : %hu", notif->sn_assoc_change.sac_error); TRACE_DEBUG(FULL, " instr : %hu", notif->sn_assoc_change.sac_inbound_streams); TRACE_DEBUG(FULL, " outstr : %hu", notif->sn_assoc_change.sac_outbound_streams); #endif /* DEBUG_SCTP */ CHECK_FCT_DO( _peer_events_send(peer, PEVENT_ASSOC_CHG, NULL), /* continue, unless this is a real problem... */ ); break; case SCTP_PEER_ADDR_CHANGE: #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification"); sSA_DUMP( FULL, " intf_change : ", &(notif->sn_paddr_change.spc_aaddr) ); TRACE_DEBUG(FULL, " state : %d", notif->sn_paddr_change.spc_state); TRACE_DEBUG(FULL, " error : %d", notif->sn_paddr_change.spc_error); #endif /* DEBUG_SCTP */ CHECK_FCT_DO( _peer_events_send(peer, PEVENT_PEERADDR_CHG, NULL), /* continue, unless this is a real problem... */ ); break; case SCTP_SEND_FAILED: #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification"); TRACE_DEBUG(FULL, " len : %hu", notif->sn_send_failed.ssf_length); TRACE_DEBUG(FULL, " err : %d", notif->sn_send_failed.ssf_error); #endif /* DEBUG_SCTP */ CHECK_FCT_DO( iret = _peer_events_send(peer, PEVENT_SND_FAILED, NULL /* could put ssf_error here if useful */), { errno = iret; goto out; } ); break; case SCTP_REMOTE_ERROR: #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification"); TRACE_DEBUG(FULL, " err : %hu", ntohs(notif->sn_remote_error.sre_error)); TRACE_DEBUG(FULL, " len : %hu", ntohs(notif->sn_remote_error.sre_length)); #endif /* DEBUG_SCTP */ CHECK_FCT_DO( _peer_events_send(peer, PEVENT_REMOTE_ERR, NULL /* sre_error ? */ ), /* continue, unless this is a real problem... */ ); break; case SCTP_SHUTDOWN_EVENT: #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification"); #endif /* DEBUG_SCTP */ CHECK_FCT_DO( iret = _peer_events_send(peer, PEVENT_DISCONNECTED, NULL), { errno = iret; goto out; } ); break; default: TRACE_DEBUG(FULL, "Received unknown notification %d", notif->sn_header.sn_type); } /* Restart to receive next message. We continue with this buffer */ *len = 0; goto nextmsg; } /* Now handle the anciliary data */ for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) { /* We deal only with anciliary data at SCTP level */ if (hdr->cmsg_level != IPPROTO_SCTP) { TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level); continue; } switch (hdr->cmsg_type) { case SCTP_SNDRCV: sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr); #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV"); TRACE_DEBUG(FULL, " sinfo_stream : %hu", sndrcv->sinfo_stream); TRACE_DEBUG(FULL, " sinfo_ssn : %hu", sndrcv->sinfo_ssn); TRACE_DEBUG(FULL, " sinfo_flags : %hu", sndrcv->sinfo_flags); // TRACE_DEBUG(FULL, " sinfo_pr_policy : %hu", sndrcv->sinfo_pr_policy); TRACE_DEBUG(FULL, " sinfo_ppid : %u" , sndrcv->sinfo_ppid); TRACE_DEBUG(FULL, " sinfo_context : %u" , sndrcv->sinfo_context); // TRACE_DEBUG(FULL, " sinfo_pr_value : %u" , sndrcv->sinfo_pr_value); TRACE_DEBUG(FULL, " sinfo_tsn : %u" , sndrcv->sinfo_tsn); TRACE_DEBUG(FULL, " sinfo_cumtsn : %u" , sndrcv->sinfo_cumtsn); #endif /* DEBUG_SCTP */ *streamid = sndrcv->sinfo_stream; break; default: TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type); } } /* Okay, we have received a normal message */ #ifdef DEBUG_SCTP TRACE_DEBUG(FULL, "Received SCTP packet (%d bytes on stream %hu) from '%s'", *len, *streamid, peer->p_diamid ? peer->p_diamid : "(unknown)"); #endif /* DEBUG_SCTP */ ret = *len; out: /* gcc... */ ; pthread_cleanup_pop(0); return ret; }