/* * 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 . * * Author : Brady Johnson * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #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; }