tport_type_sctp.c 9.27 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2006 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
 * 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.
 *
29 30
 * @RFC4168.
 *
31 32 33 34 35 36 37 38 39
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Martti Mela <Martti.Mela@nokia.com>
 *
 * @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"

40 41
#if HAVE_SCTP

42 43 44 45 46 47 48 49 50 51 52
#include "tport_internal.h"

#if HAVE_NETINET_SCTP_H
#include <netinet/sctp.h>
#endif

#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <limits.h>
53
#include <string.h>
54 55 56 57

/* ---------------------------------------------------------------------- */
/* SCTP */

58 59 60
#undef MAX_STREAMS
#define MAX_STREAMS MAX_STREAMS

Pekka Pessi's avatar
Pekka Pessi committed
61 62 63 64 65
/* Missing socket symbols */
#ifndef SOL_SCTP
#define SOL_SCTP IPPROTO_SCTP
#endif

66 67 68 69
enum { MAX_STREAMS = 1 };
typedef struct tport_sctp_t
{
  tport_t sctp_base[1];
70

71 72 73 74 75 76 77 78 79 80
  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;

81
#define TP_SCTP_MSG_MAX (65536)
82

83 84
static int tport_sctp_init_primary(tport_primary_t *,
				   tp_name_t tpn[1],
85 86
				   su_addrinfo_t *, tagi_t const *,
				   char const **return_culprit);
87 88
static int tport_sctp_init_client(tport_primary_t *,
				  tp_name_t tpn[1],
89 90
				  su_addrinfo_t *, tagi_t const *,
				  char const **return_culprit);
91 92
static int tport_sctp_init_secondary(tport_t *self, int socket, int accepted,
				     char const **return_reason);
93
static int tport_sctp_init_socket(tport_primary_t *pri,
94 95
				  int socket,
				  char const **return_reason);
96
static int tport_recv_sctp(tport_t *self);
97 98
static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg,
			       msg_iovec_t iov[], size_t iovused);
99

100 101 102
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);

103
tport_vtable_t const tport_sctp_client_vtable =
104
{
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
  /* vtp_name 		     */ "sctp",
  /* vtp_public              */ tport_type_client,
  /* vtp_pri_size            */ sizeof (tport_primary_t),
  /* vtp_init_primary        */ tport_sctp_init_client,
  /* vtp_deinit_primary      */ NULL,
  /* vtp_wakeup_pri          */ tport_accept,
  /* vtp_connect             */ NULL,
  /* vtp_secondary_size      */ sizeof (tport_t),
  /* vtp_init_secondary      */ tport_sctp_init_secondary,
  /* vtp_deinit_secondary    */ NULL,
  /* vtp_shutdown            */ NULL,
  /* vtp_set_events          */ NULL,
  /* vtp_wakeup              */ NULL,
  /* vtp_recv                */ tport_recv_sctp,
  /* vtp_send                */ tport_send_sctp,
  /* vtp_deliver             */ NULL,
  /* vtp_prepare             */ NULL,
  /* vtp_keepalive           */ NULL,
  /* vtp_stun_response       */ NULL,
  /* vtp_next_secondary_timer*/ tport_sctp_next_timer,
  /* vtp_secondary_timer     */ tport_sctp_timer,
126 127
};

128
tport_vtable_t const tport_sctp_vtable =
129
{
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
  /* vtp_name 		     */ "sctp",
  /* vtp_public              */ tport_type_local,
  /* vtp_pri_size            */ sizeof (tport_primary_t),
  /* vtp_init_primary        */ tport_sctp_init_primary,
  /* vtp_deinit_primary      */ NULL,
  /* vtp_wakeup_pri          */ tport_accept,
  /* vtp_connect             */ NULL,
  /* vtp_secondary_size      */ sizeof (tport_t),
  /* vtp_init_secondary      */ tport_sctp_init_secondary,
  /* vtp_deinit_secondary    */ NULL,
  /* vtp_shutdown            */ NULL,
  /* vtp_set_events          */ NULL,
  /* vtp_wakeup              */ NULL,
  /* vtp_recv                */ tport_recv_sctp,
  /* vtp_send                */ tport_send_sctp,
  /* vtp_deliver             */ NULL,
  /* vtp_prepare             */ NULL,
  /* vtp_keepalive           */ NULL,
  /* vtp_stun_response       */ NULL,
  /* vtp_next_secondary_timer*/ tport_sctp_next_timer,
  /* vtp_secondary_timer     */ tport_sctp_timer,
151 152
};

153
static int tport_sctp_init_primary(tport_primary_t *pri,
154
				   tp_name_t tpn[1],
155 156 157 158
				   su_addrinfo_t *ai,
				   tagi_t const *tags,
				   char const **return_culprit)
{
159 160
  int socket;

161 162 163
  if (pri->pri_params->tpp_mtu > TP_SCTP_MSG_MAX)
    pri->pri_params->tpp_mtu = TP_SCTP_MSG_MAX;

164 165
  socket = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);

166
  if (socket == INVALID_SOCKET)
167 168 169 170
    return *return_culprit = "socket", -1;

  if (tport_sctp_init_socket(pri, socket, return_culprit) < 0)
    return -1;
171

172
  return tport_stream_init_primary(pri, socket, tpn, ai, tags, return_culprit);
173 174
}

175
static int tport_sctp_init_client(tport_primary_t *pri,
176 177 178 179
				  tp_name_t tpn[1],
				  su_addrinfo_t *ai,
				  tagi_t const *tags,
				  char const **return_culprit)
180 181 182 183 184 185 186
{
  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);
}

187 188
static int tport_sctp_init_secondary(tport_t *self, int socket, int accepted,
				     char const **return_reason)
189
{
190
  self->tp_has_connection = 1;
191

192 193 194 195 196 197 198 199 200 201
  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 */
202
static int tport_sctp_init_socket(tport_primary_t *pri,
203 204 205 206 207 208 209 210 211 212
				  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;
213 214 215 216 217 218 219

  return 0;
}

/** Receive data available on the socket.
 *
 * @retval -1 error
220
 * @retval 0  end-of-stream
221 222 223
 * @retval 1  normal receive
 * @retval 2  incomplete recv, recv again
 */
224
static
225 226 227
int tport_recv_sctp(tport_t *self)
{
  msg_t *msg;
228
  ssize_t N, veclen;
229 230 231 232 233 234 235 236
  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);
237
  if (N == SOCKET_ERROR) {
238
    return su_is_blocking(su_errno()) ? 1 : -1;
239
  }
240

241 242 243 244 245
  if (N == 0) {
    if (self->tp_msg)
      msg_recv_commit(self->tp_msg, 0, 1);
    return 0;    /* End of stream */
  }
246

247
  tport_recv_bytes(self, N, N);
248 249

  veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0);
250 251 252 253 254 255
  if (veclen < 0)
    return -1;

  assert(veclen == 1); assert(iovec[0].mv_len == N);
  msg = self->tp_msg;

256
  msg_set_address(msg, self->tp_addr, self->tp_addrlen);
257

258
  memcpy(iovec[0].mv_base, sctp_buf, iovec[0].mv_len);
259 260

  if (self->tp_master->mr_dump_file)
261
    tport_dump_iovec(self, msg, N, iovec, veclen, "recv", "from");
262

263
  msg_recv_commit(msg, N, 0);  /* Mark buffer as used */
264

265
  return 2;
266 267
}

268 269
static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg,
			       msg_iovec_t iov[], size_t iovused)
270
{
271

272 273

  return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0);
274 275
}

276 277
/** Calculate tick timer if send is pending. */
int tport_next_sctp_send_tick(tport_t *self,
278
			    su_time_t *return_target,
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
			    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 */
298
  if (tport_has_queued(self) &&
299 300 301 302 303 304 305 306 307 308 309 310 311
      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,
312
			 su_time_t *return_target,
313 314
			 char const **return_why)
{
315
  return
316 317 318 319 320 321 322 323 324 325 326 327
    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);
}

328 329 330
#else
/* ISO c99 forbids empty source file */
void *sofia_tport_type_sctp_dummy;
331
#endif