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>
27 #include "pcep_session_logic.h"
28 #include "pcep_session_logic_internals.h"
29 #include "pcep_timers.h"
30 #include "pcep_utils_logging.h"
31 #include "pcep_utils_memory.h"
33 /* global var needed for callback handlers */
34 extern pcep_session_logic_handle
*session_logic_handle_
;
36 /* internal util function to create session_event's */
37 static pcep_session_event
*create_session_event(pcep_session
*session
)
39 pcep_session_event
*event
=
40 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_session_event
));
41 event
->session
= session
;
42 event
->expired_timer_id
= TIMER_ID_NOT_SET
;
43 event
->received_msg_list
= NULL
;
44 event
->socket_closed
= false;
50 /* A function pointer to this function is passed to pcep_socket_comm
51 * for each pcep_session creation, so it will be called whenever
52 * messages are ready to be read. This function will be called
53 * by the socket_comm thread.
54 * This function will decode the read PCEP message and give it
55 * to the session_logic_loop so it can be handled by the session_logic
57 int session_logic_msg_ready_handler(void *data
, int socket_fd
)
61 "%s: Cannot handle msg_ready with NULL data",
66 if (session_logic_handle_
->active
== false) {
69 "%s: Received a message ready notification while the session logic is not active",
74 pcep_session
*session
= (pcep_session
*)data
;
76 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
77 session_logic_handle_
->session_logic_condition
= true;
79 /* This event will ultimately be handled by handle_socket_comm_event()
80 * in pcep_session_logic_states.c */
81 pcep_session_event
*rcvd_msg_event
= create_session_event(session
);
84 double_linked_list
*msg_list
= pcep_msg_read(socket_fd
);
86 if (msg_list
== NULL
) {
87 /* The socket was closed, or there was a socket read error */
89 "%s: PCEP connection closed for session [%d]",
90 __func__
, session
->session_id
);
91 dll_destroy(msg_list
);
92 rcvd_msg_event
->socket_closed
= true;
93 socket_comm_session_teardown(session
->socket_comm_session
);
94 pcep_session_cancel_timers(session
);
95 session
->socket_comm_session
= NULL
;
96 session
->session_state
= SESSION_STATE_INITIALIZED
;
97 enqueue_event(session
, PCE_CLOSED_SOCKET
, NULL
);
98 } else if (msg_list
->num_entries
== 0) {
99 /* Invalid message received */
100 increment_unknown_message(session
);
102 /* Just logging the first of potentially several messages
104 struct pcep_message
*msg
=
105 ((struct pcep_message
*)msg_list
->head
->data
);
108 "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]",
109 __func__
, time(NULL
), pthread_self(),
110 msg
->msg_header
->type
, msg
->encoded_message_length
,
111 session
->session_id
);
113 rcvd_msg_event
->received_msg_list
= msg_list
;
114 msg_length
= msg
->encoded_message_length
;
117 queue_enqueue(session_logic_handle_
->session_event_queue
,
119 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
120 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
126 /* A function pointer to this function was passed to pcep_socket_comm,
127 * so it will be called when a message is sent. This is useful since
128 * message sending is asynchronous, and there are times that actions
129 * need to be performed only after a message has been sent. */
130 void session_logic_message_sent_handler(void *data
, int socket_fd
)
135 pcep_log(LOG_WARNING
,
136 "%s: Cannot handle msg_sent with NULL data", __func__
);
140 pcep_session
*session
= (pcep_session
*)data
;
141 if (session
->destroy_session_after_write
== true) {
142 /* Do not call destroy until all of the queued messages are
144 if (session
->socket_comm_session
!= NULL
145 && session
->socket_comm_session
->message_queue
->num_entries
147 destroy_pcep_session(session
);
150 /* Reset the keep alive timer for every message sent on
151 * the session, only if the session is not destroyed */
152 if (session
->timer_id_keep_alive
== TIMER_ID_NOT_SET
) {
155 "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
156 __func__
, time(NULL
), pthread_self(),
158 .keep_alive_pce_negotiated_timer_seconds
,
159 session
->session_id
);
160 session
->timer_id_keep_alive
= create_timer(
162 .keep_alive_pce_negotiated_timer_seconds
,
167 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
168 __func__
, time(NULL
), pthread_self(),
170 .keep_alive_pce_negotiated_timer_seconds
,
171 session
->session_id
);
172 reset_timer(session
->timer_id_keep_alive
);
178 /* A function pointer to this function was passed to pcep_socket_comm,
179 * so it will be called whenever the socket is closed. this function
180 * will be called by the socket_comm thread. */
181 void session_logic_conn_except_notifier(void *data
, int socket_fd
)
184 pcep_log(LOG_WARNING
,
185 "%s: Cannot handle conn_except with NULL data",
190 if (session_logic_handle_
->active
== false) {
193 "%s: Received a connection exception notification while the session logic is not active",
198 pcep_session
*session
= (pcep_session
*)data
;
201 "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]",
202 __func__
, time(NULL
), pthread_self(), socket_fd
,
203 session
->session_id
);
205 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
206 pcep_session_event
*socket_event
= create_session_event(session
);
207 socket_event
->socket_closed
= true;
208 queue_enqueue(session_logic_handle_
->session_event_queue
, socket_event
);
209 session_logic_handle_
->session_logic_condition
= true;
211 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
212 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
217 * this method is the timer expire handler, and will only
218 * pass the event to the session_logic loop and notify it
219 * that there is a timer available. this function will be
220 * called by the timers thread.
222 void session_logic_timer_expire_handler(void *data
, int timer_id
)
225 pcep_log(LOG_WARNING
, "%s: Cannot handle timer with NULL data",
230 if (session_logic_handle_
->active
== false) {
233 "%s: Received a timer expiration while the session logic is not active",
238 pcep_log(LOG_INFO
, "%s: [%ld-%ld] timer expired handler timer_id [%d]",
239 __func__
, time(NULL
), pthread_self(), timer_id
);
240 pcep_session_event
*expired_timer_event
=
241 create_session_event((pcep_session
*)data
);
242 expired_timer_event
->expired_timer_id
= timer_id
;
244 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
245 session_logic_handle_
->session_logic_condition
= true;
246 queue_enqueue(session_logic_handle_
->session_event_queue
,
247 expired_timer_event
);
249 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
250 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
255 * session_logic event loop
256 * this function is called upon thread creation from pcep_session_logic.c
258 void *session_logic_loop(void *data
)
261 pcep_log(LOG_WARNING
,
262 "%s: Cannot start session_logic_loop with NULL data",
268 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] Starting session_logic_loop thread",
269 __func__
, time(NULL
), pthread_self());
271 pcep_session_logic_handle
*session_logic_handle
=
272 (pcep_session_logic_handle
*)data
;
274 while (session_logic_handle
->active
) {
275 /* Mutex locking for session_logic_loop condition variable */
277 &(session_logic_handle
->session_logic_mutex
));
279 /* this internal loop helps avoid spurious interrupts */
280 while (!session_logic_handle
->session_logic_condition
) {
282 &(session_logic_handle
->session_logic_cond_var
),
283 &(session_logic_handle
->session_logic_mutex
));
286 pcep_session_event
*event
= queue_dequeue(
287 session_logic_handle
->session_event_queue
);
288 while (event
!= NULL
) {
289 if (event
->session
== NULL
) {
292 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
293 __func__
, time(NULL
), pthread_self(),
294 (event
->expired_timer_id
298 pceplib_free(PCEPLIB_INFRA
, event
);
299 event
= queue_dequeue(
301 ->session_event_queue
);
305 /* Check if the session still exists, and synchronize
306 * possible session destroy */
309 "%s: session_logic_loop checking session_list sessionPtr %p",
310 __func__
, event
->session
);
312 &(session_logic_handle
->session_list_mutex
));
313 if (ordered_list_find(
314 session_logic_handle
->session_list
,
319 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
320 __func__
, time(NULL
), pthread_self(),
321 (event
->expired_timer_id
325 pceplib_free(PCEPLIB_INFRA
, event
);
326 event
= queue_dequeue(
328 ->session_event_queue
);
329 pthread_mutex_unlock(
330 &(session_logic_handle_
331 ->session_list_mutex
));
335 if (event
->expired_timer_id
!= TIMER_ID_NOT_SET
) {
336 handle_timer_event(event
);
339 if (event
->received_msg_list
!= NULL
) {
340 handle_socket_comm_event(event
);
343 pceplib_free(PCEPLIB_INFRA
, event
);
344 event
= queue_dequeue(
345 session_logic_handle
->session_event_queue
);
347 pthread_mutex_unlock(
348 &(session_logic_handle_
->session_list_mutex
));
351 session_logic_handle
->session_logic_condition
= false;
352 pthread_mutex_unlock(
353 &(session_logic_handle
->session_logic_mutex
));
356 pcep_log(LOG_NOTICE
, "%s: [%ld-%ld] Finished session_logic_loop thread",
357 __func__
, time(NULL
), pthread_self());