]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_session_logic_loop.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / pceplib / pcep_session_logic_loop.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3 * This file is part of the PCEPlib, a PCEP protocol library.
4 *
5 * Copyright (C) 2020 Volta Networks https://voltanet.io/
6 *
7 * Author : Brady Johnson <brady@voltanet.io>
8 *
9 */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <pthread.h>
16 #include <stdbool.h>
17 #include <stdio.h>
18
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"
24
25 /* global var needed for callback handlers */
26 extern pcep_session_logic_handle *session_logic_handle_;
27
28 /* internal util function to create session_event's */
29 static pcep_session_event *create_session_event(pcep_session *session)
30 {
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;
37
38 return event;
39 }
40
41
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
48 * state machine. */
49 int session_logic_msg_ready_handler(void *data, int socket_fd)
50 {
51 if (data == NULL) {
52 pcep_log(LOG_WARNING,
53 "%s: Cannot handle msg_ready with NULL data",
54 __func__);
55 return -1;
56 }
57
58 if (session_logic_handle_->active == false) {
59 pcep_log(
60 LOG_WARNING,
61 "%s: Received a message ready notification while the session logic is not active",
62 __func__);
63 return -1;
64 }
65
66 pcep_session *session = (pcep_session *)data;
67
68 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
69 session_logic_handle_->session_logic_condition = true;
70
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);
74
75 int msg_length = 0;
76 double_linked_list *msg_list = pcep_msg_read(socket_fd);
77
78 if (msg_list == NULL) {
79 /* The socket was closed, or there was a socket read error */
80 pcep_log(LOG_INFO,
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);
94 } else {
95 /* Just logging the first of potentially several messages
96 * received */
97 struct pcep_message *msg =
98 ((struct pcep_message *)msg_list->head->data);
99 pcep_log(
100 LOG_INFO,
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);
105
106 rcvd_msg_event->received_msg_list = msg_list;
107 msg_length = msg->encoded_message_length;
108 }
109
110 queue_enqueue(session_logic_handle_->session_event_queue,
111 rcvd_msg_event);
112 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
113 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
114
115 return msg_length;
116 }
117
118
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)
124 {
125 (void)socket_fd;
126
127 if (data == NULL) {
128 pcep_log(LOG_WARNING,
129 "%s: Cannot handle msg_sent with NULL data", __func__);
130 return;
131 }
132
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
136 * written */
137 if (session->socket_comm_session != NULL
138 && session->socket_comm_session->message_queue->num_entries
139 == 0) {
140 destroy_pcep_session(session);
141 }
142 } else {
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) {
146 pcep_log(
147 LOG_INFO,
148 "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
149 __func__, time(NULL), pthread_self(),
150 session->pcc_config
151 .keep_alive_pce_negotiated_timer_seconds,
152 session->session_id);
153 session->timer_id_keep_alive = create_timer(
154 session->pcc_config
155 .keep_alive_pce_negotiated_timer_seconds,
156 session);
157 } else {
158 pcep_log(
159 LOG_INFO,
160 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
161 __func__, time(NULL), pthread_self(),
162 session->pcc_config
163 .keep_alive_pce_negotiated_timer_seconds,
164 session->session_id);
165 reset_timer(session->timer_id_keep_alive);
166 }
167 }
168 }
169
170
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)
175 {
176 if (data == NULL) {
177 pcep_log(LOG_WARNING,
178 "%s: Cannot handle conn_except with NULL data",
179 __func__);
180 return;
181 }
182
183 if (session_logic_handle_->active == false) {
184 pcep_log(
185 LOG_WARNING,
186 "%s: Received a connection exception notification while the session logic is not active",
187 __func__);
188 return;
189 }
190
191 pcep_session *session = (pcep_session *)data;
192 pcep_log(
193 LOG_INFO,
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);
197
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;
203
204 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
205 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
206 }
207
208
209 /*
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.
214 */
215 void session_logic_timer_expire_handler(void *data, int timer_id)
216 {
217 if (data == NULL) {
218 pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data",
219 __func__);
220 return;
221 }
222
223 if (session_logic_handle_->active == false) {
224 pcep_log(
225 LOG_WARNING,
226 "%s: Received a timer expiration while the session logic is not active",
227 __func__);
228 return;
229 }
230
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;
236
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);
241
242 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
243 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
244 }
245
246
247 /*
248 * session_logic event loop
249 * this function is called upon thread creation from pcep_session_logic.c
250 */
251 void *session_logic_loop(void *data)
252 {
253 if (data == NULL) {
254 pcep_log(LOG_WARNING,
255 "%s: Cannot start session_logic_loop with NULL data",
256 __func__);
257
258 return NULL;
259 }
260
261 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread",
262 __func__, time(NULL), pthread_self());
263
264 pcep_session_logic_handle *session_logic_handle =
265 (pcep_session_logic_handle *)data;
266
267 while (session_logic_handle->active) {
268 /* Mutex locking for session_logic_loop condition variable */
269 pthread_mutex_lock(
270 &(session_logic_handle->session_logic_mutex));
271
272 /* this internal loop helps avoid spurious interrupts */
273 while (!session_logic_handle->session_logic_condition) {
274 pthread_cond_wait(
275 &(session_logic_handle->session_logic_cond_var),
276 &(session_logic_handle->session_logic_mutex));
277 }
278
279 pcep_session_event *event = queue_dequeue(
280 session_logic_handle->session_event_queue);
281 while (event != NULL) {
282 if (event->session == NULL) {
283 pcep_log(
284 LOG_INFO,
285 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
286 __func__, time(NULL), pthread_self(),
287 (event->expired_timer_id
288 != TIMER_ID_NOT_SET)
289 ? "timer"
290 : "message");
291 pceplib_free(PCEPLIB_INFRA, event);
292 event = queue_dequeue(
293 session_logic_handle
294 ->session_event_queue);
295 continue;
296 }
297
298 /* Check if the session still exists, and synchronize
299 * possible session destroy */
300 pcep_log(
301 LOG_DEBUG,
302 "%s: session_logic_loop checking session_list sessionPtr %p",
303 __func__, event->session);
304 pthread_mutex_lock(
305 &(session_logic_handle->session_list_mutex));
306 if (ordered_list_find(
307 session_logic_handle->session_list,
308 event->session)
309 == NULL) {
310 pcep_log(
311 LOG_INFO,
312 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
313 __func__, time(NULL), pthread_self(),
314 (event->expired_timer_id
315 != TIMER_ID_NOT_SET)
316 ? "timer"
317 : "message");
318 pceplib_free(PCEPLIB_INFRA, event);
319 event = queue_dequeue(
320 session_logic_handle
321 ->session_event_queue);
322 pthread_mutex_unlock(
323 &(session_logic_handle
324 ->session_list_mutex));
325 continue;
326 }
327
328 if (event->expired_timer_id != TIMER_ID_NOT_SET) {
329 handle_timer_event(event);
330 }
331
332 if (event->received_msg_list != NULL) {
333 handle_socket_comm_event(event);
334 }
335
336 pceplib_free(PCEPLIB_INFRA, event);
337 event = queue_dequeue(
338 session_logic_handle->session_event_queue);
339
340 pthread_mutex_unlock(
341 &(session_logic_handle->session_list_mutex));
342 }
343
344 session_logic_handle->session_logic_condition = false;
345 pthread_mutex_unlock(
346 &(session_logic_handle->session_logic_mutex));
347 }
348
349 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread",
350 __func__, time(NULL), pthread_self());
351
352 return NULL;
353 }