diff options
Diffstat (limited to 'pceplib/pcep_session_logic_loop.c')
| -rw-r--r-- | pceplib/pcep_session_logic_loop.c | 360 | 
1 files changed, 360 insertions, 0 deletions
diff --git a/pceplib/pcep_session_logic_loop.c b/pceplib/pcep_session_logic_loop.c new file mode 100644 index 0000000000..5705ff2000 --- /dev/null +++ b/pceplib/pcep_session_logic_loop.c @@ -0,0 +1,360 @@ +/* + * 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> + * + */ + +#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); +	} 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; +}  | 
