2 * This file is part of the PCEPlib, a PCEP protocol library.
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * Author : Brady Johnson <brady@voltanet.io>
31 #include "pcep_session_logic.h"
32 #include "pcep_session_logic_internals.h"
33 #include "pcep_timers.h"
34 #include "pcep_utils_logging.h"
35 #include "pcep_utils_memory.h"
37 /* global var needed for callback handlers */
38 extern pcep_session_logic_handle
*session_logic_handle_
;
40 /* internal util function to create session_event's */
41 static pcep_session_event
*create_session_event(pcep_session
*session
)
43 pcep_session_event
*event
=
44 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_session_event
));
45 event
->session
= session
;
46 event
->expired_timer_id
= TIMER_ID_NOT_SET
;
47 event
->received_msg_list
= NULL
;
48 event
->socket_closed
= false;
54 /* A function pointer to this function is passed to pcep_socket_comm
55 * for each pcep_session creation, so it will be called whenever
56 * messages are ready to be read. This function will be called
57 * by the socket_comm thread.
58 * This function will decode the read PCEP message and give it
59 * to the session_logic_loop so it can be handled by the session_logic
61 int session_logic_msg_ready_handler(void *data
, int socket_fd
)
65 "%s: Cannot handle msg_ready with NULL data",
70 if (session_logic_handle_
->active
== false) {
73 "%s: Received a message ready notification while the session logic is not active",
78 pcep_session
*session
= (pcep_session
*)data
;
80 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
81 session_logic_handle_
->session_logic_condition
= true;
83 /* This event will ultimately be handled by handle_socket_comm_event()
84 * in pcep_session_logic_states.c */
85 pcep_session_event
*rcvd_msg_event
= create_session_event(session
);
88 double_linked_list
*msg_list
= pcep_msg_read(socket_fd
);
90 if (msg_list
== NULL
) {
91 /* The socket was closed, or there was a socket read error */
93 "%s: PCEP connection closed for session [%d]",
94 __func__
, session
->session_id
);
95 dll_destroy(msg_list
);
96 rcvd_msg_event
->socket_closed
= true;
97 socket_comm_session_teardown(session
->socket_comm_session
);
98 pcep_session_cancel_timers(session
);
99 session
->socket_comm_session
= NULL
;
100 session
->session_state
= SESSION_STATE_INITIALIZED
;
101 enqueue_event(session
, PCE_CLOSED_SOCKET
, NULL
);
102 } else if (msg_list
->num_entries
== 0) {
103 /* Invalid message received */
104 increment_unknown_message(session
);
105 dll_destroy_with_data(msg_list
);
107 /* Just logging the first of potentially several messages
109 struct pcep_message
*msg
=
110 ((struct pcep_message
*)msg_list
->head
->data
);
113 "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]",
114 __func__
, time(NULL
), pthread_self(),
115 msg
->msg_header
->type
, msg
->encoded_message_length
,
116 session
->session_id
);
118 rcvd_msg_event
->received_msg_list
= msg_list
;
119 msg_length
= msg
->encoded_message_length
;
122 queue_enqueue(session_logic_handle_
->session_event_queue
,
124 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
125 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
131 /* A function pointer to this function was passed to pcep_socket_comm,
132 * so it will be called when a message is sent. This is useful since
133 * message sending is asynchronous, and there are times that actions
134 * need to be performed only after a message has been sent. */
135 void session_logic_message_sent_handler(void *data
, int socket_fd
)
140 pcep_log(LOG_WARNING
,
141 "%s: Cannot handle msg_sent with NULL data", __func__
);
145 pcep_session
*session
= (pcep_session
*)data
;
146 if (session
->destroy_session_after_write
== true) {
147 /* Do not call destroy until all of the queued messages are
149 if (session
->socket_comm_session
!= NULL
150 && session
->socket_comm_session
->message_queue
->num_entries
152 destroy_pcep_session(session
);
155 /* Reset the keep alive timer for every message sent on
156 * the session, only if the session is not destroyed */
157 if (session
->timer_id_keep_alive
== TIMER_ID_NOT_SET
) {
160 "%s: [%ld-%ld] pcep_session_logic set 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 session
->timer_id_keep_alive
= create_timer(
167 .keep_alive_pce_negotiated_timer_seconds
,
172 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
173 __func__
, time(NULL
), pthread_self(),
175 .keep_alive_pce_negotiated_timer_seconds
,
176 session
->session_id
);
177 reset_timer(session
->timer_id_keep_alive
);
183 /* A function pointer to this function was passed to pcep_socket_comm,
184 * so it will be called whenever the socket is closed. this function
185 * will be called by the socket_comm thread. */
186 void session_logic_conn_except_notifier(void *data
, int socket_fd
)
189 pcep_log(LOG_WARNING
,
190 "%s: Cannot handle conn_except with NULL data",
195 if (session_logic_handle_
->active
== false) {
198 "%s: Received a connection exception notification while the session logic is not active",
203 pcep_session
*session
= (pcep_session
*)data
;
206 "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]",
207 __func__
, time(NULL
), pthread_self(), socket_fd
,
208 session
->session_id
);
210 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
211 pcep_session_event
*socket_event
= create_session_event(session
);
212 socket_event
->socket_closed
= true;
213 queue_enqueue(session_logic_handle_
->session_event_queue
, socket_event
);
214 session_logic_handle_
->session_logic_condition
= true;
216 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
217 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
222 * this method is the timer expire handler, and will only
223 * pass the event to the session_logic loop and notify it
224 * that there is a timer available. this function will be
225 * called by the timers thread.
227 void session_logic_timer_expire_handler(void *data
, int timer_id
)
230 pcep_log(LOG_WARNING
, "%s: Cannot handle timer with NULL data",
235 if (session_logic_handle_
->active
== false) {
238 "%s: Received a timer expiration while the session logic is not active",
243 pcep_log(LOG_INFO
, "%s: [%ld-%ld] timer expired handler timer_id [%d]",
244 __func__
, time(NULL
), pthread_self(), timer_id
);
245 pcep_session_event
*expired_timer_event
=
246 create_session_event((pcep_session
*)data
);
247 expired_timer_event
->expired_timer_id
= timer_id
;
249 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
250 session_logic_handle_
->session_logic_condition
= true;
251 queue_enqueue(session_logic_handle_
->session_event_queue
,
252 expired_timer_event
);
254 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
255 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
260 * session_logic event loop
261 * this function is called upon thread creation from pcep_session_logic.c
263 void *session_logic_loop(void *data
)
266 pcep_log(LOG_WARNING
,
267 "%s: Cannot start session_logic_loop with NULL data",
273 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] Starting session_logic_loop thread",
274 __func__
, time(NULL
), pthread_self());
276 pcep_session_logic_handle
*session_logic_handle
=
277 (pcep_session_logic_handle
*)data
;
279 while (session_logic_handle
->active
) {
280 /* Mutex locking for session_logic_loop condition variable */
282 &(session_logic_handle
->session_logic_mutex
));
284 /* this internal loop helps avoid spurious interrupts */
285 while (!session_logic_handle
->session_logic_condition
) {
287 &(session_logic_handle
->session_logic_cond_var
),
288 &(session_logic_handle
->session_logic_mutex
));
291 pcep_session_event
*event
= queue_dequeue(
292 session_logic_handle
->session_event_queue
);
293 while (event
!= NULL
) {
294 if (event
->session
== NULL
) {
297 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
298 __func__
, time(NULL
), pthread_self(),
299 (event
->expired_timer_id
303 pceplib_free(PCEPLIB_INFRA
, event
);
304 event
= queue_dequeue(
306 ->session_event_queue
);
310 /* Check if the session still exists, and synchronize
311 * possible session destroy */
314 "%s: session_logic_loop checking session_list sessionPtr %p",
315 __func__
, event
->session
);
317 &(session_logic_handle
->session_list_mutex
));
318 if (ordered_list_find(
319 session_logic_handle
->session_list
,
324 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
325 __func__
, time(NULL
), pthread_self(),
326 (event
->expired_timer_id
330 pceplib_free(PCEPLIB_INFRA
, event
);
331 event
= queue_dequeue(
333 ->session_event_queue
);
334 pthread_mutex_unlock(
335 &(session_logic_handle_
336 ->session_list_mutex
));
340 if (event
->expired_timer_id
!= TIMER_ID_NOT_SET
) {
341 handle_timer_event(event
);
344 if (event
->received_msg_list
!= NULL
) {
345 handle_socket_comm_event(event
);
348 pceplib_free(PCEPLIB_INFRA
, event
);
349 event
= queue_dequeue(
350 session_logic_handle
->session_event_queue
);
352 pthread_mutex_unlock(
353 &(session_logic_handle_
->session_list_mutex
));
356 session_logic_handle
->session_logic_condition
= false;
357 pthread_mutex_unlock(
358 &(session_logic_handle
->session_logic_mutex
));
361 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] Finished session_logic_loop thread",
362 __func__
, time(NULL
), pthread_self());