]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_session_logic_loop.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <pthread.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30
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"
36
37 /* global var needed for callback handlers */
38 extern pcep_session_logic_handle *session_logic_handle_;
39
40 /* internal util function to create session_event's */
41 static pcep_session_event *create_session_event(pcep_session *session)
42 {
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;
49
50 return event;
51 }
52
53
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
60 * state machine. */
61 int session_logic_msg_ready_handler(void *data, int socket_fd)
62 {
63 if (data == NULL) {
64 pcep_log(LOG_WARNING,
65 "%s: Cannot handle msg_ready with NULL data",
66 __func__);
67 return -1;
68 }
69
70 if (session_logic_handle_->active == false) {
71 pcep_log(
72 LOG_WARNING,
73 "%s: Received a message ready notification while the session logic is not active",
74 __func__);
75 return -1;
76 }
77
78 pcep_session *session = (pcep_session *)data;
79
80 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
81 session_logic_handle_->session_logic_condition = true;
82
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);
86
87 int msg_length = 0;
88 double_linked_list *msg_list = pcep_msg_read(socket_fd);
89
90 if (msg_list == NULL) {
91 /* The socket was closed, or there was a socket read error */
92 pcep_log(LOG_INFO,
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);
106 } else {
107 /* Just logging the first of potentially several messages
108 * received */
109 struct pcep_message *msg =
110 ((struct pcep_message *)msg_list->head->data);
111 pcep_log(
112 LOG_INFO,
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);
117
118 rcvd_msg_event->received_msg_list = msg_list;
119 msg_length = msg->encoded_message_length;
120 }
121
122 queue_enqueue(session_logic_handle_->session_event_queue,
123 rcvd_msg_event);
124 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
125 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
126
127 return msg_length;
128 }
129
130
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)
136 {
137 (void)socket_fd;
138
139 if (data == NULL) {
140 pcep_log(LOG_WARNING,
141 "%s: Cannot handle msg_sent with NULL data", __func__);
142 return;
143 }
144
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
148 * written */
149 if (session->socket_comm_session != NULL
150 && session->socket_comm_session->message_queue->num_entries
151 == 0) {
152 destroy_pcep_session(session);
153 }
154 } else {
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) {
158 pcep_log(
159 LOG_INFO,
160 "%s: [%ld-%ld] pcep_session_logic set 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 session->timer_id_keep_alive = create_timer(
166 session->pcc_config
167 .keep_alive_pce_negotiated_timer_seconds,
168 session);
169 } else {
170 pcep_log(
171 LOG_INFO,
172 "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]",
173 __func__, time(NULL), pthread_self(),
174 session->pcc_config
175 .keep_alive_pce_negotiated_timer_seconds,
176 session->session_id);
177 reset_timer(session->timer_id_keep_alive);
178 }
179 }
180 }
181
182
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)
187 {
188 if (data == NULL) {
189 pcep_log(LOG_WARNING,
190 "%s: Cannot handle conn_except with NULL data",
191 __func__);
192 return;
193 }
194
195 if (session_logic_handle_->active == false) {
196 pcep_log(
197 LOG_WARNING,
198 "%s: Received a connection exception notification while the session logic is not active",
199 __func__);
200 return;
201 }
202
203 pcep_session *session = (pcep_session *)data;
204 pcep_log(
205 LOG_INFO,
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);
209
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;
215
216 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
217 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
218 }
219
220
221 /*
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.
226 */
227 void session_logic_timer_expire_handler(void *data, int timer_id)
228 {
229 if (data == NULL) {
230 pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data",
231 __func__);
232 return;
233 }
234
235 if (session_logic_handle_->active == false) {
236 pcep_log(
237 LOG_WARNING,
238 "%s: Received a timer expiration while the session logic is not active",
239 __func__);
240 return;
241 }
242
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;
248
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);
253
254 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
255 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
256 }
257
258
259 /*
260 * session_logic event loop
261 * this function is called upon thread creation from pcep_session_logic.c
262 */
263 void *session_logic_loop(void *data)
264 {
265 if (data == NULL) {
266 pcep_log(LOG_WARNING,
267 "%s: Cannot start session_logic_loop with NULL data",
268 __func__);
269
270 return NULL;
271 }
272
273 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread",
274 __func__, time(NULL), pthread_self());
275
276 pcep_session_logic_handle *session_logic_handle =
277 (pcep_session_logic_handle *)data;
278
279 while (session_logic_handle->active) {
280 /* Mutex locking for session_logic_loop condition variable */
281 pthread_mutex_lock(
282 &(session_logic_handle->session_logic_mutex));
283
284 /* this internal loop helps avoid spurious interrupts */
285 while (!session_logic_handle->session_logic_condition) {
286 pthread_cond_wait(
287 &(session_logic_handle->session_logic_cond_var),
288 &(session_logic_handle->session_logic_mutex));
289 }
290
291 pcep_session_event *event = queue_dequeue(
292 session_logic_handle->session_event_queue);
293 while (event != NULL) {
294 if (event->session == NULL) {
295 pcep_log(
296 LOG_INFO,
297 "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session",
298 __func__, time(NULL), pthread_self(),
299 (event->expired_timer_id
300 != TIMER_ID_NOT_SET)
301 ? "timer"
302 : "message");
303 pceplib_free(PCEPLIB_INFRA, event);
304 event = queue_dequeue(
305 session_logic_handle
306 ->session_event_queue);
307 continue;
308 }
309
310 /* Check if the session still exists, and synchronize
311 * possible session destroy */
312 pcep_log(
313 LOG_DEBUG,
314 "%s: session_logic_loop checking session_list sessionPtr %p",
315 __func__, event->session);
316 pthread_mutex_lock(
317 &(session_logic_handle->session_list_mutex));
318 if (ordered_list_find(
319 session_logic_handle->session_list,
320 event->session)
321 == NULL) {
322 pcep_log(
323 LOG_INFO,
324 "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded",
325 __func__, time(NULL), pthread_self(),
326 (event->expired_timer_id
327 != TIMER_ID_NOT_SET)
328 ? "timer"
329 : "message");
330 pceplib_free(PCEPLIB_INFRA, event);
331 event = queue_dequeue(
332 session_logic_handle
333 ->session_event_queue);
334 pthread_mutex_unlock(
335 &(session_logic_handle
336 ->session_list_mutex));
337 continue;
338 }
339
340 if (event->expired_timer_id != TIMER_ID_NOT_SET) {
341 handle_timer_event(event);
342 }
343
344 if (event->received_msg_list != NULL) {
345 handle_socket_comm_event(event);
346 }
347
348 pceplib_free(PCEPLIB_INFRA, event);
349 event = queue_dequeue(
350 session_logic_handle->session_event_queue);
351
352 pthread_mutex_unlock(
353 &(session_logic_handle->session_list_mutex));
354 }
355
356 session_logic_handle->session_logic_condition = false;
357 pthread_mutex_unlock(
358 &(session_logic_handle->session_logic_mutex));
359 }
360
361 pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread",
362 __func__, time(NULL), pthread_self());
363
364 return NULL;
365 }