blob: 9dc2a0344eedc487b7b7c8351ddec5ecfd5dc2a5 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
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#include "fdcore-internal.h"
37
38
39/* TODO: change the behavior to handle properly forced ordering at beginning & end of OPEN state */
40
41/* This file contains code used by a peer state machine to initiate a connection to remote peer */
42
43struct next_conn {
44 struct fd_list chain;
45 int proto; /* Protocol of the next attempt */
46 union {
47 sSS ss; /* The address, only for TCP */
48 sSA4 sin;
49 sSA6 sin6;
50 };
51 uint16_t port; /* The port, for SCTP (included in ss for TCP) */
52 int dotls; /* Handshake TLS after connection ? */
53};
54
55static __inline__ void failed_connection_attempt(struct fd_peer * peer)
56{
57 /* Simply remove the first item in the list if not empty */
58 if (! FD_IS_LIST_EMPTY(&peer->p_connparams) ) {
59 struct fd_list * li = peer->p_connparams.next;
60 fd_list_unlink(li);
61 free(li);
62 }
63}
64
65static void empty_connection_list(struct fd_peer * peer)
66{
67 /* Remove all items */
68 while (!FD_IS_LIST_EMPTY(&peer->p_connparams)) {
69 failed_connection_attempt(peer);
70 }
71}
72
73static int prepare_connection_list(struct fd_peer * peer)
74{
75 struct fd_list * li, *last_prio;
76 struct next_conn * new;
77
78 uint16_t port_no; /* network order */
79 int dotls_immediate;
80 int count = 0;
81
82 TRACE_ENTRY("%p", peer);
83
84 /* Resolve peer address(es) if needed */
85 if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
86 struct addrinfo hints, *ai, *aip;
87 int ret;
88
89 memset(&hints, 0, sizeof(hints));
90 hints.ai_flags = AI_ADDRCONFIG;
91 ret = getaddrinfo(peer->p_hdr.info.pi_diamid, NULL, &hints, &ai);
92 if (ret) {
93 TRACE_DEBUG(INFO, "Unable to resolve address for peer '%s' (%s), aborting", peer->p_hdr.info.pi_diamid, gai_strerror(ret));
94 if (ret != EAI_AGAIN)
95 fd_psm_terminate( peer, NULL );
96 return 0;
97 }
98
99 for (aip = ai; aip != NULL; aip = aip->ai_next) {
100 CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, aip->ai_addr, aip->ai_addrlen, EP_FL_DISC ) );
101 }
102 freeaddrinfo(ai);
103 }
104
105 /* Remove addresses from unwanted family */
106 if (peer->p_hdr.info.config.pic_flags.pro3) {
107 CHECK_FCT( fd_ep_filter_family(
108 &peer->p_hdr.info.pi_endpoints,
109 (peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ?
110 AF_INET
111 : AF_INET6));
112 }
113 if (fd_g_config->cnf_flags.no_ip4) {
114 CHECK_FCT( fd_ep_filter_family(
115 &peer->p_hdr.info.pi_endpoints,
116 AF_INET6));
117 }
118 if (fd_g_config->cnf_flags.no_ip6) {
119 CHECK_FCT( fd_ep_filter_family(
120 &peer->p_hdr.info.pi_endpoints,
121 AF_INET));
122 }
123
124 /* We don't use the alternate addresses that were sent by the remote peer */
125 CHECK_FCT( fd_ep_clearflags(&peer->p_hdr.info.pi_endpoints, EP_FL_ADV) );
126
127
128 /* Now check we have at least one address to attempt */
129 if (FD_IS_LIST_EMPTY(&peer->p_hdr.info.pi_endpoints)) {
130 TRACE_DEBUG(INFO, "No address %savailable to connect to peer '%s', aborting",
131 peer->p_hdr.info.config.pic_flags.pro3 ? "in the configured family " : "", peer->p_hdr.info.pi_diamid);
132 fd_psm_terminate( peer, NULL );
133 return 0;
134 }
135
136 /* Check if we are able to communicate with this peer */
137 if (fd_g_config->cnf_sec_data.tls_disabled && ( peer->p_hdr.info.config.pic_flags.sec != PI_SEC_NONE)) {
138 LOG_E("Peer '%s' not configured for No_TLS and TLS is locally disabled; giving up connection attempts",
139 peer->p_hdr.info.pi_diamid);
140 fd_psm_terminate( peer, NULL );
141 return 0;
142 }
143
144 /* Cleanup any previous list */
145 empty_connection_list(peer);
146
147 /* Prepare the parameters */
148 if ((peer->p_hdr.info.config.pic_flags.sec != PI_SEC_DEFAULT) || (fd_g_config->cnf_flags.tls_alg)) {
149 dotls_immediate = 0;
150 port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_PORT);
151 } else {
152 dotls_immediate = 1;
153 port_no = htons(peer->p_hdr.info.config.pic_port ?: DIAMETER_SECURE_PORT);
154 }
155
156 last_prio = &peer->p_connparams;
157
158 /* Create TCP parameters unless specified otherwise */
159 if ((!fd_g_config->cnf_flags.no_tcp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_SCTP)) {
160 for (li = peer->p_hdr.info.pi_endpoints.next; li != &peer->p_hdr.info.pi_endpoints; li = li->next) {
161 struct fd_endpoint * ep = (struct fd_endpoint *)li;
162
163 CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
164 memset(new, 0, sizeof(struct next_conn));
165 fd_list_init(&new->chain, new);
166
167 new->proto = IPPROTO_TCP;
168
169 memcpy( &new->ss, &ep->ss, sizeof(sSS) );
170 switch (new->ss.ss_family) {
171 case AF_INET:
172 new->sin.sin_port = port_no;
173 break;
174 case AF_INET6:
175 new->sin6.sin6_port = port_no;
176 break;
177 default:
178 free(new);
179 continue; /* Move to the next endpoint */
180 }
181
182 new->dotls = dotls_immediate;
183
184 /* Add the new entry to the appropriate position (conf and disc go first) */
185 if (ep->flags & (EP_FL_CONF | EP_FL_DISC)) {
186 fd_list_insert_after(last_prio, &new->chain);
187 last_prio = &new->chain;
188 } else {
189 fd_list_insert_before(&peer->p_connparams, &new->chain);
190 }
191 count++;
192 }
193 }
194
195 /* Now, add the SCTP entry, if not disabled */
196#ifndef DISABLE_SCTP
197 if ((!fd_g_config->cnf_flags.no_sctp) && (peer->p_hdr.info.config.pic_flags.pro4 != PI_P4_TCP)) {
198 struct next_conn * new;
199
200 CHECK_MALLOC( new = malloc(sizeof(struct next_conn)) );
201 memset(new, 0, sizeof(struct next_conn));
202 fd_list_init(&new->chain, new);
203
204 new->proto = IPPROTO_SCTP;
205 new->port = ntohs(port_no); /* back to host byte order... */
206 new->dotls = dotls_immediate;
207
208 /* Add the new entry to the appropriate position (depending on preferences) */
209 if ((fd_g_config->cnf_flags.pr_tcp) || (peer->p_hdr.info.config.pic_flags.alg == PI_ALGPREF_TCP)) {
210 fd_list_insert_after(last_prio, &new->chain);
211 } else {
212 fd_list_insert_after(&peer->p_connparams, &new->chain); /* very first position */
213 }
214 count++;
215 }
216#endif /* DISABLE_SCTP */
217
218 LOG_D("Prepared %d sets of connection parameters to peer %s", count, peer->p_hdr.info.pi_diamid);
219
220 return 0;
221}
222
223
224/* The thread that attempts the connection */
225static void * connect_thr(void * arg)
226{
227 struct fd_peer * peer = arg;
228 struct cnxctx * cnx = NULL;
229 struct next_conn * nc = NULL;
230 int rebuilt = 0;
231 int fatal_error=0;
232
233 TRACE_ENTRY("%p", arg);
234 CHECK_PARAMS_DO( CHECK_PEER(peer), return NULL );
235
236 /* Set the thread name */
237 {
238 char buf[48];
239 snprintf(buf, sizeof(buf), "ConnTo:%s", peer->p_hdr.info.pi_diamid);
240 fd_log_threadname ( buf );
241 }
242
243 do {
244 /* Rebuild the list if needed, if it is empty -- but at most once */
245 if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
246 if (! rebuilt) {
247 CHECK_FCT_DO( fatal_error = prepare_connection_list(peer), goto out );
248 rebuilt ++;
249 }
250 if (FD_IS_LIST_EMPTY(&peer->p_connparams)) {
251 /* We encountered an error or we have looped over all the addresses of the peer. */
252 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "All connection attempts failed, will retry later", NULL);
253
254 CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_FAILED, 0, NULL), goto out );
255 return NULL;
256 }
257 }
258
259 /* Attempt connection to the first entry */
260 nc = (struct next_conn *)(peer->p_connparams.next);
261
262 switch (nc->proto) {
263 case IPPROTO_TCP:
264 cnx = fd_cnx_cli_connect_tcp((sSA *)&nc->ss, sSAlen(&nc->ss));
265 break;
266#ifndef DISABLE_SCTP
267 case IPPROTO_SCTP:
268 cnx = fd_cnx_cli_connect_sctp((peer->p_hdr.info.config.pic_flags.pro3 == PI_P3_IP) ? 1 : fd_g_config->cnf_flags.no_ip6,
269 nc->port, &peer->p_hdr.info.pi_endpoints);
270 break;
271#endif /* DISABLE_SCTP */
272 }
273
274 if (cnx)
275 break;
276
277 /* Pop these parameters and continue */
278 failed_connection_attempt(peer);
279
280 pthread_testcancel();
281
282 } while (!cnx); /* and until cancellation or all addresses attempted without success */
283
284 /* Now, we have an established connection in cnx */
285
286 pthread_cleanup_push((void *)fd_cnx_destroy, cnx);
287
288 /* Set the hostname in the connection, so that handshake verifies the remote identity */
289 fd_cnx_sethostname(cnx,peer->p_hdr.info.pi_diamid);
290
291 /* Handshake if needed (secure port) */
292 if (nc->dotls) {
293 CHECK_FCT_DO( fd_cnx_handshake(cnx, GNUTLS_CLIENT,
294 ALGO_HANDSHAKE_3436,
295 peer->p_hdr.info.config.pic_priority, NULL),
296 {
297 /* Handshake failed ... */
298 fd_hook_call(HOOK_PEER_CONNECT_FAILED, NULL, peer, "TLS Handshake failed", NULL);
299 fd_cnx_destroy(cnx);
300 empty_connection_list(peer);
301 fd_ep_filter(&peer->p_hdr.info.pi_endpoints, EP_FL_CONF);
302 goto out_pop;
303 } );
304 LOG_A("%s: TLS handshake successful.", peer->p_hdr.info.pi_diamid);
305 } else {
306 /* Prepare to receive the next message */
307 CHECK_FCT_DO( fatal_error = fd_cnx_start_clear(cnx, 0), goto out_pop );
308 }
309
310 /* Upon success, generate FDEVP_CNX_ESTABLISHED */
311 CHECK_FCT_DO( fatal_error = fd_event_send(peer->p_events, FDEVP_CNX_ESTABLISHED, 0, cnx), );
312out_pop:
313 ;
314 pthread_cleanup_pop(0);
315
316out:
317
318 if (fatal_error) {
319
320 /* Cleanup the connection */
321 if (cnx)
322 fd_cnx_destroy(cnx);
323
324 /* Generate a termination event */
325 CHECK_FCT_DO(fd_core_shutdown(), );
326 }
327
328 return NULL;
329}
330
331
332/* Initiate a connection attempt to a remote peer */
333int fd_p_cnx_init(struct fd_peer * peer)
334{
335 TRACE_ENTRY("%p", peer);
336
337 /* Start the connect thread */
338 CHECK_FCT( pthread_create(&peer->p_ini_thr, NULL, connect_thr, peer) );
339 return 0;
340}
341
342/* Cancel a connection attempt */
343void fd_p_cnx_abort(struct fd_peer * peer, int cleanup_all)
344{
345 TRACE_ENTRY("%p %d", peer, cleanup_all);
346 CHECK_PARAMS_DO( CHECK_PEER(peer), return );
347
348 if (peer->p_ini_thr != (pthread_t)NULL) {
349 CHECK_FCT_DO( fd_thr_term(&peer->p_ini_thr), /* continue */);
350 failed_connection_attempt(peer);
351 }
352
353 if (cleanup_all) {
354 empty_connection_list(peer);
355 }
356}
357