Brian Waters | 13d9601 | 2017-12-08 16:53:31 -0600 | [diff] [blame] | 1 | /********************************************************************************************************* |
| 2 | * Software License Agreement (BSD License) * |
| 3 | * Author: Sebastien Decugis <sdecugis@freediameter.net> * |
| 4 | * * |
| 5 | * Copyright (c) 2015, WIDE Project and NICT * |
| 6 | * All rights reserved. * |
| 7 | * * |
| 8 | * Redistribution and use of this software in source and binary forms, with or without modification, are * |
| 9 | * permitted provided that the following conditions are met: * |
| 10 | * * |
| 11 | * * Redistributions of source code must retain the above * |
| 12 | * copyright notice, this list of conditions and the * |
| 13 | * following disclaimer. * |
| 14 | * * |
| 15 | * * Redistributions in binary form must reproduce the above * |
| 16 | * copyright notice, this list of conditions and the * |
| 17 | * following disclaimer in the documentation and/or other * |
| 18 | * materials provided with the distribution. * |
| 19 | * * |
| 20 | * * Neither the name of the WIDE Project or NICT nor the * |
| 21 | * names of its contributors may be used to endorse or * |
| 22 | * promote products derived from this software without * |
| 23 | * specific prior written permission of WIDE Project and * |
| 24 | * NICT. * |
| 25 | * * |
| 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
| 27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
| 28 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
| 29 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
| 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
| 32 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
| 33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
| 34 | *********************************************************************************************************/ |
| 35 | |
| 36 | #include "fdcore-internal.h" |
| 37 | #include "cnxctx.h" |
| 38 | |
| 39 | #include <netinet/sctp.h> |
| 40 | #include <sys/uio.h> |
| 41 | |
| 42 | /* Size of buffer to receive ancilliary data. May need to be enlarged if more sockopt are set... */ |
| 43 | #ifndef CMSG_BUF_LEN |
| 44 | #define CMSG_BUF_LEN 1024 |
| 45 | #endif /* CMSG_BUF_LEN */ |
| 46 | |
| 47 | /* Use old draft-ietf-tsvwg-sctpsocket-17 API ? If not defined, RFC6458 API will be used */ |
| 48 | /* #define OLD_SCTP_SOCKET_API */ |
| 49 | |
| 50 | /* Automatically fallback to old API if some of the new symbols are not defined */ |
| 51 | #if (!defined(SCTP_CONNECTX_4_ARGS) || (!defined(SCTP_RECVRCVINFO)) || (!defined(SCTP_SNDINFO))) |
| 52 | # define OLD_SCTP_SOCKET_API |
| 53 | #endif |
| 54 | |
| 55 | |
| 56 | /* Temper with the retransmission timers to try and improve disconnection detection response? Undef this to keep the defaults of SCTP stack */ |
| 57 | #ifndef USE_DEFAULT_SCTP_RTX_PARAMS /* make this a configuration option if useful */ |
| 58 | #define ADJUST_RTX_PARAMS |
| 59 | #endif /* USE_DEFAULT_SCTP_RTX_PARAMS */ |
| 60 | |
| 61 | /* Pre-binding socket options -- # streams read in config */ |
| 62 | static int fd_setsockopt_prebind(int sk) |
| 63 | { |
| 64 | socklen_t sz; |
| 65 | |
| 66 | TRACE_ENTRY( "%d", sk); |
| 67 | |
| 68 | CHECK_PARAMS( sk > 0 ); |
| 69 | |
| 70 | { |
| 71 | int reuse = 1; |
| 72 | CHECK_SYS( setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ); |
| 73 | } |
| 74 | |
| 75 | #ifdef ADJUST_RTX_PARAMS |
| 76 | /* Set the retransmit parameters */ |
| 77 | #ifdef SCTP_RTOINFO |
| 78 | { |
| 79 | struct sctp_rtoinfo rtoinfo; |
| 80 | memset(&rtoinfo, 0, sizeof(rtoinfo)); |
| 81 | |
| 82 | if (TRACE_BOOL(ANNOYING)) { |
| 83 | sz = sizeof(rtoinfo); |
| 84 | /* Read socket defaults */ |
| 85 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz) ); |
| 86 | if (sz != sizeof(rtoinfo)) |
| 87 | { |
| 88 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(rtoinfo)); |
| 89 | return ENOTSUP; |
| 90 | } |
| 91 | fd_log_debug( "Def SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial); |
| 92 | fd_log_debug( " srto_min : %u", rtoinfo.srto_min); |
| 93 | fd_log_debug( " srto_max : %u", rtoinfo.srto_max); |
| 94 | } |
| 95 | |
| 96 | /* rtoinfo.srto_initial: Estimate of the RTT before it can be measured; keep the default value */ |
| 97 | rtoinfo.srto_max = 5000; /* Maximum retransmit timer (in ms), we want fast retransmission time. */ |
| 98 | rtoinfo.srto_min = 1000; /* Value under which the RTO does not descend, we set this value to not conflict with srto_max */ |
| 99 | |
| 100 | /* Set the option to the socket */ |
| 101 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo)) ); |
| 102 | |
| 103 | if (TRACE_BOOL(ANNOYING)) { |
| 104 | /* Check new values */ |
| 105 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_RTOINFO, &rtoinfo, &sz) ); |
| 106 | fd_log_debug( "New SCTP_RTOINFO : srto_initial : %u", rtoinfo.srto_initial); |
| 107 | fd_log_debug( " srto_max : %u", rtoinfo.srto_max); |
| 108 | fd_log_debug( " srto_min : %u", rtoinfo.srto_min); |
| 109 | } |
| 110 | } |
| 111 | #else /* SCTP_RTOINFO */ |
| 112 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_RTOINFO"); |
| 113 | #endif /* SCTP_RTOINFO */ |
| 114 | |
| 115 | /* Set the association parameters: max number of retransmits, ... */ |
| 116 | #ifdef SCTP_ASSOCINFO |
| 117 | { |
| 118 | struct sctp_assocparams assoc; |
| 119 | memset(&assoc, 0, sizeof(assoc)); |
| 120 | |
| 121 | if (TRACE_BOOL(ANNOYING)) { |
| 122 | sz = sizeof(assoc); |
| 123 | /* Read socket defaults */ |
| 124 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz) ); |
| 125 | if (sz != sizeof(assoc)) |
| 126 | { |
| 127 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(assoc)); |
| 128 | return ENOTSUP; |
| 129 | } |
| 130 | fd_log_debug( "Def SCTP_ASSOCINFO : sasoc_asocmaxrxt : %hu", assoc.sasoc_asocmaxrxt); |
| 131 | fd_log_debug( " sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations); |
| 132 | fd_log_debug( " sasoc_peer_rwnd : %u" , assoc.sasoc_peer_rwnd); |
| 133 | fd_log_debug( " sasoc_local_rwnd : %u" , assoc.sasoc_local_rwnd); |
| 134 | fd_log_debug( " sasoc_cookie_life : %u" , assoc.sasoc_cookie_life); |
| 135 | } |
| 136 | |
| 137 | assoc.sasoc_asocmaxrxt = 4; /* Maximum number of retransmission attempts: we want fast detection of errors */ |
| 138 | /* Note that this must remain less than the sum of retransmission parameters of the different paths. */ |
| 139 | |
| 140 | /* Set the option to the socket */ |
| 141 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, sizeof(assoc)) ); |
| 142 | |
| 143 | if (TRACE_BOOL(ANNOYING)) { |
| 144 | /* Check new values */ |
| 145 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_ASSOCINFO, &assoc, &sz) ); |
| 146 | fd_log_debug( "New SCTP_ASSOCINFO : sasoc_asocmaxrxt : %hu", assoc.sasoc_asocmaxrxt); |
| 147 | fd_log_debug( " sasoc_number_peer_destinations : %hu", assoc.sasoc_number_peer_destinations); |
| 148 | fd_log_debug( " sasoc_peer_rwnd : %u" , assoc.sasoc_peer_rwnd); |
| 149 | fd_log_debug( " sasoc_local_rwnd : %u" , assoc.sasoc_local_rwnd); |
| 150 | fd_log_debug( " sasoc_cookie_life : %u" , assoc.sasoc_cookie_life); |
| 151 | } |
| 152 | } |
| 153 | #else /* SCTP_ASSOCINFO */ |
| 154 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_ASSOCINFO"); |
| 155 | #endif /* SCTP_ASSOCINFO */ |
| 156 | #endif /* ADJUST_RTX_PARAMS */ |
| 157 | |
| 158 | /* Set the INIT parameters, such as number of streams */ |
| 159 | #ifdef SCTP_INITMSG |
| 160 | { |
| 161 | struct sctp_initmsg init; |
| 162 | memset(&init, 0, sizeof(init)); |
| 163 | |
| 164 | if (TRACE_BOOL(ANNOYING)) { |
| 165 | sz = sizeof(init); |
| 166 | |
| 167 | /* Read socket defaults */ |
| 168 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz) ); |
| 169 | if (sz != sizeof(init)) |
| 170 | { |
| 171 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(init)); |
| 172 | return ENOTSUP; |
| 173 | } |
| 174 | fd_log_debug( "Def SCTP_INITMSG : sinit_num_ostreams : %hu", init.sinit_num_ostreams); |
| 175 | fd_log_debug( " sinit_max_instreams : %hu", init.sinit_max_instreams); |
| 176 | fd_log_debug( " sinit_max_attempts : %hu", init.sinit_max_attempts); |
| 177 | fd_log_debug( " sinit_max_init_timeo : %hu", init.sinit_max_init_timeo); |
| 178 | } |
| 179 | |
| 180 | /* Set the init options -- need to receive SCTP_COMM_UP to confirm the requested parameters, but we don't care (best effort) */ |
| 181 | init.sinit_num_ostreams = fd_g_config->cnf_sctp_str; /* desired number of outgoing streams */ |
| 182 | init.sinit_max_init_timeo = CNX_TIMEOUT * 1000; |
| 183 | |
| 184 | /* Set the option to the socket */ |
| 185 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, sizeof(init)) ); |
| 186 | |
| 187 | if (TRACE_BOOL(ANNOYING)) { |
| 188 | /* Check new values */ |
| 189 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_INITMSG, &init, &sz) ); |
| 190 | fd_log_debug( "New SCTP_INITMSG : sinit_num_ostreams : %hu", init.sinit_num_ostreams); |
| 191 | fd_log_debug( " sinit_max_instreams : %hu", init.sinit_max_instreams); |
| 192 | fd_log_debug( " sinit_max_attempts : %hu", init.sinit_max_attempts); |
| 193 | fd_log_debug( " sinit_max_init_timeo : %hu", init.sinit_max_init_timeo); |
| 194 | } |
| 195 | } |
| 196 | #else /* SCTP_INITMSG */ |
| 197 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_INITMSG"); |
| 198 | #endif /* SCTP_INITMSG */ |
| 199 | |
| 200 | /* The SO_LINGER option will be reset if we want to perform SCTP ABORT */ |
| 201 | #ifdef SO_LINGER |
| 202 | { |
| 203 | struct linger linger; |
| 204 | memset(&linger, 0, sizeof(linger)); |
| 205 | |
| 206 | if (TRACE_BOOL(ANNOYING)) { |
| 207 | sz = sizeof(linger); |
| 208 | /* Read socket defaults */ |
| 209 | CHECK_SYS( getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz) ); |
| 210 | if (sz != sizeof(linger)) |
| 211 | { |
| 212 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(linger)); |
| 213 | return ENOTSUP; |
| 214 | } |
| 215 | fd_log_debug( "Def SO_LINGER : l_onoff : %d", linger.l_onoff); |
| 216 | fd_log_debug( " l_linger : %d", linger.l_linger); |
| 217 | } |
| 218 | |
| 219 | linger.l_onoff = 0; /* Do not activate the linger */ |
| 220 | linger.l_linger = 0; /* Ignored, but it would mean : Return immediately when closing (=> abort) (graceful shutdown in background) */ |
| 221 | |
| 222 | /* Set the option */ |
| 223 | CHECK_SYS( setsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) ); |
| 224 | |
| 225 | if (TRACE_BOOL(ANNOYING)) { |
| 226 | /* Check new values */ |
| 227 | CHECK_SYS( getsockopt(sk, SOL_SOCKET, SO_LINGER, &linger, &sz) ); |
| 228 | fd_log_debug( "New SO_LINGER : l_onoff : %d", linger.l_onoff); |
| 229 | fd_log_debug( " l_linger : %d", linger.l_linger); |
| 230 | } |
| 231 | } |
| 232 | #else /* SO_LINGER */ |
| 233 | TRACE_DEBUG(ANNOYING, "Skipping SO_LINGER"); |
| 234 | #endif /* SO_LINGER */ |
| 235 | |
| 236 | /* Set the NODELAY option (Nagle-like algorithm) */ |
| 237 | #ifdef SCTP_NODELAY |
| 238 | { |
| 239 | int nodelay; |
| 240 | |
| 241 | if (TRACE_BOOL(ANNOYING)) { |
| 242 | sz = sizeof(nodelay); |
| 243 | /* Read socket defaults */ |
| 244 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz) ); |
| 245 | if (sz != sizeof(nodelay)) |
| 246 | { |
| 247 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nodelay)); |
| 248 | return ENOTSUP; |
| 249 | } |
| 250 | fd_log_debug( "Def SCTP_NODELAY value : %s", nodelay ? "true" : "false"); |
| 251 | } |
| 252 | |
| 253 | nodelay = 1; /* We turn ON to disable the Nagle algorithm, so that packets are sent ASAP. */ |
| 254 | |
| 255 | /* Set the option to the socket */ |
| 256 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay)) ); |
| 257 | |
| 258 | if (TRACE_BOOL(ANNOYING)) { |
| 259 | /* Check new values */ |
| 260 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, &sz) ); |
| 261 | fd_log_debug( "New SCTP_NODELAY value : %s", nodelay ? "true" : "false"); |
| 262 | } |
| 263 | } |
| 264 | #else /* SCTP_NODELAY */ |
| 265 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_NODELAY"); |
| 266 | #endif /* SCTP_NODELAY */ |
| 267 | |
| 268 | /* |
| 269 | SO_RCVBUF size of receiver window |
| 270 | SO_SNDBUF size of pending data to send |
| 271 | SCTP_AUTOCLOSE for one-to-many only |
| 272 | SCTP_PRIMARY_ADDR use this address as primary locally |
| 273 | SCTP_ADAPTATION_LAYER set adaptation layer indication, we don't use this |
| 274 | */ |
| 275 | |
| 276 | /* Set the SCTP_DISABLE_FRAGMENTS option, required for TLS */ |
| 277 | #ifdef SCTP_DISABLE_FRAGMENTS |
| 278 | { |
| 279 | int nofrag; |
| 280 | |
| 281 | if (TRACE_BOOL(ANNOYING)) { |
| 282 | sz = sizeof(nofrag); |
| 283 | /* Read socket defaults */ |
| 284 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz) ); |
| 285 | if (sz != sizeof(nofrag)) |
| 286 | { |
| 287 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(nofrag)); |
| 288 | return ENOTSUP; |
| 289 | } |
| 290 | fd_log_debug( "Def SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false"); |
| 291 | } |
| 292 | |
| 293 | nofrag = 0; /* We turn ON the fragmentation, since Diameter messages & TLS messages can be quite large. */ |
| 294 | |
| 295 | /* Set the option to the socket */ |
| 296 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, sizeof(nofrag)) ); |
| 297 | |
| 298 | if (TRACE_BOOL(ANNOYING)) { |
| 299 | /* Check new values */ |
| 300 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &nofrag, &sz) ); |
| 301 | fd_log_debug( "New SCTP_DISABLE_FRAGMENTS value : %s", nofrag ? "true" : "false"); |
| 302 | } |
| 303 | } |
| 304 | #else /* SCTP_DISABLE_FRAGMENTS */ |
| 305 | # error "TLS requires support of SCTP_DISABLE_FRAGMENTS" |
| 306 | #endif /* SCTP_DISABLE_FRAGMENTS */ |
| 307 | |
| 308 | /* SCTP_PEER_ADDR_PARAMS control heartbeat per peer address. We set it as a default for all addresses in the association; not sure if it works ... */ |
| 309 | #ifdef SCTP_PEER_ADDR_PARAMS |
| 310 | { |
| 311 | struct sctp_paddrparams parms; |
| 312 | memset(&parms, 0, sizeof(parms)); |
| 313 | |
| 314 | /* Some kernel versions need this to be set */ |
| 315 | parms.spp_address.ss_family = AF_INET; |
| 316 | |
| 317 | if (TRACE_BOOL(ANNOYING)) { |
| 318 | sz = sizeof(parms); |
| 319 | |
| 320 | /* Read socket defaults */ |
| 321 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz) ); |
| 322 | if (sz != sizeof(parms)) |
| 323 | { |
| 324 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(parms)); |
| 325 | return ENOTSUP; |
| 326 | } |
| 327 | fd_log_debug( "Def SCTP_PEER_ADDR_PARAMS : spp_hbinterval : %u", parms.spp_hbinterval); |
| 328 | fd_log_debug( " spp_pathmaxrxt : %hu", parms.spp_pathmaxrxt); |
| 329 | fd_log_debug( " spp_pathmtu : %u", parms.spp_pathmtu); |
| 330 | fd_log_debug( " spp_flags : %x", parms.spp_flags); |
| 331 | // fd_log_debug( " spp_ipv6_flowlabel: %u", parms.spp_ipv6_flowlabel); |
| 332 | // fd_log_debug( " spp_ipv4_tos : %hhu",parms.spp_ipv4_tos); |
| 333 | } |
| 334 | |
| 335 | parms.spp_flags = SPP_HB_ENABLE; /* Enable heartbeat for the association */ |
| 336 | #ifdef SPP_PMTUD_ENABLE |
| 337 | parms.spp_flags |= SPP_PMTUD_ENABLE; /* also enable path MTU discovery mechanism */ |
| 338 | #endif /* SPP_PMTUD_ENABLE */ |
| 339 | |
| 340 | #ifdef ADJUST_RTX_PARAMS |
| 341 | parms.spp_hbinterval = 6000; /* Send an heartbeat every 6 seconds to quickly start retransmissions */ |
| 342 | /* parms.spp_pathmaxrxt : max nbr of restransmissions on this address. There is a relationship with sasoc_asocmaxrxt, so we leave the default here */ |
| 343 | #endif /* ADJUST_RTX_PARAMS */ |
| 344 | |
| 345 | /* Set the option to the socket */ |
| 346 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, sizeof(parms)) ); |
| 347 | |
| 348 | if (TRACE_BOOL(ANNOYING)) { |
| 349 | /* Check new values */ |
| 350 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS, &parms, &sz) ); |
| 351 | fd_log_debug( "New SCTP_PEER_ADDR_PARAMS : spp_hbinterval : %u", parms.spp_hbinterval); |
| 352 | fd_log_debug( " spp_pathmaxrxt : %hu", parms.spp_pathmaxrxt); |
| 353 | fd_log_debug( " spp_pathmtu : %u", parms.spp_pathmtu); |
| 354 | fd_log_debug( " spp_flags : %x", parms.spp_flags); |
| 355 | // fd_log_debug( " spp_ipv6_flowlabel: %u", parms.spp_ipv6_flowlabel); |
| 356 | // fd_log_debug( " spp_ipv4_tos : %hhu",parms.spp_ipv4_tos); |
| 357 | } |
| 358 | } |
| 359 | #else /* SCTP_PEER_ADDR_PARAMS */ |
| 360 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_PEER_ADDR_PARAMS"); |
| 361 | #endif /* SCTP_PEER_ADDR_PARAMS */ |
| 362 | |
| 363 | /* |
| 364 | SCTP_DEFAULT_SEND_PARAM - DEPRECATED // parameters for the sendto() call, we don't use it. |
| 365 | */ |
| 366 | |
| 367 | /* Subscribe to some notifications */ |
| 368 | #ifdef OLD_SCTP_SOCKET_API |
| 369 | #ifdef SCTP_EVENTS /* DEPRECATED */ |
| 370 | { |
| 371 | struct sctp_event_subscribe event; |
| 372 | |
| 373 | memset(&event, 0, sizeof(event)); |
| 374 | event.sctp_data_io_event = 1; /* to receive the stream ID in SCTP_SNDRCV ancilliary data on message reception */ |
| 375 | event.sctp_association_event = 0; /* new or closed associations (mostly for one-to-many style sockets) */ |
| 376 | event.sctp_address_event = 1; /* address changes */ |
| 377 | event.sctp_send_failure_event = 1; /* delivery failures */ |
| 378 | event.sctp_peer_error_event = 1; /* remote peer sends an error */ |
| 379 | event.sctp_shutdown_event = 1; /* peer has sent a SHUTDOWN */ |
| 380 | event.sctp_partial_delivery_event = 1; /* a partial delivery is aborted, probably indicating the connection is being shutdown */ |
| 381 | // event.sctp_adaptation_layer_event = 0; /* adaptation layer notifications */ |
| 382 | // event.sctp_authentication_event = 0; /* when new key is made active */ |
| 383 | |
| 384 | /* Set the option to the socket */ |
| 385 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) ); |
| 386 | |
| 387 | if (TRACE_BOOL(ANNOYING)) { |
| 388 | sz = sizeof(event); |
| 389 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_EVENTS, &event, &sz) ); |
| 390 | if (sz != sizeof(event)) |
| 391 | { |
| 392 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(event)); |
| 393 | return ENOTSUP; |
| 394 | } |
| 395 | |
| 396 | fd_log_debug( "SCTP_EVENTS : sctp_data_io_event : %hhu", event.sctp_data_io_event); |
| 397 | fd_log_debug( " sctp_association_event : %hhu", event.sctp_association_event); |
| 398 | fd_log_debug( " sctp_address_event : %hhu", event.sctp_address_event); |
| 399 | fd_log_debug( " sctp_send_failure_event : %hhu", event.sctp_send_failure_event); |
| 400 | fd_log_debug( " sctp_peer_error_event : %hhu", event.sctp_peer_error_event); |
| 401 | fd_log_debug( " sctp_shutdown_event : %hhu", event.sctp_shutdown_event); |
| 402 | fd_log_debug( " sctp_partial_delivery_event : %hhu", event.sctp_partial_delivery_event); |
| 403 | fd_log_debug( " sctp_adaptation_layer_event : %hhu", event.sctp_adaptation_layer_event); |
| 404 | // fd_log_debug( " sctp_authentication_event : %hhu", event.sctp_authentication_event); |
| 405 | } |
| 406 | } |
| 407 | #else /* SCTP_EVENTS */ |
| 408 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_EVENTS"); |
| 409 | #endif /* SCTP_EVENTS */ |
| 410 | #endif /* OLD_SCTP_SOCKET_API */ |
| 411 | |
| 412 | /* Set the v4 mapped addresses option */ |
| 413 | #ifdef SCTP_I_WANT_MAPPED_V4_ADDR |
| 414 | if (!fd_g_config->cnf_flags.no_ip6) { |
| 415 | int v4mapped; |
| 416 | |
| 417 | if (TRACE_BOOL(ANNOYING)) { |
| 418 | sz = sizeof(v4mapped); |
| 419 | /* Read socket defaults */ |
| 420 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz) ); |
| 421 | if (sz != sizeof(v4mapped)) |
| 422 | { |
| 423 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(v4mapped)); |
| 424 | return ENOTSUP; |
| 425 | } |
| 426 | fd_log_debug( "Def SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false"); |
| 427 | } |
| 428 | |
| 429 | #ifndef SCTP_USE_MAPPED_ADDRESSES |
| 430 | v4mapped = 0; /* We don't want v4 mapped addresses */ |
| 431 | #else /* SCTP_USE_MAPPED_ADDRESSES */ |
| 432 | v4mapped = 1; /* but we may have to, otherwise the bind fails in some environments */ |
| 433 | #endif /* SCTP_USE_MAPPED_ADDRESSES */ |
| 434 | |
| 435 | /* Set the option to the socket */ |
| 436 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, sizeof(v4mapped)) ); |
| 437 | |
| 438 | if (TRACE_BOOL(ANNOYING)) { |
| 439 | /* Check new values */ |
| 440 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &v4mapped, &sz) ); |
| 441 | fd_log_debug( "New SCTP_I_WANT_MAPPED_V4_ADDR value : %s", v4mapped ? "true" : "false"); |
| 442 | } |
| 443 | } else { |
| 444 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR, since IPv6 disabled."); |
| 445 | } |
| 446 | #else /* SCTP_I_WANT_MAPPED_V4_ADDR */ |
| 447 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_I_WANT_MAPPED_V4_ADDR"); |
| 448 | #endif /* SCTP_I_WANT_MAPPED_V4_ADDR */ |
| 449 | |
| 450 | /* |
| 451 | SCTP_MAXSEG max size of fragmented segments -- bound to PMTU |
| 452 | SCTP_HMAC_IDENT authentication algorithms |
| 453 | SCTP_AUTH_ACTIVE_KEY set the active key |
| 454 | SCTP_DELAYED_SACK control delayed acks |
| 455 | */ |
| 456 | |
| 457 | |
| 458 | /* Set the interleaving option */ |
| 459 | #ifdef SCTP_FRAGMENT_INTERLEAVE |
| 460 | { |
| 461 | int interleave; |
| 462 | |
| 463 | if (TRACE_BOOL(ANNOYING)) { |
| 464 | sz = sizeof(interleave); |
| 465 | /* Read socket defaults */ |
| 466 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz) ); |
| 467 | if (sz != sizeof(interleave)) |
| 468 | { |
| 469 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(interleave)); |
| 470 | return ENOTSUP; |
| 471 | } |
| 472 | fd_log_debug( "Def SCTP_FRAGMENT_INTERLEAVE value : %d", interleave); |
| 473 | } |
| 474 | |
| 475 | #if 0 |
| 476 | interleave = 2; /* Allow partial delivery on several streams at the same time, since we are stream-aware in our security modules */ |
| 477 | #else /* 0 */ |
| 478 | interleave = 1; /* hmmm actually, we are not yet capable of handling this, and we don t need it. */ |
| 479 | #endif /* 0 */ |
| 480 | |
| 481 | /* Set the option to the socket */ |
| 482 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, sizeof(interleave)) ); |
| 483 | |
| 484 | if (TRACE_BOOL(ANNOYING)) { |
| 485 | /* Check new values */ |
| 486 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE, &interleave, &sz) ); |
| 487 | fd_log_debug( "New SCTP_FRAGMENT_INTERLEAVE value : %d", interleave); |
| 488 | } |
| 489 | } |
| 490 | #else /* SCTP_FRAGMENT_INTERLEAVE */ |
| 491 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_FRAGMENT_INTERLEAVE"); |
| 492 | #endif /* SCTP_FRAGMENT_INTERLEAVE */ |
| 493 | |
| 494 | /* |
| 495 | SCTP_PARTIAL_DELIVERY_POINT control partial delivery size |
| 496 | SCTP_USE_EXT_RCVINFO - DEPRECATED use extended receive info structure (information about the next message if available) |
| 497 | */ |
| 498 | /* SCTP_AUTO_ASCONF is set by the postbind function */ |
| 499 | /* |
| 500 | SCTP_MAX_BURST number of packets that can be burst emitted |
| 501 | SCTP_CONTEXT save a context information along with the association. |
| 502 | */ |
| 503 | |
| 504 | /* SCTP_EXPLICIT_EOR: we assume implicit EOR in freeDiameter, so let's ensure this is known by the stack */ |
| 505 | #ifdef SCTP_EXPLICIT_EOR |
| 506 | { |
| 507 | int bool; |
| 508 | |
| 509 | if (TRACE_BOOL(ANNOYING)) { |
| 510 | sz = sizeof(bool); |
| 511 | /* Read socket defaults */ |
| 512 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz) ); |
| 513 | if (sz != sizeof(bool)) |
| 514 | { |
| 515 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(bool)); |
| 516 | return ENOTSUP; |
| 517 | } |
| 518 | fd_log_debug( "Def SCTP_EXPLICIT_EOR value : %s", bool ? "true" : "false"); |
| 519 | } |
| 520 | |
| 521 | bool = 0; |
| 522 | |
| 523 | /* Set the option to the socket */ |
| 524 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, sizeof(bool)) ); |
| 525 | |
| 526 | if (TRACE_BOOL(ANNOYING)) { |
| 527 | /* Check new values */ |
| 528 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &bool, &sz) ); |
| 529 | fd_log_debug( "New SCTP_EXPLICIT_EOR value : %s", bool ? "true" : "false"); |
| 530 | } |
| 531 | } |
| 532 | #else /* SCTP_EXPLICIT_EOR */ |
| 533 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_EXPLICIT_EOR"); |
| 534 | #endif /* SCTP_EXPLICIT_EOR */ |
| 535 | |
| 536 | /* |
| 537 | SCTP_REUSE_PORT share one listening port with several sockets |
| 538 | */ |
| 539 | |
| 540 | #ifndef OLD_SCTP_SOCKET_API |
| 541 | #ifdef SCTP_EVENT |
| 542 | { |
| 543 | /* Subscribe to the following events */ |
| 544 | int events_I_want[] = { |
| 545 | #ifdef SCTP_ASSOC_CHANGE |
| 546 | /* SCTP_ASSOC_CHANGE, */ |
| 547 | #endif |
| 548 | #ifdef SCTP_PEER_ADDR_CHANGE |
| 549 | SCTP_PEER_ADDR_CHANGE, |
| 550 | #endif |
| 551 | #ifdef SCTP_REMOTE_ERROR |
| 552 | SCTP_REMOTE_ERROR, |
| 553 | #endif |
| 554 | #ifdef SCTP_SEND_FAILED_EVENT |
| 555 | SCTP_SEND_FAILED_EVENT, |
| 556 | #endif |
| 557 | #ifdef SCTP_SHUTDOWN_EVENT |
| 558 | SCTP_SHUTDOWN_EVENT, |
| 559 | #endif |
| 560 | #ifdef SCTP_ADAPTATION_INDICATION |
| 561 | /* SCTP_ADAPTATION_INDICATION, */ |
| 562 | #endif |
| 563 | #ifdef SCTP_PARTIAL_DELIVERY_EVENT |
| 564 | /* SCTP_PARTIAL_DELIVERY_EVENT, */ |
| 565 | #endif |
| 566 | #ifdef SCTP_AUTHENTICATION_EVENT |
| 567 | /* SCTP_AUTHENTICATION_EVENT, */ |
| 568 | #endif |
| 569 | #ifdef SCTP_SENDER_DRY_EVENT |
| 570 | /* SCTP_SENDER_DRY_EVENT, */ |
| 571 | #endif |
| 572 | 0 |
| 573 | }; |
| 574 | int i; |
| 575 | |
| 576 | struct sctp_event event; |
| 577 | |
| 578 | for (i = 0; i < (sizeof(events_I_want) / sizeof(events_I_want[0]) - 1); i++) { |
| 579 | memset(&event, 0, sizeof(event)); |
| 580 | event.se_type = events_I_want[i]; |
| 581 | event.se_on = 1; |
| 582 | |
| 583 | /* Set the option to the socket */ |
| 584 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) ); |
| 585 | } |
| 586 | } |
| 587 | #else /* SCTP_EVENT */ |
| 588 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_EVENT"); |
| 589 | #endif /* SCTP_EVENT */ |
| 590 | |
| 591 | |
| 592 | #ifdef SCTP_RECVRCVINFO /* Replaces SCTP_SNDRCV */ |
| 593 | { |
| 594 | int bool = 1; |
| 595 | |
| 596 | /* Set the option to the socket */ |
| 597 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_RECVRCVINFO, &bool, sizeof(bool)) ); |
| 598 | |
| 599 | } |
| 600 | #else /* SCTP_RECVRCVINFO */ |
| 601 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_RECVRCVINFO"); |
| 602 | #endif /* SCTP_RECVRCVINFO */ |
| 603 | |
| 604 | |
| 605 | #endif /* OLD_SCTP_SOCKET_API */ |
| 606 | |
| 607 | /* |
| 608 | SCTP_RECVNXTINFO |
| 609 | |
| 610 | SCTP_DEFAULT_SNDINFO : send defaults |
| 611 | SCTP_DEFAULT_PRINFO : default PR-SCTP |
| 612 | */ |
| 613 | |
| 614 | |
| 615 | /* In case of no_ip4, force the v6only option */ |
| 616 | #ifdef IPV6_V6ONLY |
| 617 | if (fd_g_config->cnf_flags.no_ip4) { |
| 618 | int opt = 1; |
| 619 | CHECK_SYS(setsockopt(sk, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))); |
| 620 | } |
| 621 | #endif /* IPV6_V6ONLY */ |
| 622 | |
| 623 | return 0; |
| 624 | } |
| 625 | |
| 626 | |
| 627 | /* Post-binding socket options */ |
| 628 | static int fd_setsockopt_postbind(int sk, int bound_to_default) |
| 629 | { |
| 630 | TRACE_ENTRY( "%d %d", sk, bound_to_default); |
| 631 | |
| 632 | CHECK_PARAMS( (sk > 0) ); |
| 633 | |
| 634 | /* Set the ASCONF option */ |
| 635 | #ifdef SCTP_AUTO_ASCONF |
| 636 | if (bound_to_default) { |
| 637 | int asconf; |
| 638 | |
| 639 | if (TRACE_BOOL(ANNOYING)) { |
| 640 | socklen_t sz; |
| 641 | |
| 642 | sz = sizeof(asconf); |
| 643 | /* Read socket defaults */ |
| 644 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz) ); |
| 645 | if (sz != sizeof(asconf)) |
| 646 | { |
| 647 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %d", sz, (socklen_t)sizeof(asconf)); |
| 648 | return ENOTSUP; |
| 649 | } |
| 650 | fd_log_debug( "Def SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false"); |
| 651 | } |
| 652 | |
| 653 | asconf = 1; /* allow automatic use of added or removed addresses in the association (for bound-all sockets) */ |
| 654 | |
| 655 | /* Set the option to the socket */ |
| 656 | CHECK_SYS( setsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, sizeof(asconf)) ); |
| 657 | |
| 658 | if (TRACE_BOOL(ANNOYING)) { |
| 659 | socklen_t sz = sizeof(asconf); |
| 660 | /* Check new values */ |
| 661 | CHECK_SYS( getsockopt(sk, IPPROTO_SCTP, SCTP_AUTO_ASCONF, &asconf, &sz) ); |
| 662 | fd_log_debug( "New SCTP_AUTO_ASCONF value : %s", asconf ? "true" : "false"); |
| 663 | } |
| 664 | } |
| 665 | #else /* SCTP_AUTO_ASCONF */ |
| 666 | TRACE_DEBUG(ANNOYING, "Skipping SCTP_AUTO_ASCONF"); |
| 667 | #endif /* SCTP_AUTO_ASCONF */ |
| 668 | |
| 669 | return 0; |
| 670 | } |
| 671 | |
| 672 | /* Add addresses from a list to an array, with filter on the flags */ |
| 673 | static int add_addresses_from_list_mask(uint8_t ** array, size_t * size, int * addr_count, int target_family, uint16_t port, struct fd_list * list, uint32_t mask, uint32_t val) |
| 674 | { |
| 675 | struct fd_list * li; |
| 676 | int to_add4 = 0; |
| 677 | int to_add6 = 0; |
| 678 | union { |
| 679 | uint8_t *buf; |
| 680 | sSA4 *sin; |
| 681 | sSA6 *sin6; |
| 682 | } ptr; |
| 683 | size_t sz; |
| 684 | |
| 685 | /* First, count the number of addresses to add */ |
| 686 | for (li = list->next; li != list; li = li->next) { |
| 687 | struct fd_endpoint * ep = (struct fd_endpoint *) li; |
| 688 | |
| 689 | /* Do the flag match ? */ |
| 690 | if ((val & mask) != (ep->flags & mask)) |
| 691 | continue; |
| 692 | |
| 693 | if (ep->sa.sa_family == AF_INET) { |
| 694 | to_add4 ++; |
| 695 | } else { |
| 696 | to_add6 ++; |
| 697 | } |
| 698 | } |
| 699 | |
| 700 | if ((to_add4 + to_add6) == 0) |
| 701 | return 0; /* nothing to do */ |
| 702 | |
| 703 | /* The size to add */ |
| 704 | if (target_family == AF_INET) { |
| 705 | sz = to_add4 * sizeof(sSA4); |
| 706 | } else { |
| 707 | #ifndef SCTP_USE_MAPPED_ADDRESSES |
| 708 | sz = (to_add4 * sizeof(sSA4)) + (to_add6 * sizeof(sSA6)); |
| 709 | #else /* SCTP_USE_MAPPED_ADDRESSES */ |
| 710 | sz = (to_add4 + to_add6) * sizeof(sSA6); |
| 711 | #endif /* SCTP_USE_MAPPED_ADDRESSES */ |
| 712 | } |
| 713 | |
| 714 | /* Now, (re)alloc the array to store the new addresses */ |
| 715 | CHECK_MALLOC( *array = realloc(*array, *size + sz) ); |
| 716 | |
| 717 | /* Finally, add the addresses */ |
| 718 | for (li = list->next; li != list; li = li->next) { |
| 719 | struct fd_endpoint * ep = (struct fd_endpoint *) li; |
| 720 | |
| 721 | /* Skip v6 addresses for v4 socket */ |
| 722 | if ((target_family == AF_INET) && (ep->sa.sa_family == AF_INET6)) |
| 723 | continue; |
| 724 | |
| 725 | /* Are the flags matching ? */ |
| 726 | if ((val & mask) != (ep->flags & mask)) |
| 727 | continue; |
| 728 | |
| 729 | /* Size of the new SA we are adding (array may contain a mix of sockaddr_in and sockaddr_in6) */ |
| 730 | #ifndef SCTP_USE_MAPPED_ADDRESSES |
| 731 | if (ep->sa.sa_family == AF_INET6) |
| 732 | #else /* SCTP_USE_MAPPED_ADDRESSES */ |
| 733 | if (target_family == AF_INET6) |
| 734 | #endif /* SCTP_USE_MAPPED_ADDRESSES */ |
| 735 | sz = sizeof(sSA6); |
| 736 | else |
| 737 | sz = sizeof(sSA4); |
| 738 | |
| 739 | /* Place where we add the new address */ |
| 740 | ptr.buf = *array + *size; /* place of the new SA */ |
| 741 | |
| 742 | /* Update other information */ |
| 743 | *size += sz; |
| 744 | *addr_count += 1; |
| 745 | |
| 746 | /* And write the addr in the buffer */ |
| 747 | if (sz == sizeof(sSA4)) { |
| 748 | memcpy(ptr.buf, &ep->sin, sz); |
| 749 | ptr.sin->sin_port = port; |
| 750 | } else { |
| 751 | if (ep->sa.sa_family == AF_INET) { /* We must map the address */ |
| 752 | memset(ptr.buf, 0, sz); |
| 753 | ptr.sin6->sin6_family = AF_INET6; |
| 754 | IN6_ADDR_V4MAP( &ptr.sin6->sin6_addr.s6_addr, ep->sin.sin_addr.s_addr ); |
| 755 | } else { |
| 756 | memcpy(ptr.sin6, &ep->sin6, sz); |
| 757 | } |
| 758 | ptr.sin6->sin6_port = port; |
| 759 | } |
| 760 | } |
| 761 | |
| 762 | return 0; |
| 763 | } |
| 764 | |
| 765 | /* Create a socket server and bind it according to daemon s configuration */ |
| 766 | int fd_sctp_create_bind_server( int * sock, int family, struct fd_list * list, uint16_t port ) |
| 767 | { |
| 768 | int bind_default; |
| 769 | |
| 770 | TRACE_ENTRY("%p %i %p %hu", sock, family, list, port); |
| 771 | CHECK_PARAMS(sock); |
| 772 | |
| 773 | /* Create the socket */ |
| 774 | CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) ); |
| 775 | |
| 776 | /* Set pre-binding socket options, including number of streams etc... */ |
| 777 | CHECK_FCT( fd_setsockopt_prebind(*sock) ); |
| 778 | |
| 779 | bind_default = (! list) || (FD_IS_LIST_EMPTY(list)) ; |
| 780 | redo: |
| 781 | if ( bind_default ) { |
| 782 | /* Implicit endpoints : bind to default addresses */ |
| 783 | union { |
| 784 | sSS ss; |
| 785 | sSA sa; |
| 786 | sSA4 sin; |
| 787 | sSA6 sin6; |
| 788 | } s; |
| 789 | |
| 790 | /* 0.0.0.0 and [::] are all zeros */ |
| 791 | memset(&s, 0, sizeof(s)); |
| 792 | |
| 793 | s.sa.sa_family = family; |
| 794 | |
| 795 | if (family == AF_INET) |
| 796 | s.sin.sin_port = htons(port); |
| 797 | else |
| 798 | s.sin6.sin6_port = htons(port); |
| 799 | |
| 800 | CHECK_SYS( bind(*sock, &s.sa, sSAlen(&s)) ); |
| 801 | |
| 802 | } else { |
| 803 | /* Explicit endpoints to bind to from config */ |
| 804 | |
| 805 | sSA * sar = NULL; /* array of addresses */ |
| 806 | size_t sz = 0; /* size of the array */ |
| 807 | int count = 0; /* number of sock addr in the array */ |
| 808 | |
| 809 | /* Create the array of configured addresses */ |
| 810 | CHECK_FCT( add_addresses_from_list_mask((void *)&sar, &sz, &count, family, htons(port), list, EP_FL_CONF, EP_FL_CONF) ); |
| 811 | |
| 812 | if (!count) { |
| 813 | /* None of the addresses in the list came from configuration, we bind to default */ |
| 814 | bind_default = 1; |
| 815 | goto redo; |
| 816 | } |
| 817 | |
| 818 | #if 0 |
| 819 | union { |
| 820 | sSA *sa; |
| 821 | uint8_t *buf; |
| 822 | } ptr; |
| 823 | int i; |
| 824 | ptr.sa = sar; |
| 825 | fd_log_debug("Calling sctp_bindx with the following address array:"); |
| 826 | for (i = 0; i < count; i++) { |
| 827 | TRACE_sSA(FD_LOG_DEBUG, FULL, " - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); |
| 828 | ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6) ; |
| 829 | } |
| 830 | #endif |
| 831 | |
| 832 | /* Bind to this array */ |
| 833 | CHECK_SYS( sctp_bindx(*sock, sar, count, SCTP_BINDX_ADD_ADDR) ); |
| 834 | |
| 835 | /* We don't need sar anymore */ |
| 836 | free(sar); |
| 837 | } |
| 838 | |
| 839 | /* Now, the server is bound, set remaining sockopt */ |
| 840 | CHECK_FCT( fd_setsockopt_postbind(*sock, bind_default) ); |
| 841 | |
| 842 | /* Debug: show all local listening addresses */ |
| 843 | #if 0 |
| 844 | sSA *sar; |
| 845 | union { |
| 846 | sSA *sa; |
| 847 | uint8_t *buf; |
| 848 | } ptr; |
| 849 | int sz; |
| 850 | |
| 851 | CHECK_SYS( sz = sctp_getladdrs(*sock, 0, &sar) ); |
| 852 | |
| 853 | fd_log_debug("SCTP server bound on :"); |
| 854 | for (ptr.sa = sar; sz-- > 0; ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6)) { |
| 855 | TRACE_sSA(FD_LOG_DEBUG, FULL, " - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); |
| 856 | } |
| 857 | sctp_freeladdrs(sar); |
| 858 | #endif |
| 859 | |
| 860 | return 0; |
| 861 | } |
| 862 | |
| 863 | /* Allow clients connections on server sockets */ |
| 864 | int fd_sctp_listen( int sock ) |
| 865 | { |
| 866 | TRACE_ENTRY("%d", sock); |
| 867 | CHECK_SYS( listen(sock, 5) ); |
| 868 | return 0; |
| 869 | } |
| 870 | |
| 871 | /* Create a client socket and connect to remote server */ |
| 872 | int fd_sctp_client( int *sock, int no_ip6, uint16_t port, struct fd_list * list ) |
| 873 | { |
| 874 | int family; |
| 875 | union { |
| 876 | uint8_t *buf; |
| 877 | sSA *sa; |
| 878 | } sar; |
| 879 | size_t size = 0; |
| 880 | int count = 0; |
| 881 | int ret; |
| 882 | |
| 883 | sar.buf = NULL; |
| 884 | |
| 885 | TRACE_ENTRY("%p %i %hu %p", sock, no_ip6, port, list); |
| 886 | CHECK_PARAMS( sock && list && (!FD_IS_LIST_EMPTY(list)) ); |
| 887 | |
| 888 | if (no_ip6) { |
| 889 | family = AF_INET; |
| 890 | } else { |
| 891 | family = AF_INET6; |
| 892 | } |
| 893 | |
| 894 | /* Create the socket */ |
| 895 | CHECK_SYS( *sock = socket(family, SOCK_STREAM, IPPROTO_SCTP) ); |
| 896 | |
| 897 | /* Cleanup if we are cancelled */ |
| 898 | pthread_cleanup_push(fd_cleanup_socket, sock); |
| 899 | |
| 900 | /* Set the socket options */ |
| 901 | CHECK_FCT_DO( ret = fd_setsockopt_prebind(*sock), goto out ); |
| 902 | |
| 903 | /* Create the array of addresses, add first the configured addresses, then the discovered, then the other ones */ |
| 904 | CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF, EP_FL_CONF ), goto out ); |
| 905 | CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, EP_FL_DISC ), goto out ); |
| 906 | CHECK_FCT_DO( ret = add_addresses_from_list_mask(&sar.buf, &size, &count, family, htons(port), list, EP_FL_CONF | EP_FL_DISC, 0 ), goto out ); |
| 907 | |
| 908 | /* Try connecting */ |
| 909 | LOG_A("Attempting SCTP connection (%d addresses attempted) ", count); |
| 910 | |
| 911 | #if 0 |
| 912 | /* Dump the SAs */ |
| 913 | union { |
| 914 | uint8_t *buf; |
| 915 | sSA *sa; |
| 916 | sSA4 *sin; |
| 917 | sSA6 *sin6; |
| 918 | } ptr; |
| 919 | int i; |
| 920 | ptr.buf = sar.buf; |
| 921 | for (i=0; i< count; i++) { |
| 922 | TRACE_sSA(FD_LOG_DEBUG, FULL, " - ", ptr.sa, NI_NUMERICHOST | NI_NUMERICSERV, "" ); |
| 923 | ptr.buf += (ptr.sa->sa_family == AF_INET) ? sizeof(sSA4) : sizeof(sSA6); |
| 924 | } |
| 925 | #endif |
| 926 | |
| 927 | /* Bug in some Linux kernel, the sctp_connectx is not a cancellation point. To avoid blocking freeDiameter, we allow async cancel here */ |
| 928 | pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); |
| 929 | #ifdef SCTP_CONNECTX_4_ARGS |
| 930 | ret = sctp_connectx(*sock, sar.sa, count, NULL); |
| 931 | #else /* SCTP_CONNECTX_4_ARGS */ |
| 932 | ret = sctp_connectx(*sock, sar.sa, count); |
| 933 | #endif /* SCTP_CONNECTX_4_ARGS */ |
| 934 | /* back to normal cancelation behabior */ |
| 935 | pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); |
| 936 | |
| 937 | if (ret < 0) { |
| 938 | ret = errno; |
| 939 | /* Some errors are expected, we log at different level */ |
| 940 | LOG_A("sctp_connectx returned an error: %s", strerror(ret)); |
| 941 | goto out; |
| 942 | } |
| 943 | |
| 944 | free(sar.buf); sar.buf = NULL; |
| 945 | |
| 946 | /* Set the remaining sockopts */ |
| 947 | CHECK_FCT_DO( ret = fd_setsockopt_postbind(*sock, 1), |
| 948 | { |
| 949 | CHECK_SYS_DO( shutdown(*sock, SHUT_RDWR), /* continue */ ); |
| 950 | } ); |
| 951 | |
| 952 | out: |
| 953 | ; |
| 954 | pthread_cleanup_pop(0); |
| 955 | |
| 956 | if (ret) { |
| 957 | if (*sock > 0) { |
| 958 | CHECK_SYS_DO( close(*sock), /* continue */ ); |
| 959 | *sock = -1; |
| 960 | } |
| 961 | free(sar.buf); |
| 962 | } |
| 963 | return ret; |
| 964 | } |
| 965 | |
| 966 | /* Retrieve streams information from a connected association -- optionally provide the primary address */ |
| 967 | int fd_sctp_get_str_info( int sock, uint16_t *in, uint16_t *out, sSS *primary ) |
| 968 | { |
| 969 | struct sctp_status status; |
| 970 | socklen_t sz = sizeof(status); |
| 971 | |
| 972 | TRACE_ENTRY("%d %p %p %p", sock, in, out, primary); |
| 973 | CHECK_PARAMS( (sock > 0) && in && out ); |
| 974 | |
| 975 | /* Read the association parameters */ |
| 976 | memset(&status, 0, sizeof(status)); |
| 977 | CHECK_SYS( getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz) ); |
| 978 | if (sz != sizeof(status)) |
| 979 | { |
| 980 | TRACE_DEBUG(INFO, "Invalid size of socket option: %d / %zd", sz, sizeof(status)); |
| 981 | return ENOTSUP; |
| 982 | } |
| 983 | #if 0 |
| 984 | char sa_buf[sSA_DUMP_STRLEN]; |
| 985 | fd_sa_sdump_numeric(sa_buf, &status.sstat_primary.spinfo_address); |
| 986 | fd_log_debug( "SCTP_STATUS : sstat_state : %i" , status.sstat_state); |
| 987 | fd_log_debug( " sstat_rwnd : %u" , status.sstat_rwnd); |
| 988 | fd_log_debug( " sstat_unackdata : %hu", status.sstat_unackdata); |
| 989 | fd_log_debug( " sstat_penddata : %hu", status.sstat_penddata); |
| 990 | fd_log_debug( " sstat_instrms : %hu", status.sstat_instrms); |
| 991 | fd_log_debug( " sstat_outstrms : %hu", status.sstat_outstrms); |
| 992 | fd_log_debug( " sstat_fragmentation_point : %u" , status.sstat_fragmentation_point); |
| 993 | fd_log_debug( " sstat_primary.spinfo_address : %s" , sa_buf); |
| 994 | fd_log_debug( " sstat_primary.spinfo_state : %d" , status.sstat_primary.spinfo_state); |
| 995 | fd_log_debug( " sstat_primary.spinfo_cwnd : %u" , status.sstat_primary.spinfo_cwnd); |
| 996 | fd_log_debug( " sstat_primary.spinfo_srtt : %u" , status.sstat_primary.spinfo_srtt); |
| 997 | fd_log_debug( " sstat_primary.spinfo_rto : %u" , status.sstat_primary.spinfo_rto); |
| 998 | fd_log_debug( " sstat_primary.spinfo_mtu : %u" , status.sstat_primary.spinfo_mtu); |
| 999 | #endif /* 0 */ |
| 1000 | |
| 1001 | *in = status.sstat_instrms; |
| 1002 | *out = status.sstat_outstrms; |
| 1003 | |
| 1004 | if (primary) |
| 1005 | memcpy(primary, &status.sstat_primary.spinfo_address, sizeof(sSS)); |
| 1006 | |
| 1007 | return 0; |
| 1008 | } |
| 1009 | |
| 1010 | /* Get the list of remote endpoints of the socket */ |
| 1011 | int fd_sctp_get_remote_ep(int sock, struct fd_list * list) |
| 1012 | { |
| 1013 | union { |
| 1014 | sSA *sa; |
| 1015 | uint8_t *buf; |
| 1016 | } ptr; |
| 1017 | |
| 1018 | sSA * data = NULL; |
| 1019 | int count; |
| 1020 | |
| 1021 | TRACE_ENTRY("%d %p", sock, list); |
| 1022 | CHECK_PARAMS(list); |
| 1023 | |
| 1024 | /* Read the list on the socket */ |
| 1025 | CHECK_SYS( count = sctp_getpaddrs(sock, 0, &data) ); |
| 1026 | ptr.sa = data; |
| 1027 | |
| 1028 | while (count) { |
| 1029 | socklen_t sl; |
| 1030 | switch (ptr.sa->sa_family) { |
| 1031 | case AF_INET: sl = sizeof(sSA4); break; |
| 1032 | case AF_INET6: sl = sizeof(sSA6); break; |
| 1033 | default: |
| 1034 | TRACE_DEBUG(INFO, "Unknown address family returned in sctp_getpaddrs: %d, skip", ptr.sa->sa_family); |
| 1035 | /* There is a bug in current Linux kernel: http://www.spinics.net/lists/linux-sctp/msg00760.html */ |
| 1036 | goto stop; |
| 1037 | } |
| 1038 | |
| 1039 | CHECK_FCT( fd_ep_add_merge( list, ptr.sa, sl, EP_FL_LL ) ); |
| 1040 | ptr.buf += sl; |
| 1041 | count --; |
| 1042 | } |
| 1043 | stop: |
| 1044 | /* Free the list */ |
| 1045 | sctp_freepaddrs(data); |
| 1046 | |
| 1047 | /* Now get the primary address, the add function will take care of merging with existing entry */ |
| 1048 | { |
| 1049 | |
| 1050 | struct sctp_status status; |
| 1051 | socklen_t sz = sizeof(status); |
| 1052 | int ret; |
| 1053 | |
| 1054 | memset(&status, 0, sizeof(status)); |
| 1055 | /* Attempt to use SCTP_STATUS message to retrieve the primary address */ |
| 1056 | CHECK_SYS_DO( ret = getsockopt(sock, IPPROTO_SCTP, SCTP_STATUS, &status, &sz), /* continue */); |
| 1057 | if (sz != sizeof(status)) |
| 1058 | ret = -1; |
| 1059 | sz = sizeof(sSS); |
| 1060 | if (ret < 0) |
| 1061 | { |
| 1062 | /* Fallback to getsockname -- not recommended by draft-ietf-tsvwg-sctpsocket-19#section-7.4 */ |
| 1063 | CHECK_SYS(getpeername(sock, (sSA *)&status.sstat_primary.spinfo_address, &sz)); |
| 1064 | } |
| 1065 | |
| 1066 | CHECK_FCT( fd_ep_add_merge( list, (sSA *)&status.sstat_primary.spinfo_address, sz, EP_FL_PRIMARY ) ); |
| 1067 | } |
| 1068 | |
| 1069 | /* Done! */ |
| 1070 | return 0; |
| 1071 | } |
| 1072 | |
| 1073 | /* Send a vector over a specified stream */ |
| 1074 | ssize_t fd_sctp_sendstrv(struct cnxctx * conn, uint16_t strid, const struct iovec *iov, int iovcnt) |
| 1075 | { |
| 1076 | struct msghdr mhdr; |
| 1077 | struct cmsghdr *hdr; |
| 1078 | #ifdef OLD_SCTP_SOCKET_API |
| 1079 | struct sctp_sndrcvinfo *sndrcv; |
| 1080 | uint8_t anci[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))]; |
| 1081 | #else /* OLD_SCTP_SOCKET_API */ |
| 1082 | struct sctp_sndinfo *sndinf; |
| 1083 | uint8_t anci[CMSG_SPACE(sizeof(struct sctp_sndinfo))]; |
| 1084 | #endif /* OLD_SCTP_SOCKET_API */ |
| 1085 | ssize_t ret; |
| 1086 | struct timespec ts, now; |
| 1087 | |
| 1088 | TRACE_ENTRY("%p %hu %p %d", conn, strid, iov, iovcnt); |
| 1089 | CHECK_PARAMS_DO(conn && iov && iovcnt, { errno = EINVAL; return -1; } ); |
| 1090 | CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &ts), return -1 ); |
| 1091 | |
| 1092 | memset(&mhdr, 0, sizeof(mhdr)); |
| 1093 | memset(&anci, 0, sizeof(anci)); |
| 1094 | |
| 1095 | /* Anciliary data: specify SCTP stream */ |
| 1096 | hdr = (struct cmsghdr *)anci; |
| 1097 | hdr->cmsg_len = sizeof(anci); |
| 1098 | hdr->cmsg_level = IPPROTO_SCTP; |
| 1099 | #ifdef OLD_SCTP_SOCKET_API |
| 1100 | hdr->cmsg_type = SCTP_SNDRCV; |
| 1101 | sndrcv = (struct sctp_sndrcvinfo *)CMSG_DATA(hdr); |
| 1102 | sndrcv->sinfo_stream = strid; |
| 1103 | #else /* OLD_SCTP_SOCKET_API */ |
| 1104 | hdr->cmsg_type = SCTP_SNDINFO; |
| 1105 | sndinf = (struct sctp_sndinfo *)CMSG_DATA(hdr); |
| 1106 | sndinf->snd_sid = strid; |
| 1107 | #endif /* OLD_SCTP_SOCKET_API */ |
| 1108 | /* note : we could store other data also, for example in .sinfo_ppid for remote peer or in .sinfo_context for errors. */ |
| 1109 | |
| 1110 | /* We don't use mhdr.msg_name here; it could be used to specify an address different from the primary */ |
| 1111 | |
| 1112 | mhdr.msg_iov = (struct iovec *)iov; |
| 1113 | mhdr.msg_iovlen = iovcnt; |
| 1114 | |
| 1115 | mhdr.msg_control = anci; |
| 1116 | mhdr.msg_controllen = sizeof(anci); |
| 1117 | |
| 1118 | TRACE_DEBUG(FULL, "Sending %d chunks of data (first:%zdb) on stream %hu of socket %d", iovcnt, iov[0].iov_len, strid, conn->cc_socket); |
| 1119 | again: |
| 1120 | ret = sendmsg(conn->cc_socket, &mhdr, 0); |
| 1121 | /* Handle special case of timeout */ |
| 1122 | if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) { |
| 1123 | pthread_testcancel(); |
| 1124 | /* Check how much time we were blocked for this sending. */ |
| 1125 | CHECK_SYS_DO( clock_gettime(CLOCK_REALTIME, &now), return -1 ); |
| 1126 | if ( ((now.tv_sec - ts.tv_sec) * 1000 + ((now.tv_nsec - ts.tv_nsec) / 1000000L)) > MAX_HOTL_BLOCKING_TIME) { |
| 1127 | LOG_D("Unable to send any data for %dms, closing the connection", MAX_HOTL_BLOCKING_TIME); |
| 1128 | } else if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) { |
| 1129 | goto again; /* don't care, just ignore */ |
| 1130 | } |
| 1131 | |
| 1132 | /* propagate the error */ |
| 1133 | errno = -ret; |
| 1134 | ret = -1; |
| 1135 | } |
| 1136 | |
| 1137 | CHECK_SYS_DO( ret, ); /* for tracing error only */ |
| 1138 | |
| 1139 | return ret; |
| 1140 | } |
| 1141 | |
| 1142 | /* Receive the next data from the socket, or next notification */ |
| 1143 | int fd_sctp_recvmeta(struct cnxctx * conn, uint16_t * strid, uint8_t ** buf, size_t * len, int *event) |
| 1144 | { |
| 1145 | ssize_t ret = 0; |
| 1146 | struct msghdr mhdr; |
| 1147 | char ancidata[ CMSG_BUF_LEN ]; |
| 1148 | struct iovec iov; |
| 1149 | uint8_t *data = NULL; |
| 1150 | size_t bufsz = 0, datasize = 0; |
| 1151 | size_t mempagesz = sysconf(_SC_PAGESIZE); /* We alloc buffer by memory pages for efficiency */ |
| 1152 | int timedout = 0; |
| 1153 | |
| 1154 | TRACE_ENTRY("%p %p %p %p %p", conn, strid, buf, len, event); |
| 1155 | CHECK_PARAMS( conn && buf && len && event ); |
| 1156 | |
| 1157 | /* Cleanup out parameters */ |
| 1158 | *buf = NULL; |
| 1159 | *len = 0; |
| 1160 | *event = 0; |
| 1161 | |
| 1162 | /* Prepare header for receiving message */ |
| 1163 | memset(&mhdr, 0, sizeof(mhdr)); |
| 1164 | mhdr.msg_iov = &iov; |
| 1165 | mhdr.msg_iovlen = 1; |
| 1166 | mhdr.msg_control = &ancidata; |
| 1167 | mhdr.msg_controllen = sizeof(ancidata); |
| 1168 | |
| 1169 | next_message: |
| 1170 | datasize = 0; |
| 1171 | |
| 1172 | /* We will loop while all data is not received. */ |
| 1173 | incomplete: |
| 1174 | while (datasize >= bufsz ) { |
| 1175 | /* The buffer is full, enlarge it */ |
| 1176 | bufsz += mempagesz; |
| 1177 | CHECK_MALLOC( data = realloc(data, bufsz ) ); |
| 1178 | } |
| 1179 | /* the new data will be received following the preceding */ |
| 1180 | memset(&iov, 0, sizeof(iov)); |
| 1181 | iov.iov_base = data + datasize ; |
| 1182 | iov.iov_len = bufsz - datasize; |
| 1183 | |
| 1184 | /* Receive data from the socket */ |
| 1185 | again: |
| 1186 | pthread_cleanup_push(free, data); |
| 1187 | ret = recvmsg(conn->cc_socket, &mhdr, 0); |
| 1188 | pthread_testcancel(); |
| 1189 | pthread_cleanup_pop(0); |
| 1190 | |
| 1191 | /* First, handle timeouts (same as fd_cnx_s_recv) */ |
| 1192 | if ((ret < 0) && ((errno == EAGAIN) || (errno == EINTR))) { |
| 1193 | if (! fd_cnx_teststate(conn, CC_STATUS_CLOSING )) |
| 1194 | goto again; /* don't care, just ignore */ |
| 1195 | if (!timedout) { |
| 1196 | timedout ++; /* allow for one timeout while closing */ |
| 1197 | goto again; |
| 1198 | } |
| 1199 | /* fallback to normal handling */ |
| 1200 | } |
| 1201 | |
| 1202 | /* Handle errors */ |
| 1203 | if (ret <= 0) { /* Socket timedout, closed, or an error occurred */ |
| 1204 | CHECK_SYS_DO(ret, /* to log in case of error */); |
| 1205 | free(data); |
| 1206 | *event = FDEVP_CNX_ERROR; |
| 1207 | return 0; |
| 1208 | } |
| 1209 | |
| 1210 | /* Update the size of data we received */ |
| 1211 | datasize += ret; |
| 1212 | |
| 1213 | /* SCTP provides an indication when we received a full record; loop if it is not the case */ |
| 1214 | if ( ! (mhdr.msg_flags & MSG_EOR) ) { |
| 1215 | goto incomplete; |
| 1216 | } |
| 1217 | |
| 1218 | /* Handle the case where the data received is a notification */ |
| 1219 | if (mhdr.msg_flags & MSG_NOTIFICATION) { |
| 1220 | union sctp_notification * notif = (union sctp_notification *) data; |
| 1221 | |
| 1222 | TRACE_DEBUG(FULL, "Received %zdb data of notification on socket %d", datasize, conn->cc_socket); |
| 1223 | |
| 1224 | switch (notif->sn_header.sn_type) { |
| 1225 | |
| 1226 | case SCTP_ASSOC_CHANGE: /* We should not receive these as we did not subscribe for it */ |
| 1227 | TRACE_DEBUG(FULL, "Received SCTP_ASSOC_CHANGE notification"); |
| 1228 | TRACE_DEBUG(ANNOYING, " state : %hu", notif->sn_assoc_change.sac_state); |
| 1229 | TRACE_DEBUG(ANNOYING, " error : %hu", notif->sn_assoc_change.sac_error); |
| 1230 | TRACE_DEBUG(ANNOYING, " instr : %hu", notif->sn_assoc_change.sac_inbound_streams); |
| 1231 | TRACE_DEBUG(ANNOYING, " outstr : %hu", notif->sn_assoc_change.sac_outbound_streams); |
| 1232 | |
| 1233 | *event = FDEVP_CNX_EP_CHANGE; |
| 1234 | break; |
| 1235 | |
| 1236 | case SCTP_PEER_ADDR_CHANGE: |
| 1237 | TRACE_DEBUG(FULL, "Received SCTP_PEER_ADDR_CHANGE notification"); |
| 1238 | /* TRACE_sSA(FD_LOG_DEBUG, ANNOYING, " intf_change : ", &(notif->sn_paddr_change.spc_aaddr), NI_NUMERICHOST | NI_NUMERICSERV, "" ); */ |
| 1239 | TRACE_DEBUG(ANNOYING, " state : %d", notif->sn_paddr_change.spc_state); |
| 1240 | TRACE_DEBUG(ANNOYING, " error : %d", notif->sn_paddr_change.spc_error); |
| 1241 | |
| 1242 | *event = FDEVP_CNX_EP_CHANGE; |
| 1243 | break; |
| 1244 | |
| 1245 | case SCTP_REMOTE_ERROR: |
| 1246 | TRACE_DEBUG(FULL, "Received SCTP_REMOTE_ERROR notification"); |
| 1247 | TRACE_DEBUG(ANNOYING, " err : %hu", ntohs(notif->sn_remote_error.sre_error)); |
| 1248 | TRACE_DEBUG(ANNOYING, " len : %hu", ntohs(notif->sn_remote_error.sre_length)); |
| 1249 | |
| 1250 | *event = FDEVP_CNX_ERROR; |
| 1251 | break; |
| 1252 | |
| 1253 | #ifdef OLD_SCTP_SOCKET_API |
| 1254 | case SCTP_SEND_FAILED: |
| 1255 | TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED notification"); |
| 1256 | TRACE_DEBUG(ANNOYING, " len : %hu", notif->sn_send_failed.ssf_length); |
| 1257 | TRACE_DEBUG(ANNOYING, " err : %d", notif->sn_send_failed.ssf_error); |
| 1258 | |
| 1259 | *event = FDEVP_CNX_ERROR; |
| 1260 | break; |
| 1261 | #else /* OLD_SCTP_SOCKET_API */ |
| 1262 | case SCTP_SEND_FAILED_EVENT: |
| 1263 | TRACE_DEBUG(FULL, "Received SCTP_SEND_FAILED_EVENT notification"); |
| 1264 | *event = FDEVP_CNX_ERROR; |
| 1265 | break; |
| 1266 | #endif /* OLD_SCTP_SOCKET_API */ |
| 1267 | case SCTP_SHUTDOWN_EVENT: |
| 1268 | TRACE_DEBUG(FULL, "Received SCTP_SHUTDOWN_EVENT notification"); |
| 1269 | |
| 1270 | *event = FDEVP_CNX_SHUTDOWN; |
| 1271 | break; |
| 1272 | |
| 1273 | #ifndef OLD_SCTP_SOCKET_API |
| 1274 | case SCTP_NOTIFICATIONS_STOPPED_EVENT: |
| 1275 | TRACE_DEBUG(INFO, "Received SCTP_NOTIFICATIONS_STOPPED_EVENT notification, marking the association in error state"); |
| 1276 | *event = FDEVP_CNX_ERROR; |
| 1277 | break; |
| 1278 | #endif /* OLD_SCTP_SOCKET_API */ |
| 1279 | |
| 1280 | default: |
| 1281 | TRACE_DEBUG(FULL, "Received unknown notification %d, ignored", notif->sn_header.sn_type); |
| 1282 | goto next_message; |
| 1283 | } |
| 1284 | |
| 1285 | free(data); |
| 1286 | return 0; |
| 1287 | } |
| 1288 | |
| 1289 | /* From this point, we have received a message */ |
| 1290 | *event = FDEVP_CNX_MSG_RECV; |
| 1291 | *buf = data; |
| 1292 | *len = datasize; |
| 1293 | |
| 1294 | if (strid) { |
| 1295 | struct cmsghdr *hdr; |
| 1296 | #ifdef OLD_SCTP_SOCKET_API |
| 1297 | struct sctp_sndrcvinfo *sndrcv; |
| 1298 | #else /* OLD_SCTP_SOCKET_API */ |
| 1299 | struct sctp_rcvinfo *rcvinf; |
| 1300 | #endif /* OLD_SCTP_SOCKET_API */ |
| 1301 | |
| 1302 | /* Handle the anciliary data */ |
| 1303 | for (hdr = CMSG_FIRSTHDR(&mhdr); hdr; hdr = CMSG_NXTHDR(&mhdr, hdr)) { |
| 1304 | |
| 1305 | /* We deal only with anciliary data at SCTP level */ |
| 1306 | if (hdr->cmsg_level != IPPROTO_SCTP) { |
| 1307 | TRACE_DEBUG(FULL, "Received some anciliary data at level %d, skipped", hdr->cmsg_level); |
| 1308 | continue; |
| 1309 | } |
| 1310 | |
| 1311 | #ifdef OLD_SCTP_SOCKET_API |
| 1312 | /* Also only interested in SCTP_SNDRCV message for the moment */ |
| 1313 | if (hdr->cmsg_type != SCTP_SNDRCV) { |
| 1314 | TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type); |
| 1315 | continue; |
| 1316 | } |
| 1317 | |
| 1318 | sndrcv = (struct sctp_sndrcvinfo *) CMSG_DATA(hdr); |
| 1319 | if (TRACE_BOOL(ANNOYING)) { |
| 1320 | fd_log_debug( "Anciliary block IPPROTO_SCTP / SCTP_SNDRCV"); |
| 1321 | fd_log_debug( " sinfo_stream : %hu", sndrcv->sinfo_stream); |
| 1322 | fd_log_debug( " sinfo_ssn : %hu", sndrcv->sinfo_ssn); |
| 1323 | fd_log_debug( " sinfo_flags : %hu", sndrcv->sinfo_flags); |
| 1324 | /* fd_log_debug( " sinfo_pr_policy : %hu", sndrcv->sinfo_pr_policy); */ |
| 1325 | fd_log_debug( " sinfo_ppid : %u" , sndrcv->sinfo_ppid); |
| 1326 | fd_log_debug( " sinfo_context : %u" , sndrcv->sinfo_context); |
| 1327 | /* fd_log_debug( " sinfo_pr_value : %u" , sndrcv->sinfo_pr_value); */ |
| 1328 | fd_log_debug( " sinfo_tsn : %u" , sndrcv->sinfo_tsn); |
| 1329 | fd_log_debug( " sinfo_cumtsn : %u" , sndrcv->sinfo_cumtsn); |
| 1330 | } |
| 1331 | |
| 1332 | *strid = sndrcv->sinfo_stream; |
| 1333 | #else /* OLD_SCTP_SOCKET_API */ |
| 1334 | /* Also only interested in SCTP_RCVINFO message for the moment */ |
| 1335 | if (hdr->cmsg_type != SCTP_RCVINFO) { |
| 1336 | TRACE_DEBUG(FULL, "Anciliary block IPPROTO_SCTP / %d, skipped", hdr->cmsg_type); |
| 1337 | continue; |
| 1338 | } |
| 1339 | |
| 1340 | rcvinf = (struct sctp_rcvinfo *) CMSG_DATA(hdr); |
| 1341 | |
| 1342 | *strid = rcvinf->rcv_sid; |
| 1343 | #endif /* OLD_SCTP_SOCKET_API */ |
| 1344 | |
| 1345 | |
| 1346 | } |
| 1347 | TRACE_DEBUG(FULL, "Received %zdb data on socket %d, stream %hu", datasize, conn->cc_socket, *strid); |
| 1348 | } else { |
| 1349 | TRACE_DEBUG(FULL, "Received %zdb data on socket %d (stream ignored)", datasize, conn->cc_socket); |
| 1350 | } |
| 1351 | |
| 1352 | return 0; |
| 1353 | } |