/*
* 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;
}