mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 13:43:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			366 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			366 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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;
 | 
						|
}
 |