/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /**@CFILE tport_type_sctp.c Transport using SCTP. * * See tport.docs for more detailed description of tport interface. * * @RFC4168. * * @author Pekka Pessi * @author Martti Mela * * @date Created: Fri Mar 24 08:45:49 EET 2006 ppessi * @date Original Created: Thu Jul 20 12:54:32 2000 ppessi */ #include "config.h" #if HAVE_SCTP #include "tport_internal.h" #if HAVE_NETINET_SCTP_H #include #endif #include #include #include #include #include #include /* ---------------------------------------------------------------------- */ /* SCTP */ #undef MAX_STREAMS #define MAX_STREAMS MAX_STREAMS /* Missing socket symbols */ #ifndef SOL_SCTP #define SOL_SCTP IPPROTO_SCTP #endif enum { MAX_STREAMS = 1 }; typedef struct tport_sctp_t { tport_t sctp_base[1]; msg_t *sctp_recv[MAX_STREAMS]; struct sctp_send { msg_t *ss_msg; msg_iovec_t *ss_unsent; /**< Pointer to first unsent iovec */ unsigned ss_unsentlen; /**< Number of unsent iovecs */ msg_iovec_t *ss_iov; /**< Iovecs allocated for sending */ unsigned ss_iovlen; /**< Number of allocated iovecs */ } sctp_send[MAX_STREAMS]; } tport_sctp_t; #define TP_SCTP_MSG_MAX (65536) static int tport_sctp_init_primary(tport_primary_t *, tp_name_t tpn[1], su_addrinfo_t *, tagi_t const *, char const **return_culprit); static int tport_sctp_init_client(tport_primary_t *, tp_name_t tpn[1], su_addrinfo_t *, tagi_t const *, char const **return_culprit); static int tport_sctp_init_secondary(tport_t *self, int socket, int accepted, char const **return_reason); static int tport_sctp_init_socket(tport_primary_t *pri, int socket, char const **return_reason); static int tport_recv_sctp(tport_t *self); static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg, msg_iovec_t iov[], size_t iovused); static int tport_sctp_next_timer(tport_t *self, su_time_t *, char const **); static void tport_sctp_timer(tport_t *self, su_time_t); tport_vtable_t const tport_sctp_client_vtable = { "sctp", tport_type_client, sizeof (tport_primary_t), tport_sctp_init_client, NULL, tport_accept, NULL, sizeof (tport_t), tport_sctp_init_secondary, NULL, NULL, NULL, NULL, tport_recv_sctp, tport_send_sctp, NULL, NULL, NULL, NULL, tport_sctp_next_timer, tport_sctp_timer, }; tport_vtable_t const tport_sctp_vtable = { "sctp", tport_type_local, sizeof (tport_primary_t), tport_sctp_init_primary, NULL, tport_accept, NULL, sizeof (tport_t), tport_sctp_init_secondary, NULL, NULL, NULL, NULL, tport_recv_sctp, tport_send_sctp, NULL, NULL, NULL, NULL, tport_sctp_next_timer, tport_sctp_timer, }; static int tport_sctp_init_primary(tport_primary_t *pri, tp_name_t tpn[1], su_addrinfo_t *ai, tagi_t const *tags, char const **return_culprit) { int socket; if (pri->pri_params->tpp_mtu > TP_SCTP_MSG_MAX) pri->pri_params->tpp_mtu = TP_SCTP_MSG_MAX; socket = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (socket == INVALID_SOCKET) return *return_culprit = "socket", -1; if (tport_sctp_init_socket(pri, socket, return_culprit) < 0) return -1; return tport_stream_init_primary(pri, socket, tpn, ai, tags, return_culprit); } static int tport_sctp_init_client(tport_primary_t *pri, tp_name_t tpn[1], su_addrinfo_t *ai, tagi_t const *tags, char const **return_culprit) { if (pri->pri_params->tpp_mtu > TP_SCTP_MSG_MAX) pri->pri_params->tpp_mtu = TP_SCTP_MSG_MAX; return tport_tcp_init_client(pri, tpn, ai, tags, return_culprit); } static int tport_sctp_init_secondary(tport_t *self, int socket, int accepted, char const **return_reason) { self->tp_has_connection = 1; if (accepted) { /* Accepted socket inherit the init information from listen socket */ return 0; } else { return tport_sctp_init_socket(self->tp_pri, socket, return_reason); } } /** Initialize a SCTP socket */ static int tport_sctp_init_socket(tport_primary_t *pri, int socket, char const **return_reason) { struct sctp_initmsg initmsg = { 0 }; initmsg.sinit_num_ostreams = MAX_STREAMS; initmsg.sinit_max_instreams = MAX_STREAMS; if (setsockopt(socket, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof initmsg) < 0) return *return_reason = "SCTP_INITMSG", -1; return 0; } /** Receive data available on the socket. * * @retval -1 error * @retval 0 end-of-stream * @retval 1 normal receive * @retval 2 incomplete recv, recv again */ static int tport_recv_sctp(tport_t *self) { msg_t *msg; ssize_t N, veclen; msg_iovec_t iovec[2] = {{ 0 }}; char sctp_buf[TP_SCTP_MSG_MAX]; iovec[0].mv_base = sctp_buf; iovec[0].mv_len = sizeof(sctp_buf); N = su_vrecv(self->tp_socket, iovec, 1, 0, NULL, NULL); if (N == SOCKET_ERROR) { return su_is_blocking(su_errno()) ? 1 : -1; } if (N == 0) { if (self->tp_msg) msg_recv_commit(self->tp_msg, 0, 1); return 0; /* End of stream */ } tport_recv_bytes(self, N, N); veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0); if (veclen < 0) return -1; assert(veclen == 1); assert(iovec[0].mv_len == N); msg = self->tp_msg; msg_set_address(msg, self->tp_addr, self->tp_addrlen); memcpy(iovec[0].mv_base, sctp_buf, iovec[0].mv_len); if (self->tp_master->mr_dump_file) tport_dump_iovec(self, msg, N, iovec, veclen, "recv", "from"); msg_recv_commit(msg, N, 0); /* Mark buffer as used */ return 2; } static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg, msg_iovec_t iov[], size_t iovused) { return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0); } /** Calculate tick timer if send is pending. */ int tport_next_sctp_send_tick(tport_t *self, su_time_t *return_target, char const **return_why) { unsigned timeout = 100; /* Retry 10 times a second... */ if (tport_has_queued(self)) { su_time_t ntime = su_time_add(self->tp_ktime, timeout); if (su_time_cmp(ntime, *return_target) < 0) *return_target = ntime, *return_why = "send tick"; } return 0; } /** Tick timer if send is pending */ void tport_sctp_send_tick_timer(tport_t *self, su_time_t now) { unsigned timeout = 100; /* Send timeout */ if (tport_has_queued(self) && su_time_cmp(su_time_add(self->tp_ktime, timeout), now) < 0) { uint64_t bytes = self->tp_stats.sent_bytes; su_time_t stime = self->tp_stime; tport_send_queue(self); if (self->tp_stats.sent_bytes == bytes) self->tp_stime = stime; /* Restore send timestamp */ } } /** Calculate next timer for SCTP. */ int tport_sctp_next_timer(tport_t *self, su_time_t *return_target, char const **return_why) { return tport_next_recv_timeout(self, return_target, return_why) | tport_next_sctp_send_tick(self, return_target, return_why); } /** SCTP timer. */ void tport_sctp_timer(tport_t *self, su_time_t now) { tport_sctp_send_tick_timer(self, now); tport_recv_timeout_timer(self, now); tport_base_timer(self, now); } #endif