]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_session_logic_loop.c
pceplib: Integrate pcelib into frr
[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 } else {
102 /* Just logging the first of potentially several messages
103 * received */
104 struct pcep_message *msg =
105 ((struct pcep_message *)msg_list->head->data);
106 pcep_log(
107 LOG_INFO,
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);
112
113 rcvd_msg_event->received_msg_list = msg_list;
114 msg_length = msg->encoded_message_length;
115 }
116
117 queue_enqueue(session_logic_handle_->session_event_queue,
118 rcvd_msg_event);
119 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
120 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
121
122 return msg_length;
123 }
124
125
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)
131 {
132 (void)socket_fd;
133
134 if (data == NULL) {
135 pcep_log(LOG_WARNING,
136 "%s: Cannot handle msg_sent with NULL data", __func__);
137 return;
138 }
139
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
143 * written */
144 if (session->socket_comm_session != NULL
145 && session->socket_comm_session->message_queue->num_entries
146 == 0) {
147 destroy_pcep_session(session);
148 }
149 } else {
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) {
153 pcep_log(
154 LOG_INFO,
155 "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]",
156 __func__, time(NULL), pthread_self(),
157 session->pcc_config
158 .keep_alive_pce_negotiated_timer_seconds,
159 session->session_id);
160 session->timer_id_keep_alive = create_timer(
161 session->pcc_config
162 .keep_alive_pce_negotiated_timer_seconds,
163 session);
164 } else {
165 pcep_log(
166 LOG_INFO,
167 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
168 __func__, time(NULL), pthread_self(),
169 session->pcc_config
170 .keep_alive_pce_negotiated_timer_seconds,
171 session->session_id);
172 reset_timer(session->timer_id_keep_alive);
173 }
174 }
175 }
176
177
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)
182 {
183 if (data == NULL) {
184 pcep_log(LOG_WARNING,
185 "%s: Cannot handle conn_except with NULL data",
186 __func__);
187 return;
188 }
189
190 if (session_logic_handle_->active == false) {
191 pcep_log(
192 LOG_WARNING,
193 "%s: Received a connection exception notification while the session logic is not active",
194 __func__);
195 return;
196 }
197
198 pcep_session *session = (pcep_session *)data;
199 pcep_log(
200 LOG_INFO,
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);
204
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;
210
211 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
212 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
213 }
214
215
216 /*
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.
221 */
222 void session_logic_timer_expire_handler(void *data, int timer_id)
223 {
224 if (data == NULL) {
225 pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data",
226 __func__);
227 return;
228 }
229
230 if (session_logic_handle_->active == false) {
231 pcep_log(
232 LOG_WARNING,
233 "%s: Received a timer expiration while the session logic is not active",
234 __func__);
235 return;
236 }
237
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;
243
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);
248
249 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
250 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
251 }
252
253
254 /*
255 * session_logic event loop
256 * this function is called upon thread creation from pcep_session_logic.c
257 */
258 void *session_logic_loop(void *data)
259 {
260 if (data == NULL) {
261 pcep_log(LOG_WARNING,
262 "%s: Cannot start session_logic_loop with NULL data",
263 __func__);
264
265 return NULL;
266 }
267
268 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread",
269 __func__, time(NULL), pthread_self());
270
271 pcep_session_logic_handle *session_logic_handle =
272 (pcep_session_logic_handle *)data;
273
274 while (session_logic_handle->active) {
275 /* Mutex locking for session_logic_loop condition variable */
276 pthread_mutex_lock(
277 &(session_logic_handle->session_logic_mutex));
278
279 /* this internal loop helps avoid spurious interrupts */
280 while (!session_logic_handle->session_logic_condition) {
281 pthread_cond_wait(
282 &(session_logic_handle->session_logic_cond_var),
283 &(session_logic_handle->session_logic_mutex));
284 }
285
286 pcep_session_event *event = queue_dequeue(
287 session_logic_handle->session_event_queue);
288 while (event != NULL) {
289 if (event->session == NULL) {
290 pcep_log(
291 LOG_INFO,
292 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
293 __func__, time(NULL), pthread_self(),
294 (event->expired_timer_id
295 != TIMER_ID_NOT_SET)
296 ? "timer"
297 : "message");
298 pceplib_free(PCEPLIB_INFRA, event);
299 event = queue_dequeue(
300 session_logic_handle
301 ->session_event_queue);
302 continue;
303 }
304
305 /* Check if the session still exists, and synchronize
306 * possible session destroy */
307 pcep_log(
308 LOG_DEBUG,
309 "%s: session_logic_loop checking session_list sessionPtr %p",
310 __func__, event->session);
311 pthread_mutex_lock(
312 &(session_logic_handle->session_list_mutex));
313 if (ordered_list_find(
314 session_logic_handle->session_list,
315 event->session)
316 == NULL) {
317 pcep_log(
318 LOG_INFO,
319 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
320 __func__, time(NULL), pthread_self(),
321 (event->expired_timer_id
322 != TIMER_ID_NOT_SET)
323 ? "timer"
324 : "message");
325 pceplib_free(PCEPLIB_INFRA, event);
326 event = queue_dequeue(
327 session_logic_handle
328 ->session_event_queue);
329 pthread_mutex_unlock(
330 &(session_logic_handle_
331 ->session_list_mutex));
332 continue;
333 }
334
335 if (event->expired_timer_id != TIMER_ID_NOT_SET) {
336 handle_timer_event(event);
337 }
338
339 if (event->received_msg_list != NULL) {
340 handle_socket_comm_event(event);
341 }
342
343 pceplib_free(PCEPLIB_INFRA, event);
344 event = queue_dequeue(
345 session_logic_handle->session_event_queue);
346
347 pthread_mutex_unlock(
348 &(session_logic_handle_->session_list_mutex));
349 }
350
351 session_logic_handle->session_logic_condition = false;
352 pthread_mutex_unlock(
353 &(session_logic_handle->session_logic_mutex));
354 }
355
356 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread",
357 __func__, time(NULL), pthread_self());
358
359 return NULL;
360 }