1 | /********************************************************************************************************* |
---|
2 | * Software License Agreement (BSD License) * |
---|
3 | * Author: Sebastien Decugis <sdecugis@nict.go.jp> * |
---|
4 | * * |
---|
5 | * Copyright (c) 2011, 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 | /* The actual declaration of peer_state_str */ |
---|
39 | DECLARE_STATE_STR(); |
---|
40 | |
---|
41 | /* Helper for next macro */ |
---|
42 | #define case_str( _val ) \ |
---|
43 | case _val : return #_val |
---|
44 | |
---|
45 | DECLARE_PEV_STR(); |
---|
46 | |
---|
47 | /************************************************************************/ |
---|
48 | /* Delayed startup */ |
---|
49 | /************************************************************************/ |
---|
50 | static int started = 0; |
---|
51 | static pthread_mutex_t started_mtx = PTHREAD_MUTEX_INITIALIZER; |
---|
52 | static pthread_cond_t started_cnd = PTHREAD_COND_INITIALIZER; |
---|
53 | |
---|
54 | /* Wait for start signal */ |
---|
55 | static int fd_psm_waitstart() |
---|
56 | { |
---|
57 | int ret = 0; |
---|
58 | TRACE_ENTRY(""); |
---|
59 | CHECK_POSIX( pthread_mutex_lock(&started_mtx) ); |
---|
60 | awake: |
---|
61 | if (!ret && !started) { |
---|
62 | pthread_cleanup_push( fd_cleanup_mutex, &started_mtx ); |
---|
63 | CHECK_POSIX_DO( ret = pthread_cond_wait(&started_cnd, &started_mtx), ); |
---|
64 | pthread_cleanup_pop( 0 ); |
---|
65 | goto awake; |
---|
66 | } |
---|
67 | CHECK_POSIX( pthread_mutex_unlock(&started_mtx) ); |
---|
68 | return ret; |
---|
69 | } |
---|
70 | |
---|
71 | /* Allow the state machines to start */ |
---|
72 | int fd_psm_start() |
---|
73 | { |
---|
74 | TRACE_ENTRY(""); |
---|
75 | CHECK_POSIX( pthread_mutex_lock(&started_mtx) ); |
---|
76 | started = 1; |
---|
77 | CHECK_POSIX( pthread_cond_broadcast(&started_cnd) ); |
---|
78 | CHECK_POSIX( pthread_mutex_unlock(&started_mtx) ); |
---|
79 | return 0; |
---|
80 | } |
---|
81 | |
---|
82 | |
---|
83 | /************************************************************************/ |
---|
84 | /* Manage the list of active peers */ |
---|
85 | /************************************************************************/ |
---|
86 | |
---|
87 | /* Enter/leave OPEN state */ |
---|
88 | static int enter_open_state(struct fd_peer * peer) |
---|
89 | { |
---|
90 | struct fd_list * li; |
---|
91 | CHECK_PARAMS( FD_IS_LIST_EMPTY(&peer->p_actives) ); |
---|
92 | |
---|
93 | /* Callback registered by the credential validator (fd_peer_validate_register) */ |
---|
94 | if (peer->p_cb2) { |
---|
95 | CHECK_FCT_DO( (*peer->p_cb2)(&peer->p_hdr.info), |
---|
96 | { |
---|
97 | TRACE_DEBUG(FULL, "Validation failed, terminating the connection"); |
---|
98 | fd_psm_terminate(peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ); |
---|
99 | } ); |
---|
100 | peer->p_cb2 = NULL; |
---|
101 | return 0; |
---|
102 | } |
---|
103 | /* Insert in the active peers list */ |
---|
104 | CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) ); |
---|
105 | for (li = fd_g_activ_peers.next; li != &fd_g_activ_peers; li = li->next) { |
---|
106 | struct fd_peer * next_p = (struct fd_peer *)li->o; |
---|
107 | int cmp = strcmp(peer->p_hdr.info.pi_diamid, next_p->p_hdr.info.pi_diamid); |
---|
108 | if (cmp < 0) |
---|
109 | break; |
---|
110 | } |
---|
111 | fd_list_insert_before(li, &peer->p_actives); |
---|
112 | CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); |
---|
113 | |
---|
114 | /* Callback registered when the peer was added, by fd_peer_add */ |
---|
115 | if (peer->p_cb) { |
---|
116 | TRACE_DEBUG(FULL, "Calling add callback for peer %s", peer->p_hdr.info.pi_diamid); |
---|
117 | (*peer->p_cb)(&peer->p_hdr.info, peer->p_cb_data); |
---|
118 | peer->p_cb = NULL; |
---|
119 | peer->p_cb_data = NULL; |
---|
120 | } |
---|
121 | |
---|
122 | /* Start the thread to handle outgoing messages */ |
---|
123 | CHECK_FCT( fd_out_start(peer) ); |
---|
124 | |
---|
125 | /* Update the expiry timer now */ |
---|
126 | CHECK_FCT( fd_p_expi_update(peer) ); |
---|
127 | |
---|
128 | return 0; |
---|
129 | } |
---|
130 | static int leave_open_state(struct fd_peer * peer) |
---|
131 | { |
---|
132 | /* Remove from active peers list */ |
---|
133 | CHECK_POSIX( pthread_rwlock_wrlock(&fd_g_activ_peers_rw) ); |
---|
134 | fd_list_unlink( &peer->p_actives ); |
---|
135 | CHECK_POSIX( pthread_rwlock_unlock(&fd_g_activ_peers_rw) ); |
---|
136 | |
---|
137 | /* Stop the "out" thread */ |
---|
138 | CHECK_FCT( fd_out_stop(peer) ); |
---|
139 | |
---|
140 | /* Failover the messages */ |
---|
141 | fd_peer_failover_msg(peer); |
---|
142 | |
---|
143 | return 0; |
---|
144 | } |
---|
145 | |
---|
146 | |
---|
147 | /************************************************************************/ |
---|
148 | /* Helpers for state changes */ |
---|
149 | /************************************************************************/ |
---|
150 | |
---|
151 | /* Cleanup pending events in the peer */ |
---|
152 | void fd_psm_events_free(struct fd_peer * peer) |
---|
153 | { |
---|
154 | struct fd_event * ev; |
---|
155 | /* Purge all events, and free the associated data if any */ |
---|
156 | while (fd_fifo_tryget( peer->p_events, &ev ) == 0) { |
---|
157 | switch (ev->code) { |
---|
158 | case FDEVP_CNX_ESTABLISHED: { |
---|
159 | fd_cnx_destroy(ev->data); |
---|
160 | } |
---|
161 | break; |
---|
162 | |
---|
163 | case FDEVP_TERMINATE: |
---|
164 | /* Do not free the string since it is a constant */ |
---|
165 | break; |
---|
166 | |
---|
167 | case FDEVP_CNX_INCOMING: { |
---|
168 | struct cnx_incoming * evd = ev->data; |
---|
169 | fd_msg_log( FD_MSG_LOG_DROPPED, evd->cer, "Message discarded while cleaning peer state machine queue." ); |
---|
170 | CHECK_FCT_DO( fd_msg_free(evd->cer), /* continue */); |
---|
171 | fd_cnx_destroy(evd->cnx); |
---|
172 | } |
---|
173 | default: |
---|
174 | free(ev->data); |
---|
175 | } |
---|
176 | free(ev); |
---|
177 | } |
---|
178 | } |
---|
179 | |
---|
180 | |
---|
181 | /* Change state */ |
---|
182 | int fd_psm_change_state(struct fd_peer * peer, int new_state) |
---|
183 | { |
---|
184 | int old; |
---|
185 | |
---|
186 | TRACE_ENTRY("%p %d(%s)", peer, new_state, STATE_STR(new_state)); |
---|
187 | CHECK_PARAMS( CHECK_PEER(peer) ); |
---|
188 | fd_cpu_flush_cache(); |
---|
189 | old = peer->p_hdr.info.runtime.pir_state; |
---|
190 | if (old == new_state) |
---|
191 | return 0; |
---|
192 | |
---|
193 | TRACE_DEBUG(((old == STATE_OPEN) || (new_state == STATE_OPEN)) ? INFO : FULL, "'%s'\t-> '%s'\t'%s'", |
---|
194 | STATE_STR(old), |
---|
195 | STATE_STR(new_state), |
---|
196 | peer->p_hdr.info.pi_diamid); |
---|
197 | |
---|
198 | peer->p_hdr.info.runtime.pir_state = new_state; |
---|
199 | fd_cpu_flush_cache(); |
---|
200 | |
---|
201 | if (old == STATE_OPEN) { |
---|
202 | CHECK_FCT( leave_open_state(peer) ); |
---|
203 | } |
---|
204 | |
---|
205 | if (new_state == STATE_OPEN) { |
---|
206 | CHECK_FCT( enter_open_state(peer) ); |
---|
207 | } |
---|
208 | |
---|
209 | if (new_state == STATE_CLOSED) { |
---|
210 | /* Purge event list */ |
---|
211 | fd_psm_events_free(peer); |
---|
212 | |
---|
213 | /* If the peer is not persistant, we destroy it */ |
---|
214 | if (peer->p_hdr.info.config.pic_flags.persist == PI_PRST_NONE) { |
---|
215 | CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, NULL) ); |
---|
216 | } |
---|
217 | } |
---|
218 | |
---|
219 | return 0; |
---|
220 | } |
---|
221 | |
---|
222 | /* Set timeout timer of next event */ |
---|
223 | void fd_psm_next_timeout(struct fd_peer * peer, int add_random, int delay) |
---|
224 | { |
---|
225 | TRACE_DEBUG(FULL, "Peer timeout reset to %d seconds%s", delay, add_random ? " (+/- 2)" : "" ); |
---|
226 | |
---|
227 | /* Initialize the timer */ |
---|
228 | CHECK_POSIX_DO( clock_gettime( CLOCK_REALTIME, &peer->p_psm_timer ), ASSERT(0) ); |
---|
229 | |
---|
230 | if (add_random) { |
---|
231 | if (delay > 2) |
---|
232 | delay -= 2; |
---|
233 | else |
---|
234 | delay = 0; |
---|
235 | |
---|
236 | /* Add a random value between 0 and 4sec */ |
---|
237 | peer->p_psm_timer.tv_sec += random() % 4; |
---|
238 | peer->p_psm_timer.tv_nsec+= random() % 1000000000L; |
---|
239 | if (peer->p_psm_timer.tv_nsec > 1000000000L) { |
---|
240 | peer->p_psm_timer.tv_nsec -= 1000000000L; |
---|
241 | peer->p_psm_timer.tv_sec ++; |
---|
242 | } |
---|
243 | } |
---|
244 | |
---|
245 | peer->p_psm_timer.tv_sec += delay; |
---|
246 | |
---|
247 | #ifdef SLOW_PSM |
---|
248 | /* temporary for debug */ |
---|
249 | peer->p_psm_timer.tv_sec += 10; |
---|
250 | #endif |
---|
251 | } |
---|
252 | |
---|
253 | /* Cleanup the peer */ |
---|
254 | void fd_psm_cleanup(struct fd_peer * peer, int terminate) |
---|
255 | { |
---|
256 | /* Move to CLOSED state: failover messages, stop OUT thread, unlink peer from active list */ |
---|
257 | fd_cpu_flush_cache(); |
---|
258 | if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) { |
---|
259 | CHECK_FCT_DO( fd_psm_change_state(peer, STATE_CLOSED), /* continue */ ); |
---|
260 | } |
---|
261 | |
---|
262 | fd_p_cnx_abort(peer, terminate); |
---|
263 | |
---|
264 | fd_p_ce_clear_cnx(peer, NULL); |
---|
265 | |
---|
266 | if (peer->p_receiver) { |
---|
267 | fd_cnx_destroy(peer->p_receiver); |
---|
268 | peer->p_receiver = NULL; |
---|
269 | } |
---|
270 | |
---|
271 | if (terminate) { |
---|
272 | fd_psm_events_free(peer); |
---|
273 | CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ ); |
---|
274 | } |
---|
275 | |
---|
276 | } |
---|
277 | |
---|
278 | |
---|
279 | /************************************************************************/ |
---|
280 | /* The PSM thread */ |
---|
281 | /************************************************************************/ |
---|
282 | /* Cancelation cleanup : set ZOMBIE state in the peer */ |
---|
283 | void cleanup_setstate(void * arg) |
---|
284 | { |
---|
285 | struct fd_peer * peer = (struct fd_peer *)arg; |
---|
286 | CHECK_PARAMS_DO( CHECK_PEER(peer), return ); |
---|
287 | peer->p_hdr.info.runtime.pir_state = STATE_ZOMBIE; |
---|
288 | fd_cpu_flush_cache(); |
---|
289 | return; |
---|
290 | } |
---|
291 | |
---|
292 | /* The state machine thread (controler) */ |
---|
293 | static void * p_psm_th( void * arg ) |
---|
294 | { |
---|
295 | struct fd_peer * peer = (struct fd_peer *)arg; |
---|
296 | int created_started = started ? 1 : 0; |
---|
297 | int event; |
---|
298 | size_t ev_sz; |
---|
299 | void * ev_data; |
---|
300 | |
---|
301 | CHECK_PARAMS_DO( CHECK_PEER(peer), ASSERT(0) ); |
---|
302 | |
---|
303 | pthread_cleanup_push( cleanup_setstate, arg ); |
---|
304 | |
---|
305 | /* Set the thread name */ |
---|
306 | { |
---|
307 | char buf[48]; |
---|
308 | sprintf(buf, "PSM/%.*s", (int)sizeof(buf) - 5, peer->p_hdr.info.pi_diamid); |
---|
309 | fd_log_threadname ( buf ); |
---|
310 | } |
---|
311 | |
---|
312 | /* The state machine starts in CLOSED state */ |
---|
313 | peer->p_hdr.info.runtime.pir_state = STATE_CLOSED; |
---|
314 | |
---|
315 | /* Wait that the PSM are authorized to start in the daemon */ |
---|
316 | CHECK_FCT_DO( fd_psm_waitstart(), goto psm_end ); |
---|
317 | |
---|
318 | /* Initialize the timer */ |
---|
319 | if (peer->p_flags.pf_responder) { |
---|
320 | fd_psm_next_timeout(peer, 0, INCNX_TIMEOUT); |
---|
321 | } else { |
---|
322 | fd_psm_next_timeout(peer, created_started, 0); |
---|
323 | } |
---|
324 | |
---|
325 | psm_loop: |
---|
326 | /* Get next event */ |
---|
327 | TRACE_DEBUG(FULL, "'%s' in state '%s' waiting for next event.", |
---|
328 | peer->p_hdr.info.pi_diamid, STATE_STR(peer->p_hdr.info.runtime.pir_state)); |
---|
329 | CHECK_FCT_DO( fd_event_timedget(peer->p_events, &peer->p_psm_timer, FDEVP_PSM_TIMEOUT, &event, &ev_sz, &ev_data), goto psm_end ); |
---|
330 | TRACE_DEBUG(FULL, "'%s'\t<-- '%s'\t(%p,%zd)\t'%s'", |
---|
331 | STATE_STR(peer->p_hdr.info.runtime.pir_state), |
---|
332 | fd_pev_str(event), ev_data, ev_sz, |
---|
333 | peer->p_hdr.info.pi_diamid); |
---|
334 | |
---|
335 | /* Now, the action depends on the current state and the incoming event */ |
---|
336 | |
---|
337 | /* The following states are impossible */ |
---|
338 | ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_NEW ); |
---|
339 | ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE ); |
---|
340 | ASSERT( peer->p_hdr.info.runtime.pir_state != STATE_OPEN_HANDSHAKE ); /* because it should exist only between two loops */ |
---|
341 | |
---|
342 | /* Purge invalid events */ |
---|
343 | if (!CHECK_PEVENT(event)) { |
---|
344 | TRACE_DEBUG(INFO, "Invalid event received in PSM '%s' : %d", peer->p_hdr.info.pi_diamid, event); |
---|
345 | goto psm_loop; |
---|
346 | } |
---|
347 | |
---|
348 | /* Handle the (easy) debug event now */ |
---|
349 | if (event == FDEVP_DUMP_ALL) { |
---|
350 | fd_peer_dump(peer, ANNOYING); |
---|
351 | goto psm_loop; |
---|
352 | } |
---|
353 | |
---|
354 | /* Requests to terminate the peer object */ |
---|
355 | if (event == FDEVP_TERMINATE) { |
---|
356 | switch (peer->p_hdr.info.runtime.pir_state) { |
---|
357 | case STATE_OPEN: |
---|
358 | case STATE_REOPEN: |
---|
359 | /* We cannot just close the conenction, we have to send a DPR first */ |
---|
360 | CHECK_FCT_DO( fd_p_dp_initiate(peer, ev_data), goto psm_end ); |
---|
361 | goto psm_loop; |
---|
362 | |
---|
363 | /* |
---|
364 | case STATE_CLOSING: |
---|
365 | case STATE_WAITCNXACK: |
---|
366 | case STATE_WAITCNXACK_ELEC: |
---|
367 | case STATE_WAITCEA: |
---|
368 | case STATE_SUSPECT: |
---|
369 | case STATE_CLOSED: |
---|
370 | */ |
---|
371 | default: |
---|
372 | /* In these cases, we just cleanup the peer object (if needed) and terminate */ |
---|
373 | goto psm_end; |
---|
374 | } |
---|
375 | } |
---|
376 | |
---|
377 | /* A message was received */ |
---|
378 | if (event == FDEVP_CNX_MSG_RECV) { |
---|
379 | struct msg * msg = NULL; |
---|
380 | struct msg_hdr * hdr; |
---|
381 | |
---|
382 | /* If the current state does not allow receiving messages, just drop it */ |
---|
383 | if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSED) { |
---|
384 | TRACE_DEBUG(FULL, "Purging message in queue while in CLOSED state (%zdb)", ev_sz); |
---|
385 | free(ev_data); |
---|
386 | goto psm_loop; |
---|
387 | } |
---|
388 | |
---|
389 | /* Parse the received buffer */ |
---|
390 | CHECK_FCT_DO( fd_msg_parse_buffer( (void *)&ev_data, ev_sz, &msg), |
---|
391 | { |
---|
392 | fd_log_debug("Received invalid data from peer '%s', closing the connection\n", peer->p_hdr.info.pi_diamid); |
---|
393 | free(ev_data); |
---|
394 | CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset ); |
---|
395 | goto psm_loop; |
---|
396 | } ); |
---|
397 | |
---|
398 | /* Log incoming message */ |
---|
399 | fd_msg_log( FD_MSG_LOG_RECEIVED, msg, "Received %zdb from '%s'", ev_sz, peer->p_hdr.info.pi_diamid ); |
---|
400 | |
---|
401 | /* Extract the header */ |
---|
402 | CHECK_FCT_DO( fd_msg_hdr(msg, &hdr), goto psm_end ); |
---|
403 | |
---|
404 | /* If it is an answer, associate with the request or drop */ |
---|
405 | if (!(hdr->msg_flags & CMD_FLAG_REQUEST)) { |
---|
406 | struct msg * req; |
---|
407 | /* Search matching request (same hbhid) */ |
---|
408 | CHECK_FCT_DO( fd_p_sr_fetch(&peer->p_sr, hdr->msg_hbhid, &req), goto psm_end ); |
---|
409 | if (req == NULL) { |
---|
410 | fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Answer received with no corresponding sent request." ); |
---|
411 | fd_msg_free(msg); |
---|
412 | goto psm_loop; |
---|
413 | } |
---|
414 | |
---|
415 | /* Associate */ |
---|
416 | CHECK_FCT_DO( fd_msg_answ_associate( msg, req ), goto psm_end ); |
---|
417 | } |
---|
418 | |
---|
419 | /* Now handle non-link-local messages */ |
---|
420 | if (fd_msg_is_routable(msg)) { |
---|
421 | switch (peer->p_hdr.info.runtime.pir_state) { |
---|
422 | /* To maximize compatibility -- should not be a security issue here */ |
---|
423 | case STATE_REOPEN: |
---|
424 | case STATE_SUSPECT: |
---|
425 | case STATE_CLOSING: |
---|
426 | TRACE_DEBUG(FULL, "Accepted a message while not in OPEN state... "); |
---|
427 | /* The standard situation : */ |
---|
428 | case STATE_OPEN: |
---|
429 | /* We received a valid routable message, update the expiry timer */ |
---|
430 | CHECK_FCT_DO( fd_p_expi_update(peer), goto psm_end ); |
---|
431 | |
---|
432 | /* Set the message source and add the Route-Record */ |
---|
433 | CHECK_FCT_DO( fd_msg_source_set( msg, peer->p_hdr.info.pi_diamid, 1, fd_g_config->cnf_dict ), goto psm_end); |
---|
434 | |
---|
435 | /* Requeue to the global incoming queue */ |
---|
436 | CHECK_FCT_DO(fd_fifo_post(fd_g_incoming, &msg), goto psm_end ); |
---|
437 | |
---|
438 | /* Update the peer timer (only in OPEN state) */ |
---|
439 | if ((peer->p_hdr.info.runtime.pir_state == STATE_OPEN) && (!peer->p_flags.pf_dw_pending)) { |
---|
440 | fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); |
---|
441 | } |
---|
442 | break; |
---|
443 | |
---|
444 | /* In other states, we discard the message, it is either old or invalid to send it for the remote peer */ |
---|
445 | case STATE_WAITCNXACK: |
---|
446 | case STATE_WAITCNXACK_ELEC: |
---|
447 | case STATE_WAITCEA: |
---|
448 | case STATE_CLOSED: |
---|
449 | default: |
---|
450 | /* In such case, just discard the message */ |
---|
451 | fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received from peer '%s' while connection was not in OPEN state.", peer->p_hdr.info.pi_diamid ); |
---|
452 | fd_msg_free(msg); |
---|
453 | } |
---|
454 | goto psm_loop; |
---|
455 | } |
---|
456 | |
---|
457 | /* Link-local message: They must be understood by our dictionary, otherwise we return an error */ |
---|
458 | { |
---|
459 | int ret = fd_msg_parse_or_error( &msg ); |
---|
460 | if (ret != EBADMSG) { |
---|
461 | CHECK_FCT_DO( ret, goto psm_end ); |
---|
462 | } else { |
---|
463 | if (msg) { |
---|
464 | /* Send the error back to the peer */ |
---|
465 | CHECK_FCT_DO( ret = fd_out_send(&msg, NULL, peer, FD_CNX_ORDERED), ); |
---|
466 | if (msg) { |
---|
467 | /* Only if an error occurred & the message was not saved / dumped */ |
---|
468 | fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Internal error: Problem while sending (%s)\n", strerror(ret) ); |
---|
469 | CHECK_FCT_DO( fd_msg_free(msg), goto psm_end); |
---|
470 | } |
---|
471 | } else { |
---|
472 | /* We received an invalid answer, let's disconnect */ |
---|
473 | CHECK_FCT_DO( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL), goto psm_reset ); |
---|
474 | } |
---|
475 | goto psm_loop; |
---|
476 | } |
---|
477 | } |
---|
478 | |
---|
479 | /* Handle the LL message and update the expiry timer appropriately */ |
---|
480 | switch (hdr->msg_code) { |
---|
481 | case CC_CAPABILITIES_EXCHANGE: |
---|
482 | CHECK_FCT_DO( fd_p_ce_msgrcv(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset ); |
---|
483 | break; |
---|
484 | |
---|
485 | case CC_DISCONNECT_PEER: |
---|
486 | CHECK_FCT_DO( fd_p_dp_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset ); |
---|
487 | if (peer->p_hdr.info.runtime.pir_state == STATE_CLOSING) |
---|
488 | goto psm_end; |
---|
489 | break; |
---|
490 | |
---|
491 | case CC_DEVICE_WATCHDOG: |
---|
492 | CHECK_FCT_DO( fd_p_dw_handle(&msg, (hdr->msg_flags & CMD_FLAG_REQUEST), peer), goto psm_reset ); |
---|
493 | break; |
---|
494 | |
---|
495 | default: |
---|
496 | /* Unknown / unexpected / invalid message */ |
---|
497 | TRACE_DEBUG(INFO, "Invalid non-routable command received: %u.", hdr->msg_code); |
---|
498 | if (hdr->msg_flags & CMD_FLAG_REQUEST) { |
---|
499 | do { |
---|
500 | /* Reply with an error code */ |
---|
501 | CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, MSGFL_ANSW_ERROR ), break ); |
---|
502 | |
---|
503 | /* Set the error code */ |
---|
504 | CHECK_FCT_DO( fd_msg_rescode_set(msg, "DIAMETER_INVALID_HDR_BITS", NULL, NULL, 1 ), break ); |
---|
505 | |
---|
506 | /* Send the answer */ |
---|
507 | CHECK_FCT_DO( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED), break ); |
---|
508 | } while (0); |
---|
509 | } else { |
---|
510 | /* We did ASK for it ??? */ |
---|
511 | fd_log_debug("Invalid PXY flag in answer header ?\n"); |
---|
512 | } |
---|
513 | |
---|
514 | /* Cleanup the message if not done */ |
---|
515 | if (msg) { |
---|
516 | fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Received un-handled non-routable command from peer '%s'.", peer->p_hdr.info.pi_diamid ); |
---|
517 | CHECK_FCT_DO( fd_msg_free(msg), /* continue */); |
---|
518 | msg = NULL; |
---|
519 | } |
---|
520 | }; |
---|
521 | |
---|
522 | /* At this point the message must have been fully handled already */ |
---|
523 | if (msg) { |
---|
524 | fd_msg_log( FD_MSG_LOG_DROPPED, msg, "Internal error: unhandled message.", peer->p_hdr.info.pi_diamid ); |
---|
525 | fd_msg_free(msg); |
---|
526 | } |
---|
527 | |
---|
528 | goto psm_loop; |
---|
529 | } |
---|
530 | |
---|
531 | /* The connection object is broken */ |
---|
532 | if (event == FDEVP_CNX_ERROR) { |
---|
533 | switch (peer->p_hdr.info.runtime.pir_state) { |
---|
534 | case STATE_WAITCNXACK_ELEC: |
---|
535 | /* Abort the initiating side */ |
---|
536 | fd_p_cnx_abort(peer, 0); |
---|
537 | /* Process the receiver side */ |
---|
538 | CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end ); |
---|
539 | break; |
---|
540 | |
---|
541 | case STATE_WAITCEA: |
---|
542 | case STATE_OPEN: |
---|
543 | case STATE_REOPEN: |
---|
544 | case STATE_WAITCNXACK: |
---|
545 | case STATE_SUSPECT: |
---|
546 | default: |
---|
547 | /* Mark the connection problem */ |
---|
548 | peer->p_flags.pf_cnx_pb = 1; |
---|
549 | |
---|
550 | /* Destroy the connection, restart the timer to a new connection attempt */ |
---|
551 | fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc); |
---|
552 | |
---|
553 | case STATE_CLOSED: |
---|
554 | goto psm_reset; |
---|
555 | |
---|
556 | case STATE_CLOSING: |
---|
557 | /* We sent a DPR so we are terminating, do not wait for DPA */ |
---|
558 | goto psm_end; |
---|
559 | |
---|
560 | } |
---|
561 | goto psm_loop; |
---|
562 | } |
---|
563 | |
---|
564 | /* The connection notified a change in endpoints */ |
---|
565 | if (event == FDEVP_CNX_EP_CHANGE) { |
---|
566 | /* We actually don't care if we are in OPEN state here... */ |
---|
567 | |
---|
568 | /* Cleanup the remote LL and primary addresses */ |
---|
569 | CHECK_FCT_DO( fd_ep_filter( &peer->p_hdr.info.pi_endpoints, EP_FL_CONF | EP_FL_DISC | EP_FL_ADV ), /* ignore the error */); |
---|
570 | CHECK_FCT_DO( fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_PRIMARY ), /* ignore the error */); |
---|
571 | |
---|
572 | /* Get the new ones */ |
---|
573 | CHECK_FCT_DO( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints), /* ignore the error */); |
---|
574 | |
---|
575 | /* We do not support local endpoints change currently, but it could be added here if needed (refresh fd_g_config->cnf_endpoints)*/ |
---|
576 | |
---|
577 | if (TRACE_BOOL(ANNOYING)) { |
---|
578 | TRACE_DEBUG(ANNOYING, "New remote endpoint(s):" ); |
---|
579 | fd_ep_dump(6, &peer->p_hdr.info.pi_endpoints); |
---|
580 | } |
---|
581 | |
---|
582 | /* Done */ |
---|
583 | goto psm_loop; |
---|
584 | } |
---|
585 | |
---|
586 | /* A new connection was established and CER containing this peer id was received */ |
---|
587 | if (event == FDEVP_CNX_INCOMING) { |
---|
588 | struct cnx_incoming * params = ev_data; |
---|
589 | ASSERT(params); |
---|
590 | |
---|
591 | /* Handle the message */ |
---|
592 | CHECK_FCT_DO( fd_p_ce_handle_newCER(¶ms->cer, peer, ¶ms->cnx, params->validate), goto psm_end ); |
---|
593 | |
---|
594 | /* Cleanup if needed */ |
---|
595 | if (params->cnx) { |
---|
596 | fd_cnx_destroy(params->cnx); |
---|
597 | params->cnx = NULL; |
---|
598 | } |
---|
599 | if (params->cer) { |
---|
600 | fd_msg_log( FD_MSG_LOG_DROPPED, params->cer, "Internal error: this CER was not handled as expected." ); |
---|
601 | CHECK_FCT_DO( fd_msg_free(params->cer), ); |
---|
602 | params->cer = NULL; |
---|
603 | } |
---|
604 | |
---|
605 | /* Loop */ |
---|
606 | free(ev_data); |
---|
607 | goto psm_loop; |
---|
608 | } |
---|
609 | |
---|
610 | /* A new connection has been established with the remote peer */ |
---|
611 | if (event == FDEVP_CNX_ESTABLISHED) { |
---|
612 | struct cnxctx * cnx = ev_data; |
---|
613 | |
---|
614 | /* Release the resources of the connecting thread */ |
---|
615 | CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */); |
---|
616 | peer->p_ini_thr = (pthread_t)NULL; |
---|
617 | |
---|
618 | switch (peer->p_hdr.info.runtime.pir_state) { |
---|
619 | case STATE_WAITCNXACK_ELEC: |
---|
620 | case STATE_WAITCNXACK: |
---|
621 | fd_p_ce_handle_newcnx(peer, cnx); |
---|
622 | break; |
---|
623 | |
---|
624 | default: |
---|
625 | /* Just abort the attempt and continue */ |
---|
626 | TRACE_DEBUG(FULL, "Connection attempt successful but current state is %s, closing...", STATE_STR(peer->p_hdr.info.runtime.pir_state)); |
---|
627 | fd_cnx_destroy(cnx); |
---|
628 | } |
---|
629 | |
---|
630 | goto psm_loop; |
---|
631 | } |
---|
632 | |
---|
633 | /* A new connection has not been established with the remote peer */ |
---|
634 | if (event == FDEVP_CNX_FAILED) { |
---|
635 | |
---|
636 | /* Release the resources of the connecting thread */ |
---|
637 | CHECK_POSIX_DO( pthread_join( peer->p_ini_thr, NULL), /* ignore, it is not a big deal */); |
---|
638 | peer->p_ini_thr = (pthread_t)NULL; |
---|
639 | |
---|
640 | switch (peer->p_hdr.info.runtime.pir_state) { |
---|
641 | case STATE_WAITCNXACK_ELEC: |
---|
642 | /* Abort the initiating side */ |
---|
643 | fd_p_cnx_abort(peer, 0); |
---|
644 | /* Process the receiver side */ |
---|
645 | CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end ); |
---|
646 | break; |
---|
647 | |
---|
648 | case STATE_WAITCNXACK: |
---|
649 | /* Go back to CLOSE */ |
---|
650 | fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc); |
---|
651 | goto psm_reset; |
---|
652 | |
---|
653 | default: |
---|
654 | /* Just ignore */ |
---|
655 | TRACE_DEBUG(FULL, "Connection attempt failed but current state is %s, ignoring...", STATE_STR(peer->p_hdr.info.runtime.pir_state)); |
---|
656 | } |
---|
657 | |
---|
658 | goto psm_loop; |
---|
659 | } |
---|
660 | |
---|
661 | /* The timeout for the current state has been reached */ |
---|
662 | if (event == FDEVP_PSM_TIMEOUT) { |
---|
663 | switch (peer->p_hdr.info.runtime.pir_state) { |
---|
664 | case STATE_OPEN: |
---|
665 | case STATE_REOPEN: |
---|
666 | CHECK_FCT_DO( fd_p_dw_timeout(peer), goto psm_end ); |
---|
667 | goto psm_loop; |
---|
668 | |
---|
669 | case STATE_CLOSED: |
---|
670 | CHECK_FCT_DO( fd_psm_change_state(peer, STATE_WAITCNXACK), goto psm_end ); |
---|
671 | fd_psm_next_timeout(peer, 0, CNX_TIMEOUT); |
---|
672 | CHECK_FCT_DO( fd_p_cnx_init(peer), goto psm_end ); |
---|
673 | goto psm_loop; |
---|
674 | |
---|
675 | case STATE_SUSPECT: |
---|
676 | /* Mark the connection problem */ |
---|
677 | peer->p_flags.pf_cnx_pb = 1; |
---|
678 | |
---|
679 | case STATE_CLOSING: |
---|
680 | case STATE_WAITCNXACK: |
---|
681 | case STATE_WAITCEA: |
---|
682 | /* Destroy the connection, restart the timer to a new connection attempt */ |
---|
683 | fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_tctimer ?: fd_g_config->cnf_timer_tc); |
---|
684 | goto psm_reset; |
---|
685 | |
---|
686 | case STATE_WAITCNXACK_ELEC: |
---|
687 | /* Abort the initiating side */ |
---|
688 | fd_p_cnx_abort(peer, 0); |
---|
689 | /* Process the receiver side */ |
---|
690 | CHECK_FCT_DO( fd_p_ce_process_receiver(peer), goto psm_end ); |
---|
691 | goto psm_loop; |
---|
692 | |
---|
693 | default: |
---|
694 | ASSERT(0); /* implementation problem, we did not foresee this case? */ |
---|
695 | } |
---|
696 | } |
---|
697 | |
---|
698 | /* Default action : the handling has not yet been implemented. [for debug only] */ |
---|
699 | TRACE_DEBUG(INFO, "Missing handler in PSM for '%s'\t<-- '%s'", STATE_STR(peer->p_hdr.info.runtime.pir_state), fd_pev_str(event)); |
---|
700 | psm_reset: |
---|
701 | if (peer->p_flags.pf_delete) |
---|
702 | goto psm_end; |
---|
703 | fd_psm_cleanup(peer, 0); |
---|
704 | goto psm_loop; |
---|
705 | |
---|
706 | psm_end: |
---|
707 | fd_psm_cleanup(peer, 1); |
---|
708 | TRACE_DEBUG(INFO, "'%s'\t-> STATE_ZOMBIE (terminated)\t'%s'", |
---|
709 | STATE_STR(peer->p_hdr.info.runtime.pir_state), |
---|
710 | peer->p_hdr.info.pi_diamid); |
---|
711 | pthread_cleanup_pop(1); /* set STATE_ZOMBIE */ |
---|
712 | fd_cpu_flush_cache(); |
---|
713 | peer->p_psm = (pthread_t)NULL; |
---|
714 | pthread_detach(pthread_self()); |
---|
715 | return NULL; |
---|
716 | } |
---|
717 | |
---|
718 | |
---|
719 | /************************************************************************/ |
---|
720 | /* Functions to control the PSM */ |
---|
721 | /************************************************************************/ |
---|
722 | /* Create the PSM thread of one peer structure */ |
---|
723 | int fd_psm_begin(struct fd_peer * peer ) |
---|
724 | { |
---|
725 | TRACE_ENTRY("%p", peer); |
---|
726 | |
---|
727 | /* Check the peer and state are OK */ |
---|
728 | CHECK_PARAMS( CHECK_PEER(peer) && (peer->p_hdr.info.runtime.pir_state == STATE_NEW) ); |
---|
729 | |
---|
730 | /* Create the FIFO for events */ |
---|
731 | CHECK_FCT( fd_fifo_new(&peer->p_events) ); |
---|
732 | |
---|
733 | /* Create the PSM controler thread */ |
---|
734 | CHECK_POSIX( pthread_create( &peer->p_psm, NULL, p_psm_th, peer ) ); |
---|
735 | |
---|
736 | /* We're done */ |
---|
737 | return 0; |
---|
738 | } |
---|
739 | |
---|
740 | /* End the PSM (clean ending) */ |
---|
741 | int fd_psm_terminate(struct fd_peer * peer, char * reason ) |
---|
742 | { |
---|
743 | TRACE_ENTRY("%p", peer); |
---|
744 | CHECK_PARAMS( CHECK_PEER(peer) ); |
---|
745 | |
---|
746 | fd_cpu_flush_cache(); |
---|
747 | if (peer->p_hdr.info.runtime.pir_state != STATE_ZOMBIE) { |
---|
748 | CHECK_FCT( fd_event_send(peer->p_events, FDEVP_TERMINATE, 0, reason) ); |
---|
749 | } else { |
---|
750 | TRACE_DEBUG(FULL, "Peer '%s' was already terminated", peer->p_hdr.info.pi_diamid); |
---|
751 | } |
---|
752 | return 0; |
---|
753 | } |
---|
754 | |
---|
755 | /* End the PSM & cleanup the peer structure */ |
---|
756 | void fd_psm_abord(struct fd_peer * peer ) |
---|
757 | { |
---|
758 | TRACE_ENTRY("%p", peer); |
---|
759 | |
---|
760 | /* Cancel PSM thread */ |
---|
761 | CHECK_FCT_DO( fd_thr_term(&peer->p_psm), /* continue */ ); |
---|
762 | |
---|
763 | /* Cleanup the data */ |
---|
764 | fd_psm_cleanup(peer, 1); |
---|
765 | |
---|
766 | /* Destroy the event list */ |
---|
767 | CHECK_FCT_DO( fd_fifo_del(&peer->p_events), /* continue */ ); |
---|
768 | |
---|
769 | /* Remaining cleanups are performed in fd_peer_free */ |
---|
770 | return; |
---|
771 | } |
---|
772 | |
---|