comparison libfdcore/sctp_dtls.c @ 1188:e1ced4db7f67

Backup work in progress on DTLS, not usable
author Sebastien Decugis <sdecugis@freediameter.net>
date Tue, 11 Jun 2013 18:13:29 +0800
parents
children 1e8267ad057c
comparison
equal deleted inserted replaced
1187:b4a0f9ee3129 1188:e1ced4db7f67
1 /*********************************************************************************************************
2 * Software License Agreement (BSD License) *
3 * Author: Sebastien Decugis <sdecugis@freediameter.net> *
4 * *
5 * Copyright (c) 2013, 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 /* This file contains the code for DTLS over multi-stream SCTP implementation */
37
38 #include "fdcore-internal.h"
39 #include "cnxctx.h"
40
41
42 #define DTLS_TYPE_application_data 23
43
44 #ifdef GNUTLS_VERSION_300
45 /* Check if data is available for gnutls on a given context */
46 static int sctp_dtls_pull_timeout(gnutls_transport_ptr_t tr, unsigned int ms)
47 {
48 struct cnxctx * conn = (struct cnxctx *)tr;
49 fd_set rfds;
50 struct timeval tv;
51
52 FD_ZERO (&rfds);
53 FD_SET (conn->cc_socket, &rfds);
54
55 tv.tv_sec = 0;
56 tv.tv_usec = ms * 1000;
57
58 while(tv.tv_usec >= 1000000)
59 {
60 tv.tv_usec -= 1000000;
61 tv.tv_sec++;
62 }
63
64 return select (conn->cc_socket + 1, &rfds, NULL, NULL, &tv);
65 }
66 #endif /* GNUTLS_VERSION_300 */
67
68 /* Send data over the connection, called by gnutls */
69 static ssize_t sctp_dtls_pushv(gnutls_transport_ptr_t tr, const giovec_t * iov, int iovcnt)
70 {
71 struct cnxctx * conn = (struct cnxctx *)tr;
72 ssize_t ret;
73
74 TRACE_ENTRY("%p %p %d", tr, iov, iovcnt);
75 CHECK_PARAMS_DO( tr && iov, { errno = EINVAL; return -1; } );
76
77 /* If no unordered delivery is allowed, send over stream 0 always */
78 if (conn->cc_sctp_para.unordered == 0) {
79 ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt);
80 }
81 /* Otherwise, we need to check the type of record. */
82 else {
83 if ((iovcnt > 0)
84 && (iov->iov_len > 0) &&
85 (*((uint8_t *)iov->iov_base) == DTLS_TYPE_application_data)) {
86 /* Data is sent over different streams */
87 if (conn->cc_sctp_para.str_out > 32) {
88 TODO("Limiting to 32 streams. Remove this limit when anti-replay is disabled");
89 conn->cc_sctp_para.str_out = 32;
90 }
91 if (conn->cc_sctp_para.str_out > 1) {
92 conn->cc_sctp_para.next += 1;
93 conn->cc_sctp_para.next %= conn->cc_sctp_para.str_out;
94 } else {
95 conn->cc_sctp_para.next = 0;
96 }
97 ret = fd_sctp_sendstrv(conn, conn->cc_sctp_para.next, (const struct iovec *)iov, iovcnt);
98
99 } else {
100 /* other TLS messages are always sent over stream 0 */
101 ret = fd_sctp_sendstrv(conn, 0, (const struct iovec *)iov, iovcnt);
102 }
103 }
104
105 return ret;
106 }
107
108 #ifndef GNUTLS_VERSION_212
109 static ssize_t sctp_dtls_push(gnutls_transport_ptr_t tr, const void * data, size_t len)
110 {
111 giovec_t iov;
112 iov.iov_base = (void *)data;
113 iov.iov_len = len;
114 return sctp_dtls_pushv(tr, &iov, 1);
115 }
116 #endif /* GNUTLS_VERSION_212 */
117
118 /* Retrieve data received on any stream */
119 static ssize_t sctp_dtls_pull(gnutls_transport_ptr_t tr, void * buf, size_t len)
120 {
121 struct cnxctx * conn = (struct cnxctx *)tr;
122
123 /* If needed we can use fd_sctp_recvmeta to retrieve more information here */
124 return fd_cnx_s_recv(conn, buf, len);
125 }
126
127 /* Set the parameters of a session to use the cnxctx object */
128 #ifndef GNUTLS_VERSION_300
129 GCC_DIAG_OFF("-Wdeprecated-declarations")
130 #endif /* !GNUTLS_VERSION_300 */
131 int fd_sctp_dtls_settransport(gnutls_session_t session, struct cnxctx * conn)
132 {
133 /* Set the transport pointer passed to push & pull callbacks */
134 GNUTLS_TRACE( gnutls_transport_set_ptr( session, (gnutls_transport_ptr_t) conn ) );
135
136 /* Reset the low water value, since we don't use sockets */
137 #ifndef GNUTLS_VERSION_300
138 /* starting version 2.12, this call is not needed */
139 GNUTLS_TRACE( gnutls_transport_set_lowat( session, 0 ) );
140 #else /* GNUTLS_VERSION_300 */
141 /* but in 3.0 we have to provide the pull_timeout callback */
142 GNUTLS_TRACE( gnutls_transport_set_pull_timeout_function( session, sctp_dtls_pull_timeout ) );
143 #endif /* GNUTLS_VERSION_300 */
144
145 /* Set the push and pull callbacks */
146 GNUTLS_TRACE( gnutls_transport_set_pull_function(session, sctp_dtls_pull) );
147 #ifndef GNUTLS_VERSION_212
148 GNUTLS_TRACE( gnutls_transport_set_push_function(session, sctp_dtls_push) );
149 #else /* GNUTLS_VERSION_212 */
150 GNUTLS_TRACE( gnutls_transport_set_vec_push_function(session, sctp_dtls_pushv) );
151 #endif /* GNUTLS_VERSION_212 */
152
153 return;
154 }
155 #ifndef GNUTLS_VERSION_300
156 GCC_DIAG_ON("-Wdeprecated-declarations")
157 #endif /* !GNUTLS_VERSION_300 */
158
159
160
161
162 /* Set additional session parameters before handshake. The GNUTLS_DATAGRAM is already set in fd_tls_prepare */
163 int fd_sctp_dtls_prepare(gnutls_session_t session)
164 {
165 /* We do not use cookies at the moment. Not sure it is useful or not */
166 TODO("Cookie exchange?");
167 /* gnutls_dtls_prestate_set (session, &prestate); */
168
169 gnutls_dtls_set_mtu(session, 2^14 /* as per RFC 6083 */);
170
171 gnutls_dtls_set_timeouts(session, 70000, 60000); /* Set retrans > total so that there is no retransmission, since SCTP is reliable */
172
173 #ifdef GNUTLS_VERSION_320
174 TODO("Disable replay protection");
175 #endif /* GNUTLS_VERSION_320 */
176
177
178 }
179
180
181 static ssize_t fd_dtls_recv_handle_error(struct cnxctx * conn, gnutls_session_t session, void * data, size_t sz, uint8_t seq[8])
182 {
183 ssize_t ret;
184 again:
185 CHECK_GNUTLS_DO( ret = gnutls_record_recv_seq(session, data, sz, seq),
186 {
187 switch (ret) {
188 case GNUTLS_E_REHANDSHAKE:
189 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING)) {
190 CHECK_GNUTLS_DO( ret = gnutls_handshake(session),
191 {
192 if (TRACE_BOOL(INFO)) {
193 fd_log_debug("TLS re-handshake failed on socket %d (%s) : %s", conn->cc_socket, conn->cc_id, gnutls_strerror(ret));
194 }
195 goto end;
196 } );
197 }
198
199 case GNUTLS_E_AGAIN:
200 case GNUTLS_E_INTERRUPTED:
201 if (!fd_cnx_teststate(conn, CC_STATUS_CLOSING))
202 goto again;
203 TRACE_DEBUG(FULL, "Connection is closing, so abord gnutls_record_recv now.");
204 break;
205
206 case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
207 /* The connection is closed */
208 TRACE_DEBUG(FULL, "Got 0 size while reading the socket, probably connection closed...");
209 break;
210
211 default:
212 if (gnutls_error_is_fatal (ret) == 0) {
213 LOG_N("Ignoring non-fatal GNU TLS error: %s", gnutls_strerror (ret));
214 goto again;
215 }
216 LOG_E("Fatal GNUTLS error: %s", gnutls_strerror (ret));
217 }
218 } );
219
220 if (ret == 0)
221 CHECK_GNUTLS_DO( gnutls_bye(session, GNUTLS_SHUT_RDWR), );
222
223 end:
224 if (ret <= 0)
225 fd_cnx_markerror(conn);
226 return ret;
227 }
228
229
230 /* Receiver thread that reassemble the decrypted messages (when size is > 2<<14) for upper layer */
231 void * fd_sctp_dtls_rcvthr(void * arg) {
232
233 struct cnxctx * conn = arg;
234
235 TRACE_ENTRY("%p", arg);
236 CHECK_PARAMS_DO(conn && (conn->cc_socket > 0), return NULL );
237
238 /* Set the thread name */
239 {
240 char buf[48];
241 snprintf(buf, sizeof(buf), "Receiver (%d) DTLS", conn->cc_socket);
242 fd_log_threadname ( buf );
243 }
244
245 ASSERT( fd_cnx_teststate(conn, CC_STATUS_TLS) );
246 ASSERT( fd_cnx_target_queue(conn) );
247
248 /* The next function only returns when there is an error on the socket */
249 do {
250
251 TODO("Reassemble the packets based on their sequence number & stream");
252
253
254 #if 0
255
256 uint8_t header[4];
257 struct fd_cnx_rcvdata rcv_data;
258 struct fd_msg_pmdl *pmdl=NULL;
259 ssize_t ret = 0;
260 size_t received = 0;
261 uint8_t seq[8];
262
263 do {
264 ret = fd_dtls_recv_handle_error(conn, session, &header[received], sizeof(header) - received, seq);
265 if (ret <= 0) {
266 /* The connection is closed */
267 goto out;
268 }
269 received += ret;
270 } while (received < sizeof(header));
271
272 rcv_data.length = ((size_t)header[1] << 16) + ((size_t)header[2] << 8) + (size_t)header[3];
273
274 /* Check the received word is a valid beginning of a Diameter message */
275 if ((header[0] != DIAMETER_VERSION) /* defined in <libfreeDiameter.h> */
276 || (rcv_data.length > DIAMETER_MSG_SIZE_MAX)) { /* to avoid too big mallocs */
277 /* The message is suspect */
278 LOG_E( "Received suspect header [ver: %d, size: %zd] from '%s', assume disconnection", (int)header[0], rcv_data.length, conn->cc_remid);
279 fd_cnx_markerror(conn);
280 goto out;
281 }
282
283 /* Ok, now we can really receive the data */
284 CHECK_MALLOC( rcv_data.buffer = fd_cnx_alloc_msg_buffer( rcv_data.length, &pmdl ) );
285 memcpy(rcv_data.buffer, header, sizeof(header));
286
287 while (received < rcv_data.length) {
288 pthread_cleanup_push(free_rcvdata, &rcv_data); /* In case we are canceled, clean the partialy built buffer */
289 ret = fd_tls_recv_handle_error(conn, session, rcv_data.buffer + received, rcv_data.length - received);
290 pthread_cleanup_pop(0);
291
292 if (ret <= 0) {
293 free_rcvdata(&rcv_data);
294 goto out;
295 }
296 received += ret;
297 }
298
299 fd_hook_call(HOOK_DATA_RECEIVED, NULL, NULL, &rcv_data, pmdl);
300
301 /* We have received a complete message, pass it to the daemon */
302 CHECK_FCT_DO( ret = fd_event_send( fd_cnx_target_queue(conn), FDEVP_CNX_MSG_RECV, rcv_data.length, rcv_data.buffer),
303 {
304 free_rcvdata(&rcv_data);
305 CHECK_FCT_DO(fd_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), );
306 return ret;
307 } );
308 #endif // 0
309
310 } while (1);
311
312 out:
313 TRACE_DEBUG(FULL, "Thread terminated");
314 return NULL;
315 }
"Welcome to our mercurial repository"