]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_session_logic_loop.c
pceplib: Fixing coverity messages.
[mirror_frr.git] / pceplib / pcep_session_logic_loop.c
1 /*
2 * This file is part of the PCEPlib, a PCEP protocol library.
3 *
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author : Brady Johnson <brady@voltanet.io>
20 *
21 */
22
23 #include <pthread.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26
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"
32
33 /* global var needed for callback handlers */
34 extern pcep_session_logic_handle *session_logic_handle_;
35
36 /* internal util function to create session_event's */
37 static pcep_session_event *create_session_event(pcep_session *session)
38 {
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;
45
46 return event;
47 }
48
49
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
56 * state machine. */
57 int session_logic_msg_ready_handler(void *data, int socket_fd)
58 {
59 if (data == NULL) {
60 pcep_log(LOG_WARNING,
61 "%s: Cannot handle msg_ready with NULL data",
62 __func__);
63 return -1;
64 }
65
66 if (session_logic_handle_->active == false) {
67 pcep_log(
68 LOG_WARNING,
69 "%s: Received a message ready notification while the session logic is not active",
70 __func__);
71 return -1;
72 }
73
74 pcep_session *session = (pcep_session *)data;
75
76 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
77 session_logic_handle_->session_logic_condition = true;
78
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);
82
83 int msg_length = 0;
84 double_linked_list *msg_list = pcep_msg_read(socket_fd);
85
86 if (msg_list == NULL) {
87 /* The socket was closed, or there was a socket read error */
88 pcep_log(LOG_INFO,
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);
101 dll_destroy_with_data(msg_list);
102 } else {
103 /* Just logging the first of potentially several messages
104 * received */
105 struct pcep_message *msg =
106 ((struct pcep_message *)msg_list->head->data);
107 pcep_log(
108 LOG_INFO,
109 "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]",
110 __func__, time(NULL), pthread_self(),
111 msg->msg_header->type, msg->encoded_message_length,
112 session->session_id);
113
114 rcvd_msg_event->received_msg_list = msg_list;
115 msg_length = msg->encoded_message_length;
116 }
117
118 queue_enqueue(session_logic_handle_->session_event_queue,
119 rcvd_msg_event);
120 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
121 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
122
123 return msg_length;
124 }
125
126
127 /* A function pointer to this function was passed to pcep_socket_comm,
128 * so it will be called when a message is sent. This is useful since
129 * message sending is asynchronous, and there are times that actions
130 * need to be performed only after a message has been sent. */
131 void session_logic_message_sent_handler(void *data, int socket_fd)
132 {
133 (void)socket_fd;
134
135 if (data == NULL) {
136 pcep_log(LOG_WARNING,
137 "%s: Cannot handle msg_sent with NULL data", __func__);
138 return;
139 }
140
141 pcep_session *session = (pcep_session *)data;
142 if (session->destroy_session_after_write == true) {
143 /* Do not call destroy until all of the queued messages are
144 * written */
145 if (session->socket_comm_session != NULL
146 && session->socket_comm_session->message_queue->num_entries
147 == 0) {
148 destroy_pcep_session(session);
149 }
150 } else {
151 /* Reset the keep alive timer for every message sent on
152 * the session, only if the session is not destroyed */
153 if (session->timer_id_keep_alive == TIMER_ID_NOT_SET) {
154 pcep_log(
155 LOG_INFO,
156 "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
157 __func__, time(NULL), pthread_self(),
158 session->pcc_config
159 .keep_alive_pce_negotiated_timer_seconds,
160 session->session_id);
161 session->timer_id_keep_alive = create_timer(
162 session->pcc_config
163 .keep_alive_pce_negotiated_timer_seconds,
164 session);
165 } else {
166 pcep_log(
167 LOG_INFO,
168 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
169 __func__, time(NULL), pthread_self(),
170 session->pcc_config
171 .keep_alive_pce_negotiated_timer_seconds,
172 session->session_id);
173 reset_timer(session->timer_id_keep_alive);
174 }
175 }
176 }
177
178
179 /* A function pointer to this function was passed to pcep_socket_comm,
180 * so it will be called whenever the socket is closed. this function
181 * will be called by the socket_comm thread. */
182 void session_logic_conn_except_notifier(void *data, int socket_fd)
183 {
184 if (data == NULL) {
185 pcep_log(LOG_WARNING,
186 "%s: Cannot handle conn_except with NULL data",
187 __func__);
188 return;
189 }
190
191 if (session_logic_handle_->active == false) {
192 pcep_log(
193 LOG_WARNING,
194 "%s: Received a connection exception notification while the session logic is not active",
195 __func__);
196 return;
197 }
198
199 pcep_session *session = (pcep_session *)data;
200 pcep_log(
201 LOG_INFO,
202 "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]",
203 __func__, time(NULL), pthread_self(), socket_fd,
204 session->session_id);
205
206 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
207 pcep_session_event *socket_event = create_session_event(session);
208 socket_event->socket_closed = true;
209 queue_enqueue(session_logic_handle_->session_event_queue, socket_event);
210 session_logic_handle_->session_logic_condition = true;
211
212 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
213 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
214 }
215
216
217 /*
218 * this method is the timer expire handler, and will only
219 * pass the event to the session_logic loop and notify it
220 * that there is a timer available. this function will be
221 * called by the timers thread.
222 */
223 void session_logic_timer_expire_handler(void *data, int timer_id)
224 {
225 if (data == NULL) {
226 pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data",
227 __func__);
228 return;
229 }
230
231 if (session_logic_handle_->active == false) {
232 pcep_log(
233 LOG_WARNING,
234 "%s: Received a timer expiration while the session logic is not active",
235 __func__);
236 return;
237 }
238
239 pcep_log(LOG_INFO, "%s: [%ld-%ld] timer expired handler timer_id [%d]",
240 __func__, time(NULL), pthread_self(), timer_id);
241 pcep_session_event *expired_timer_event =
242 create_session_event((pcep_session *)data);
243 expired_timer_event->expired_timer_id = timer_id;
244
245 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
246 session_logic_handle_->session_logic_condition = true;
247 queue_enqueue(session_logic_handle_->session_event_queue,
248 expired_timer_event);
249
250 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
251 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
252 }
253
254
255 /*
256 * session_logic event loop
257 * this function is called upon thread creation from pcep_session_logic.c
258 */
259 void *session_logic_loop(void *data)
260 {
261 if (data == NULL) {
262 pcep_log(LOG_WARNING,
263 "%s: Cannot start session_logic_loop with NULL data",
264 __func__);
265
266 return NULL;
267 }
268
269 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread",
270 __func__, time(NULL), pthread_self());
271
272 pcep_session_logic_handle *session_logic_handle =
273 (pcep_session_logic_handle *)data;
274
275 while (session_logic_handle->active) {
276 /* Mutex locking for session_logic_loop condition variable */
277 pthread_mutex_lock(
278 &(session_logic_handle->session_logic_mutex));
279
280 /* this internal loop helps avoid spurious interrupts */
281 while (!session_logic_handle->session_logic_condition) {
282 pthread_cond_wait(
283 &(session_logic_handle->session_logic_cond_var),
284 &(session_logic_handle->session_logic_mutex));
285 }
286
287 pcep_session_event *event = queue_dequeue(
288 session_logic_handle->session_event_queue);
289 while (event != NULL) {
290 if (event->session == NULL) {
291 pcep_log(
292 LOG_INFO,
293 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
294 __func__, time(NULL), pthread_self(),
295 (event->expired_timer_id
296 != TIMER_ID_NOT_SET)
297 ? "timer"
298 : "message");
299 pceplib_free(PCEPLIB_INFRA, event);
300 event = queue_dequeue(
301 session_logic_handle
302 ->session_event_queue);
303 continue;
304 }
305
306 /* Check if the session still exists, and synchronize
307 * possible session destroy */
308 pcep_log(
309 LOG_DEBUG,
310 "%s: session_logic_loop checking session_list sessionPtr %p",
311 __func__, event->session);
312 pthread_mutex_lock(
313 &(session_logic_handle->session_list_mutex));
314 if (ordered_list_find(
315 session_logic_handle->session_list,
316 event->session)
317 == NULL) {
318 pcep_log(
319 LOG_INFO,
320 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
321 __func__, time(NULL), pthread_self(),
322 (event->expired_timer_id
323 != TIMER_ID_NOT_SET)
324 ? "timer"
325 : "message");
326 pceplib_free(PCEPLIB_INFRA, event);
327 event = queue_dequeue(
328 session_logic_handle
329 ->session_event_queue);
330 pthread_mutex_unlock(
331 &(session_logic_handle_
332 ->session_list_mutex));
333 continue;
334 }
335
336 if (event->expired_timer_id != TIMER_ID_NOT_SET) {
337 handle_timer_event(event);
338 }
339
340 if (event->received_msg_list != NULL) {
341 handle_socket_comm_event(event);
342 }
343
344 pceplib_free(PCEPLIB_INFRA, event);
345 event = queue_dequeue(
346 session_logic_handle->session_event_queue);
347
348 pthread_mutex_unlock(
349 &(session_logic_handle_->session_list_mutex));
350 }
351
352 session_logic_handle->session_logic_condition = false;
353 pthread_mutex_unlock(
354 &(session_logic_handle->session_logic_mutex));
355 }
356
357 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread",
358 __func__, time(NULL), pthread_self());
359
360 return NULL;
361 }