1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * This file is part of the PCEPlib, a PCEP protocol library.
5 * Copyright (C) 2020 Volta Networks https://voltanet.io/
7 * Author : Brady Johnson <brady@voltanet.io>
19 #include "pcep_session_logic.h"
20 #include "pcep_session_logic_internals.h"
21 #include "pcep_timers.h"
22 #include "pcep_utils_logging.h"
23 #include "pcep_utils_memory.h"
25 /* global var needed for callback handlers */
26 extern pcep_session_logic_handle
*session_logic_handle_
;
28 /* internal util function to create session_event's */
29 static pcep_session_event
*create_session_event(pcep_session
*session
)
31 pcep_session_event
*event
=
32 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_session_event
));
33 event
->session
= session
;
34 event
->expired_timer_id
= TIMER_ID_NOT_SET
;
35 event
->received_msg_list
= NULL
;
36 event
->socket_closed
= false;
42 /* A function pointer to this function is passed to pcep_socket_comm
43 * for each pcep_session creation, so it will be called whenever
44 * messages are ready to be read. This function will be called
45 * by the socket_comm thread.
46 * This function will decode the read PCEP message and give it
47 * to the session_logic_loop so it can be handled by the session_logic
49 int session_logic_msg_ready_handler(void *data
, int socket_fd
)
53 "%s: Cannot handle msg_ready with NULL data",
58 if (session_logic_handle_
->active
== false) {
61 "%s: Received a message ready notification while the session logic is not active",
66 pcep_session
*session
= (pcep_session
*)data
;
68 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
69 session_logic_handle_
->session_logic_condition
= true;
71 /* This event will ultimately be handled by handle_socket_comm_event()
72 * in pcep_session_logic_states.c */
73 pcep_session_event
*rcvd_msg_event
= create_session_event(session
);
76 double_linked_list
*msg_list
= pcep_msg_read(socket_fd
);
78 if (msg_list
== NULL
) {
79 /* The socket was closed, or there was a socket read error */
81 "%s: PCEP connection closed for session [%d]",
82 __func__
, session
->session_id
);
83 dll_destroy(msg_list
);
84 rcvd_msg_event
->socket_closed
= true;
85 socket_comm_session_teardown(session
->socket_comm_session
);
86 pcep_session_cancel_timers(session
);
87 session
->socket_comm_session
= NULL
;
88 session
->session_state
= SESSION_STATE_INITIALIZED
;
89 enqueue_event(session
, PCE_CLOSED_SOCKET
, NULL
);
90 } else if (msg_list
->num_entries
== 0) {
91 /* Invalid message received */
92 increment_unknown_message(session
);
93 dll_destroy_with_data(msg_list
);
95 /* Just logging the first of potentially several messages
97 struct pcep_message
*msg
=
98 ((struct pcep_message
*)msg_list
->head
->data
);
101 "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]",
102 __func__
, time(NULL
), pthread_self(),
103 msg
->msg_header
->type
, msg
->encoded_message_length
,
104 session
->session_id
);
106 rcvd_msg_event
->received_msg_list
= msg_list
;
107 msg_length
= msg
->encoded_message_length
;
110 queue_enqueue(session_logic_handle_
->session_event_queue
,
112 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
113 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
119 /* A function pointer to this function was passed to pcep_socket_comm,
120 * so it will be called when a message is sent. This is useful since
121 * message sending is asynchronous, and there are times that actions
122 * need to be performed only after a message has been sent. */
123 void session_logic_message_sent_handler(void *data
, int socket_fd
)
128 pcep_log(LOG_WARNING
,
129 "%s: Cannot handle msg_sent with NULL data", __func__
);
133 pcep_session
*session
= (pcep_session
*)data
;
134 if (session
->destroy_session_after_write
== true) {
135 /* Do not call destroy until all of the queued messages are
137 if (session
->socket_comm_session
!= NULL
138 && session
->socket_comm_session
->message_queue
->num_entries
140 destroy_pcep_session(session
);
143 /* Reset the keep alive timer for every message sent on
144 * the session, only if the session is not destroyed */
145 if (session
->timer_id_keep_alive
== TIMER_ID_NOT_SET
) {
148 "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
149 __func__
, time(NULL
), pthread_self(),
151 .keep_alive_pce_negotiated_timer_seconds
,
152 session
->session_id
);
153 session
->timer_id_keep_alive
= create_timer(
155 .keep_alive_pce_negotiated_timer_seconds
,
160 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
161 __func__
, time(NULL
), pthread_self(),
163 .keep_alive_pce_negotiated_timer_seconds
,
164 session
->session_id
);
165 reset_timer(session
->timer_id_keep_alive
);
171 /* A function pointer to this function was passed to pcep_socket_comm,
172 * so it will be called whenever the socket is closed. this function
173 * will be called by the socket_comm thread. */
174 void session_logic_conn_except_notifier(void *data
, int socket_fd
)
177 pcep_log(LOG_WARNING
,
178 "%s: Cannot handle conn_except with NULL data",
183 if (session_logic_handle_
->active
== false) {
186 "%s: Received a connection exception notification while the session logic is not active",
191 pcep_session
*session
= (pcep_session
*)data
;
194 "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]",
195 __func__
, time(NULL
), pthread_self(), socket_fd
,
196 session
->session_id
);
198 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
199 pcep_session_event
*socket_event
= create_session_event(session
);
200 socket_event
->socket_closed
= true;
201 queue_enqueue(session_logic_handle_
->session_event_queue
, socket_event
);
202 session_logic_handle_
->session_logic_condition
= true;
204 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
205 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
210 * this method is the timer expire handler, and will only
211 * pass the event to the session_logic loop and notify it
212 * that there is a timer available. this function will be
213 * called by the timers thread.
215 void session_logic_timer_expire_handler(void *data
, int timer_id
)
218 pcep_log(LOG_WARNING
, "%s: Cannot handle timer with NULL data",
223 if (session_logic_handle_
->active
== false) {
226 "%s: Received a timer expiration while the session logic is not active",
231 pcep_log(LOG_INFO
, "%s: [%ld-%ld] timer expired handler timer_id [%d]",
232 __func__
, time(NULL
), pthread_self(), timer_id
);
233 pcep_session_event
*expired_timer_event
=
234 create_session_event((pcep_session
*)data
);
235 expired_timer_event
->expired_timer_id
= timer_id
;
237 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
238 session_logic_handle_
->session_logic_condition
= true;
239 queue_enqueue(session_logic_handle_
->session_event_queue
,
240 expired_timer_event
);
242 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
243 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
248 * session_logic event loop
249 * this function is called upon thread creation from pcep_session_logic.c
251 void *session_logic_loop(void *data
)
254 pcep_log(LOG_WARNING
,
255 "%s: Cannot start session_logic_loop with NULL data",
261 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] Starting session_logic_loop thread",
262 __func__
, time(NULL
), pthread_self());
264 pcep_session_logic_handle
*session_logic_handle
=
265 (pcep_session_logic_handle
*)data
;
267 while (session_logic_handle
->active
) {
268 /* Mutex locking for session_logic_loop condition variable */
270 &(session_logic_handle
->session_logic_mutex
));
272 /* this internal loop helps avoid spurious interrupts */
273 while (!session_logic_handle
->session_logic_condition
) {
275 &(session_logic_handle
->session_logic_cond_var
),
276 &(session_logic_handle
->session_logic_mutex
));
279 pcep_session_event
*event
= queue_dequeue(
280 session_logic_handle
->session_event_queue
);
281 while (event
!= NULL
) {
282 if (event
->session
== NULL
) {
285 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
286 __func__
, time(NULL
), pthread_self(),
287 (event
->expired_timer_id
291 pceplib_free(PCEPLIB_INFRA
, event
);
292 event
= queue_dequeue(
294 ->session_event_queue
);
298 /* Check if the session still exists, and synchronize
299 * possible session destroy */
302 "%s: session_logic_loop checking session_list sessionPtr %p",
303 __func__
, event
->session
);
305 &(session_logic_handle
->session_list_mutex
));
306 if (ordered_list_find(
307 session_logic_handle
->session_list
,
312 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
313 __func__
, time(NULL
), pthread_self(),
314 (event
->expired_timer_id
318 pceplib_free(PCEPLIB_INFRA
, event
);
319 event
= queue_dequeue(
321 ->session_event_queue
);
322 pthread_mutex_unlock(
323 &(session_logic_handle
324 ->session_list_mutex
));
328 if (event
->expired_timer_id
!= TIMER_ID_NOT_SET
) {
329 handle_timer_event(event
);
332 if (event
->received_msg_list
!= NULL
) {
333 handle_socket_comm_event(event
);
336 pceplib_free(PCEPLIB_INFRA
, event
);
337 event
= queue_dequeue(
338 session_logic_handle
->session_event_queue
);
340 pthread_mutex_unlock(
341 &(session_logic_handle
->session_list_mutex
));
344 session_logic_handle
->session_logic_condition
= false;
345 pthread_mutex_unlock(
346 &(session_logic_handle
->session_logic_mutex
));
349 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] Finished session_logic_loop thread",
350 __func__
, time(NULL
), pthread_self());