diff options
Diffstat (limited to '')
-rw-r--r-- | pceplib/pcep_session_logic_loop.c | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/pceplib/pcep_session_logic_loop.c b/pceplib/pcep_session_logic_loop.c new file mode 100644 index 0000000..77392fb --- /dev/null +++ b/pceplib/pcep_session_logic_loop.c @@ -0,0 +1,365 @@ +/* + * This file is part of the PCEPlib, a PCEP protocol library. + * + * Copyright (C) 2020 Volta Networks https://voltanet.io/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> + +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_timers.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +/* global var needed for callback handlers */ +extern pcep_session_logic_handle *session_logic_handle_; + +/* internal util function to create session_event's */ +static pcep_session_event *create_session_event(pcep_session *session) +{ + pcep_session_event *event = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session_event)); + event->session = session; + event->expired_timer_id = TIMER_ID_NOT_SET; + event->received_msg_list = NULL; + event->socket_closed = false; + + return event; +} + + +/* A function pointer to this function is passed to pcep_socket_comm + * for each pcep_session creation, so it will be called whenever + * messages are ready to be read. This function will be called + * by the socket_comm thread. + * This function will decode the read PCEP message and give it + * to the session_logic_loop so it can be handled by the session_logic + * state machine. */ +int session_logic_msg_ready_handler(void *data, int socket_fd) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot handle msg_ready with NULL data", + __func__); + return -1; + } + + if (session_logic_handle_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Received a message ready notification while the session logic is not active", + __func__); + return -1; + } + + pcep_session *session = (pcep_session *)data; + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + + /* This event will ultimately be handled by handle_socket_comm_event() + * in pcep_session_logic_states.c */ + pcep_session_event *rcvd_msg_event = create_session_event(session); + + int msg_length = 0; + double_linked_list *msg_list = pcep_msg_read(socket_fd); + + if (msg_list == NULL) { + /* The socket was closed, or there was a socket read error */ + pcep_log(LOG_INFO, + "%s: PCEP connection closed for session [%d]", + __func__, session->session_id); + dll_destroy(msg_list); + rcvd_msg_event->socket_closed = true; + socket_comm_session_teardown(session->socket_comm_session); + pcep_session_cancel_timers(session); + session->socket_comm_session = NULL; + session->session_state = SESSION_STATE_INITIALIZED; + enqueue_event(session, PCE_CLOSED_SOCKET, NULL); + } else if (msg_list->num_entries == 0) { + /* Invalid message received */ + increment_unknown_message(session); + dll_destroy_with_data(msg_list); + } else { + /* Just logging the first of potentially several messages + * received */ + struct pcep_message *msg = + ((struct pcep_message *)msg_list->head->data); + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]", + __func__, time(NULL), pthread_self(), + msg->msg_header->type, msg->encoded_message_length, + session->session_id); + + rcvd_msg_event->received_msg_list = msg_list; + msg_length = msg->encoded_message_length; + } + + queue_enqueue(session_logic_handle_->session_event_queue, + rcvd_msg_event); + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); + + return msg_length; +} + + +/* A function pointer to this function was passed to pcep_socket_comm, + * so it will be called when a message is sent. This is useful since + * message sending is asynchronous, and there are times that actions + * need to be performed only after a message has been sent. */ +void session_logic_message_sent_handler(void *data, int socket_fd) +{ + (void)socket_fd; + + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot handle msg_sent with NULL data", __func__); + return; + } + + pcep_session *session = (pcep_session *)data; + if (session->destroy_session_after_write == true) { + /* Do not call destroy until all of the queued messages are + * written */ + if (session->socket_comm_session != NULL + && session->socket_comm_session->message_queue->num_entries + == 0) { + destroy_pcep_session(session); + } + } else { + /* Reset the keep alive timer for every message sent on + * the session, only if the session is not destroyed */ + if (session->timer_id_keep_alive == TIMER_ID_NOT_SET) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]", + __func__, time(NULL), pthread_self(), + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + session->session_id); + session->timer_id_keep_alive = create_timer( + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + session); + } else { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]", + __func__, time(NULL), pthread_self(), + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + session->session_id); + reset_timer(session->timer_id_keep_alive); + } + } +} + + +/* A function pointer to this function was passed to pcep_socket_comm, + * so it will be called whenever the socket is closed. this function + * will be called by the socket_comm thread. */ +void session_logic_conn_except_notifier(void *data, int socket_fd) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot handle conn_except with NULL data", + __func__); + return; + } + + if (session_logic_handle_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Received a connection exception notification while the session logic is not active", + __func__); + return; + } + + pcep_session *session = (pcep_session *)data; + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]", + __func__, time(NULL), pthread_self(), socket_fd, + session->session_id); + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + pcep_session_event *socket_event = create_session_event(session); + socket_event->socket_closed = true; + queue_enqueue(session_logic_handle_->session_event_queue, socket_event); + session_logic_handle_->session_logic_condition = true; + + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); +} + + +/* + * this method is the timer expire handler, and will only + * pass the event to the session_logic loop and notify it + * that there is a timer available. this function will be + * called by the timers thread. + */ +void session_logic_timer_expire_handler(void *data, int timer_id) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data", + __func__); + return; + } + + if (session_logic_handle_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Received a timer expiration while the session logic is not active", + __func__); + return; + } + + pcep_log(LOG_INFO, "%s: [%ld-%ld] timer expired handler timer_id [%d]", + __func__, time(NULL), pthread_self(), timer_id); + pcep_session_event *expired_timer_event = + create_session_event((pcep_session *)data); + expired_timer_event->expired_timer_id = timer_id; + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + queue_enqueue(session_logic_handle_->session_event_queue, + expired_timer_event); + + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); +} + + +/* + * session_logic event loop + * this function is called upon thread creation from pcep_session_logic.c + */ +void *session_logic_loop(void *data) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot start session_logic_loop with NULL data", + __func__); + + return NULL; + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread", + __func__, time(NULL), pthread_self()); + + pcep_session_logic_handle *session_logic_handle = + (pcep_session_logic_handle *)data; + + while (session_logic_handle->active) { + /* Mutex locking for session_logic_loop condition variable */ + pthread_mutex_lock( + &(session_logic_handle->session_logic_mutex)); + + /* this internal loop helps avoid spurious interrupts */ + while (!session_logic_handle->session_logic_condition) { + pthread_cond_wait( + &(session_logic_handle->session_logic_cond_var), + &(session_logic_handle->session_logic_mutex)); + } + + pcep_session_event *event = queue_dequeue( + session_logic_handle->session_event_queue); + while (event != NULL) { + if (event->session == NULL) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session", + __func__, time(NULL), pthread_self(), + (event->expired_timer_id + != TIMER_ID_NOT_SET) + ? "timer" + : "message"); + pceplib_free(PCEPLIB_INFRA, event); + event = queue_dequeue( + session_logic_handle + ->session_event_queue); + continue; + } + + /* Check if the session still exists, and synchronize + * possible session destroy */ + pcep_log( + LOG_DEBUG, + "%s: session_logic_loop checking session_list sessionPtr %p", + __func__, event->session); + pthread_mutex_lock( + &(session_logic_handle->session_list_mutex)); + if (ordered_list_find( + session_logic_handle->session_list, + event->session) + == NULL) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded", + __func__, time(NULL), pthread_self(), + (event->expired_timer_id + != TIMER_ID_NOT_SET) + ? "timer" + : "message"); + pceplib_free(PCEPLIB_INFRA, event); + event = queue_dequeue( + session_logic_handle + ->session_event_queue); + pthread_mutex_unlock( + &(session_logic_handle + ->session_list_mutex)); + continue; + } + + if (event->expired_timer_id != TIMER_ID_NOT_SET) { + handle_timer_event(event); + } + + if (event->received_msg_list != NULL) { + handle_socket_comm_event(event); + } + + pceplib_free(PCEPLIB_INFRA, event); + event = queue_dequeue( + session_logic_handle->session_event_queue); + + pthread_mutex_unlock( + &(session_logic_handle->session_list_mutex)); + } + + session_logic_handle->session_logic_condition = false; + pthread_mutex_unlock( + &(session_logic_handle->session_logic_mutex)); + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread", + __func__, time(NULL), pthread_self()); + + return NULL; +} |