1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2006 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@CFILE tport_type_sctp.c Transport using SCTP.
26 *
27 * See tport.docs for more detailed description of tport interface.
28 *
29 * @RFC4168.
30 *
31 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
32 * @author Martti Mela <Martti.Mela@nokia.com>
33 *
34 * @date Created: Fri Mar 24 08:45:49 EET 2006 ppessi
35 * @date Original Created: Thu Jul 20 12:54:32 2000 ppessi
36 */
37
38 #include "config.h"
39
40 #if HAVE_SCTP
41
42 #include "tport_internal.h"
43
44 #if HAVE_NETINET_SCTP_H
45 #include <netinet/sctp.h>
46 #endif
47
48 #include <stdlib.h>
49 #include <time.h>
50 #include <assert.h>
51 #include <errno.h>
52 #include <limits.h>
53 #include <string.h>
54
55 /* ---------------------------------------------------------------------- */
56 /* SCTP */
57
58 #undef MAX_STREAMS
59 #define MAX_STREAMS MAX_STREAMS
60
61 /* Missing socket symbols */
62 #ifndef SOL_SCTP
63 #define SOL_SCTP IPPROTO_SCTP
64 #endif
65
66 enum { MAX_STREAMS = 1 };
67 typedef struct tport_sctp_t
68 {
69 tport_t sctp_base[1];
70
71 msg_t *sctp_recv[MAX_STREAMS];
72 struct sctp_send {
73 msg_t *ss_msg;
74 msg_iovec_t *ss_unsent; /**< Pointer to first unsent iovec */
75 unsigned ss_unsentlen; /**< Number of unsent iovecs */
76 msg_iovec_t *ss_iov; /**< Iovecs allocated for sending */
77 unsigned ss_iovlen; /**< Number of allocated iovecs */
78 } sctp_send[MAX_STREAMS];
79 } tport_sctp_t;
80
81 #define TP_SCTP_MSG_MAX (65536)
82
83 static int tport_sctp_init_primary(tport_primary_t *,
84 tp_name_t tpn[1],
85 su_addrinfo_t *, tagi_t const *,
86 char const **return_culprit);
87 static int tport_sctp_init_client(tport_primary_t *,
88 tp_name_t tpn[1],
89 su_addrinfo_t *, tagi_t const *,
90 char const **return_culprit);
91 static int tport_sctp_init_secondary(tport_t *self, int socket, int accepted,
92 char const **return_reason);
93 static int tport_sctp_init_socket(tport_primary_t *pri,
94 int socket,
95 char const **return_reason);
96 static int tport_recv_sctp(tport_t *self);
97 static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg,
98 msg_iovec_t iov[], size_t iovused);
99
100 static int tport_sctp_next_timer(tport_t *self, su_time_t *, char const **);
101 static void tport_sctp_timer(tport_t *self, su_time_t);
102
103 tport_vtable_t const tport_sctp_client_vtable =
104 {
105 /* vtp_name */ "sctp",
106 /* vtp_public */ tport_type_client,
107 /* vtp_pri_size */ sizeof (tport_primary_t),
108 /* vtp_init_primary */ tport_sctp_init_client,
109 /* vtp_deinit_primary */ NULL,
110 /* vtp_wakeup_pri */ tport_accept,
111 /* vtp_connect */ NULL,
112 /* vtp_secondary_size */ sizeof (tport_t),
113 /* vtp_init_secondary */ tport_sctp_init_secondary,
114 /* vtp_deinit_secondary */ NULL,
115 /* vtp_shutdown */ NULL,
116 /* vtp_set_events */ NULL,
117 /* vtp_wakeup */ NULL,
118 /* vtp_recv */ tport_recv_sctp,
119 /* vtp_send */ tport_send_sctp,
120 /* vtp_deliver */ NULL,
121 /* vtp_prepare */ NULL,
122 /* vtp_keepalive */ NULL,
123 /* vtp_stun_response */ NULL,
124 /* vtp_next_secondary_timer*/ tport_sctp_next_timer,
125 /* vtp_secondary_timer */ tport_sctp_timer,
126 };
127
128 tport_vtable_t const tport_sctp_vtable =
129 {
130 /* vtp_name */ "sctp",
131 /* vtp_public */ tport_type_local,
132 /* vtp_pri_size */ sizeof (tport_primary_t),
133 /* vtp_init_primary */ tport_sctp_init_primary,
134 /* vtp_deinit_primary */ NULL,
135 /* vtp_wakeup_pri */ tport_accept,
136 /* vtp_connect */ NULL,
137 /* vtp_secondary_size */ sizeof (tport_t),
138 /* vtp_init_secondary */ tport_sctp_init_secondary,
139 /* vtp_deinit_secondary */ NULL,
140 /* vtp_shutdown */ NULL,
141 /* vtp_set_events */ NULL,
142 /* vtp_wakeup */ NULL,
143 /* vtp_recv */ tport_recv_sctp,
144 /* vtp_send */ tport_send_sctp,
145 /* vtp_deliver */ NULL,
146 /* vtp_prepare */ NULL,
147 /* vtp_keepalive */ NULL,
148 /* vtp_stun_response */ NULL,
149 /* vtp_next_secondary_timer*/ tport_sctp_next_timer,
150 /* vtp_secondary_timer */ tport_sctp_timer,
151 };
152
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)153 static int tport_sctp_init_primary(tport_primary_t *pri,
154 tp_name_t tpn[1],
155 su_addrinfo_t *ai,
156 tagi_t const *tags,
157 char const **return_culprit)
158 {
159 int socket;
160
161 if (pri->pri_params->tpp_mtu > TP_SCTP_MSG_MAX)
162 pri->pri_params->tpp_mtu = TP_SCTP_MSG_MAX;
163
164 socket = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
165
166 if (socket == INVALID_SOCKET)
167 return *return_culprit = "socket", -1;
168
169 if (tport_sctp_init_socket(pri, socket, return_culprit) < 0)
170 return -1;
171
172 return tport_stream_init_primary(pri, socket, tpn, ai, tags, return_culprit);
173 }
174
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)175 static int tport_sctp_init_client(tport_primary_t *pri,
176 tp_name_t tpn[1],
177 su_addrinfo_t *ai,
178 tagi_t const *tags,
179 char const **return_culprit)
180 {
181 if (pri->pri_params->tpp_mtu > TP_SCTP_MSG_MAX)
182 pri->pri_params->tpp_mtu = TP_SCTP_MSG_MAX;
183
184 return tport_tcp_init_client(pri, tpn, ai, tags, return_culprit);
185 }
186
tport_sctp_init_secondary(tport_t * self,int socket,int accepted,char const ** return_reason)187 static int tport_sctp_init_secondary(tport_t *self, int socket, int accepted,
188 char const **return_reason)
189 {
190 self->tp_has_connection = 1;
191
192 if (accepted) {
193 /* Accepted socket inherit the init information from listen socket */
194 return 0;
195 }
196 else {
197 return tport_sctp_init_socket(self->tp_pri, socket, return_reason);
198 }
199 }
200
201 /** Initialize a SCTP socket */
tport_sctp_init_socket(tport_primary_t * pri,int socket,char const ** return_reason)202 static int tport_sctp_init_socket(tport_primary_t *pri,
203 int socket,
204 char const **return_reason)
205 {
206 struct sctp_initmsg initmsg = { 0 };
207
208 initmsg.sinit_num_ostreams = MAX_STREAMS;
209 initmsg.sinit_max_instreams = MAX_STREAMS;
210
211 if (setsockopt(socket, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof initmsg) < 0)
212 return *return_reason = "SCTP_INITMSG", -1;
213
214 return 0;
215 }
216
217 /** Receive data available on the socket.
218 *
219 * @retval -1 error
220 * @retval 0 end-of-stream
221 * @retval 1 normal receive
222 * @retval 2 incomplete recv, recv again
223 */
224 static
tport_recv_sctp(tport_t * self)225 int tport_recv_sctp(tport_t *self)
226 {
227 msg_t *msg;
228 ssize_t N, veclen;
229 msg_iovec_t iovec[2] = {{ 0 }};
230
231 char sctp_buf[TP_SCTP_MSG_MAX];
232
233 iovec[0].mv_base = sctp_buf;
234 iovec[0].mv_len = sizeof(sctp_buf);
235
236 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 if (N == 0) {
242 if (self->tp_msg)
243 msg_recv_commit(self->tp_msg, 0, 1);
244 return 0; /* End of stream */
245 }
246
247 tport_recv_bytes(self, N, N);
248
249 veclen = tport_recv_iovec(self, &self->tp_msg, iovec, N, 0);
250 if (veclen < 0)
251 return -1;
252
253 assert(veclen == 1); assert(iovec[0].mv_len == N);
254 msg = self->tp_msg;
255
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 if (self->tp_master->mr_capt_sock)
264 tport_capt_msg(self, msg, N, iovec, veclen, "recv");
265
266 msg_recv_commit(msg, N, 0); /* Mark buffer as used */
267
268 return 2;
269 }
270
tport_send_sctp(tport_t const * self,msg_t * msg,msg_iovec_t iov[],size_t iovused)271 static ssize_t tport_send_sctp(tport_t const *self, msg_t *msg,
272 msg_iovec_t iov[], size_t iovused)
273 {
274
275
276 return su_vsend(self->tp_socket, iov, iovused, MSG_NOSIGNAL, NULL, 0);
277 }
278
279 /** Calculate tick timer if send is pending. */
tport_next_sctp_send_tick(tport_t * self,su_time_t * return_target,char const ** return_why)280 int tport_next_sctp_send_tick(tport_t *self,
281 su_time_t *return_target,
282 char const **return_why)
283 {
284 unsigned timeout = 100; /* Retry 10 times a second... */
285
286 if (tport_has_queued(self)) {
287 su_time_t ntime = su_time_add(self->tp_ktime, timeout);
288 if (su_time_cmp(ntime, *return_target) < 0)
289 *return_target = ntime, *return_why = "send tick";
290 }
291
292 return 0;
293 }
294
295 /** Tick timer if send is pending */
tport_sctp_send_tick_timer(tport_t * self,su_time_t now)296 void tport_sctp_send_tick_timer(tport_t *self, su_time_t now)
297 {
298 unsigned timeout = 100;
299
300 /* Send timeout */
301 if (tport_has_queued(self) &&
302 su_time_cmp(su_time_add(self->tp_ktime, timeout), now) < 0) {
303 uint64_t bytes = self->tp_stats.sent_bytes;
304 su_time_t stime = self->tp_stime;
305
306 tport_send_queue(self);
307
308 if (self->tp_stats.sent_bytes == bytes)
309 self->tp_stime = stime; /* Restore send timestamp */
310 }
311 }
312
313 /** Calculate next timer for SCTP. */
tport_sctp_next_timer(tport_t * self,su_time_t * return_target,char const ** return_why)314 int tport_sctp_next_timer(tport_t *self,
315 su_time_t *return_target,
316 char const **return_why)
317 {
318 return
319 tport_next_recv_timeout(self, return_target, return_why) |
320 tport_next_sctp_send_tick(self, return_target, return_why);
321 }
322
323 /** SCTP timer. */
tport_sctp_timer(tport_t * self,su_time_t now)324 void tport_sctp_timer(tport_t *self, su_time_t now)
325 {
326 tport_sctp_send_tick_timer(self, now);
327 tport_recv_timeout_timer(self, now);
328 tport_base_timer(self, now);
329 }
330
331 #else
332 /* ISO c99 forbids empty source file */
333 void *sofia_tport_type_sctp_dummy;
334 #endif
335