1 // SPDX-License-Identifier: LGPL-2.1-or-later
3 * This file is part of the PCEPlib, a PCEP protocol library.
5 * Copyright (C) 2020 Volta Networks https://voltanet.io/
7 * Author : Brady Johnson <brady@voltanet.io>
24 #include "pcep_msg_encoding.h"
25 #include "pcep_session_logic.h"
26 #include "pcep_session_logic_internals.h"
27 #include "pcep_timers.h"
28 #include "pcep_utils_counters.h"
29 #include "pcep_utils_ordered_list.h"
30 #include "pcep_utils_logging.h"
31 #include "pcep_utils_memory.h"
34 * public API function implementations for the session_logic
37 pcep_session_logic_handle
*session_logic_handle_
= NULL
;
38 pcep_event_queue
*session_logic_event_queue_
= NULL
;
41 void send_pcep_open(pcep_session
*session
); /* forward decl */
43 static bool run_session_logic_common(void)
45 if (session_logic_handle_
!= NULL
) {
47 "%s: Session Logic is already initialized.", __func__
);
51 session_logic_handle_
= pceplib_malloc(
52 PCEPLIB_INFRA
, sizeof(pcep_session_logic_handle
));
53 memset(session_logic_handle_
, 0, sizeof(pcep_session_logic_handle
));
55 session_logic_handle_
->active
= true;
56 session_logic_handle_
->session_list
=
57 ordered_list_initialize(pointer_compare_function
);
58 session_logic_handle_
->session_event_queue
= queue_initialize();
60 /* Initialize the event queue */
61 session_logic_event_queue_
=
62 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_event_queue
));
63 session_logic_event_queue_
->event_queue
= queue_initialize();
64 if (pthread_mutex_init(&(session_logic_event_queue_
->event_queue_mutex
),
69 "%s: Cannot initialize session_logic event queue mutex.",
74 pthread_cond_init(&(session_logic_handle_
->session_logic_cond_var
),
77 if (pthread_mutex_init(&(session_logic_handle_
->session_logic_mutex
),
80 pcep_log(LOG_ERR
, "%s: Cannot initialize session_logic mutex.",
85 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
86 session_logic_handle_
->session_logic_condition
= true;
87 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
88 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
90 if (pthread_mutex_init(&(session_logic_handle_
->session_list_mutex
),
93 pcep_log(LOG_ERR
, "%s: Cannot initialize session_list mutex.",
102 bool run_session_logic(void)
104 if (!run_session_logic_common()) {
108 if (pthread_create(&(session_logic_handle_
->session_logic_thread
), NULL
,
109 session_logic_loop
, session_logic_handle_
)) {
110 pcep_log(LOG_ERR
, "%s: Cannot initialize session_logic thread.",
115 if (!initialize_timers(session_logic_timer_expire_handler
)) {
116 pcep_log(LOG_ERR
, "%s: Cannot initialize session_logic timers.",
121 /* No need to call initialize_socket_comm_loop() since it will be
122 * called internally when the first socket_comm_session is created. */
128 bool run_session_logic_with_infra(pceplib_infra_config
*infra_config
)
130 if (infra_config
== NULL
) {
131 return run_session_logic();
134 /* Initialize the memory infrastructure before anything gets allocated
136 if (infra_config
->pceplib_infra_mt
!= NULL
137 && infra_config
->pceplib_messages_mt
!= NULL
) {
138 pceplib_memory_initialize(
139 infra_config
->pceplib_infra_mt
,
140 infra_config
->pceplib_messages_mt
,
141 infra_config
->malloc_func
, infra_config
->calloc_func
,
142 infra_config
->realloc_func
, infra_config
->strdup_func
,
143 infra_config
->free_func
);
146 if (!run_session_logic_common()) {
150 /* Create the pcep_session_logic pthread so it can be managed externally
152 if (infra_config
->pthread_create_func
!= NULL
) {
153 if (infra_config
->pthread_create_func(
154 &(session_logic_handle_
->session_logic_thread
),
155 NULL
, session_logic_loop
, session_logic_handle_
,
156 "pcep_session_logic")) {
159 "%s: Cannot initialize external session_logic thread.",
165 &(session_logic_handle_
->session_logic_thread
),
166 NULL
, session_logic_loop
, session_logic_handle_
)) {
168 "%s: Cannot initialize session_logic thread.",
174 session_logic_event_queue_
->event_callback
=
175 infra_config
->pcep_event_func
;
176 session_logic_event_queue_
->event_callback_data
=
177 infra_config
->external_infra_data
;
179 if (!initialize_timers_external_infra(
180 session_logic_timer_expire_handler
,
181 infra_config
->external_infra_data
,
182 infra_config
->timer_create_func
,
183 infra_config
->timer_cancel_func
,
184 infra_config
->pthread_create_func
)) {
187 "%s: Cannot initialize session_logic timers with infra.",
192 /* We found a problem with the FRR sockets, where not all the KeepAlive
193 * messages were received, so if the pthread_create_func is set, the
194 * internal PCEPlib socket infrastructure will be used. */
196 /* For the SocketComm, the socket_read/write_func and the
197 * pthread_create_func are mutually exclusive. */
198 if (infra_config
->pthread_create_func
!= NULL
) {
199 if (!initialize_socket_comm_external_infra(
200 infra_config
->external_infra_data
, NULL
, NULL
,
201 infra_config
->pthread_create_func
)) {
204 "%s: Cannot initialize session_logic socket comm with infra.",
208 } else if (infra_config
->socket_read_func
!= NULL
209 && infra_config
->socket_write_func
!= NULL
) {
210 if (!initialize_socket_comm_external_infra(
211 infra_config
->external_infra_data
,
212 infra_config
->socket_read_func
,
213 infra_config
->socket_write_func
, NULL
)) {
216 "%s: Cannot initialize session_logic socket comm with infra.",
225 bool run_session_logic_wait_for_completion(void)
227 if (!run_session_logic()) {
231 /* Blocking call, waits for session logic thread to complete */
232 pthread_join(session_logic_handle_
->session_logic_thread
, NULL
);
238 bool stop_session_logic(void)
240 if (session_logic_handle_
== NULL
) {
241 pcep_log(LOG_WARNING
, "%s: Session logic already stopped",
246 session_logic_handle_
->active
= false;
249 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
250 session_logic_handle_
->session_logic_condition
= true;
251 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
252 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
253 pthread_join(session_logic_handle_
->session_logic_thread
, NULL
);
255 pthread_mutex_destroy(&(session_logic_handle_
->session_logic_mutex
));
256 pthread_mutex_destroy(&(session_logic_handle_
->session_list_mutex
));
257 ordered_list_destroy(session_logic_handle_
->session_list
);
258 queue_destroy(session_logic_handle_
->session_event_queue
);
260 /* destroy the event_queue */
261 pthread_mutex_destroy(&(session_logic_event_queue_
->event_queue_mutex
));
262 queue_destroy(session_logic_event_queue_
->event_queue
);
263 pceplib_free(PCEPLIB_INFRA
, session_logic_event_queue_
);
265 /* Explicitly stop the socket comm loop started by the pcep_sessions */
266 destroy_socket_comm_loop();
268 pceplib_free(PCEPLIB_INFRA
, session_logic_handle_
);
269 session_logic_handle_
= NULL
;
275 void close_pcep_session(pcep_session
*session
)
277 close_pcep_session_with_reason(session
, PCEP_CLOSE_REASON_NO
);
280 void close_pcep_session_with_reason(pcep_session
*session
,
281 enum pcep_close_reason reason
)
283 struct pcep_message
*close_msg
= pcep_msg_create_close(reason
);
287 "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]",
288 __func__
, time(NULL
), pthread_self(), session
->session_id
);
290 session_send_message(session
, close_msg
);
291 socket_comm_session_close_tcp_after_write(session
->socket_comm_session
);
292 session
->session_state
= SESSION_STATE_INITIALIZED
;
296 void destroy_pcep_session(pcep_session
*session
)
298 if (session
== NULL
) {
299 pcep_log(LOG_WARNING
, "%s: Cannot destroy NULL session",
304 /* Remove the session from the session_list and synchronize session
305 * destroy with the session_logic_loop, so that no in-flight events
306 * will be handled now that the session is destroyed. */
307 pthread_mutex_lock(&(session_logic_handle_
->session_list_mutex
));
308 ordered_list_remove_first_node_equals(
309 session_logic_handle_
->session_list
, session
);
311 "%s: destroy_pcep_session delete session_list sessionPtr %p",
314 pcep_session_cancel_timers(session
);
315 delete_counters_group(session
->pcep_session_counters
);
316 queue_destroy_with_data(session
->num_unknown_messages_time_queue
);
317 socket_comm_session_teardown(session
->socket_comm_session
);
319 if (session
->pcc_config
.pcep_msg_versioning
!= NULL
) {
320 pceplib_free(PCEPLIB_INFRA
,
321 session
->pcc_config
.pcep_msg_versioning
);
324 if (session
->pce_config
.pcep_msg_versioning
!= NULL
) {
325 pceplib_free(PCEPLIB_INFRA
,
326 session
->pce_config
.pcep_msg_versioning
);
329 int session_id
= session
->session_id
;
330 pceplib_free(PCEPLIB_INFRA
, session
);
331 pcep_log(LOG_INFO
, "%s: [%ld-%ld] session [%d] destroyed", __func__
,
332 time(NULL
), pthread_self(), session_id
);
333 pthread_mutex_unlock(&(session_logic_handle_
->session_list_mutex
));
336 void pcep_session_cancel_timers(pcep_session
*session
)
338 if (session
== NULL
) {
342 if (session
->timer_id_dead_timer
!= TIMER_ID_NOT_SET
) {
343 cancel_timer(session
->timer_id_dead_timer
);
346 if (session
->timer_id_keep_alive
!= TIMER_ID_NOT_SET
) {
347 cancel_timer(session
->timer_id_keep_alive
);
350 if (session
->timer_id_open_keep_wait
!= TIMER_ID_NOT_SET
) {
351 cancel_timer(session
->timer_id_open_keep_wait
);
354 if (session
->timer_id_open_keep_alive
!= TIMER_ID_NOT_SET
) {
355 cancel_timer(session
->timer_id_open_keep_alive
);
359 /* Internal util function */
360 static int get_next_session_id(void)
362 if (session_id_
== INT_MAX
) {
366 return session_id_
++;
369 /* Internal util function */
370 static pcep_session
*create_pcep_session_pre_setup(pcep_configuration
*config
)
372 if (config
== NULL
) {
373 pcep_log(LOG_WARNING
,
374 "%s: Cannot create pcep session with NULL config",
379 pcep_session
*session
=
380 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_session
));
381 memset(session
, 0, sizeof(pcep_session
));
382 session
->session_id
= get_next_session_id();
383 session
->session_state
= SESSION_STATE_INITIALIZED
;
384 session
->timer_id_open_keep_wait
= TIMER_ID_NOT_SET
;
385 session
->timer_id_open_keep_alive
= TIMER_ID_NOT_SET
;
386 session
->timer_id_dead_timer
= TIMER_ID_NOT_SET
;
387 session
->timer_id_keep_alive
= TIMER_ID_NOT_SET
;
388 session
->stateful_pce
= false;
389 session
->num_unknown_messages_time_queue
= queue_initialize();
390 session
->pce_open_received
= false;
391 session
->pce_open_rejected
= false;
392 session
->pce_open_keep_alive_sent
= false;
393 session
->pcc_open_rejected
= false;
394 session
->pce_open_accepted
= false;
395 session
->pcc_open_accepted
= false;
396 session
->destroy_session_after_write
= false;
397 session
->lsp_db_version
= config
->lsp_db_version
;
398 memcpy(&(session
->pcc_config
), config
, sizeof(pcep_configuration
));
399 /* copy the pcc_config to the pce_config until we receive the open
400 * keep_alive response */
401 memcpy(&(session
->pce_config
), config
, sizeof(pcep_configuration
));
402 if (config
->pcep_msg_versioning
!= NULL
) {
403 session
->pcc_config
.pcep_msg_versioning
= pceplib_malloc(
404 PCEPLIB_INFRA
, sizeof(struct pcep_versioning
));
405 memcpy(session
->pcc_config
.pcep_msg_versioning
,
406 config
->pcep_msg_versioning
,
407 sizeof(struct pcep_versioning
));
408 session
->pce_config
.pcep_msg_versioning
= pceplib_malloc(
409 PCEPLIB_INFRA
, sizeof(struct pcep_versioning
));
410 memcpy(session
->pce_config
.pcep_msg_versioning
,
411 config
->pcep_msg_versioning
,
412 sizeof(struct pcep_versioning
));
415 pthread_mutex_lock(&(session_logic_handle_
->session_list_mutex
));
416 ordered_list_add_node(session_logic_handle_
->session_list
, session
);
419 "%s: create_pcep_session_pre_setup add session_list sessionPtr %p",
421 pthread_mutex_unlock(&(session_logic_handle_
->session_list_mutex
));
426 /* Internal util function */
427 static bool create_pcep_session_post_setup(pcep_session
*session
)
429 if (!socket_comm_session_connect_tcp(session
->socket_comm_session
)) {
430 pcep_log(LOG_WARNING
, "%s: Cannot establish TCP socket.",
432 destroy_pcep_session(session
);
437 session
->time_connected
= time(NULL
);
438 create_session_counters(session
);
440 send_pcep_open(session
);
442 session
->session_state
= SESSION_STATE_PCEP_CONNECTING
;
443 session
->timer_id_open_keep_wait
=
444 create_timer(session
->pcc_config
.keep_alive_seconds
, session
);
445 // session->session_state = SESSION_STATE_OPENED;
450 pcep_session
*create_pcep_session(pcep_configuration
*config
,
451 struct in_addr
*pce_ip
)
453 if (pce_ip
== NULL
) {
454 pcep_log(LOG_WARNING
,
455 "%s: Cannot create pcep session with NULL pce_ip",
460 pcep_session
*session
= create_pcep_session_pre_setup(config
);
461 if (session
== NULL
) {
465 session
->socket_comm_session
= socket_comm_session_initialize_with_src(
466 NULL
, session_logic_msg_ready_handler
,
467 session_logic_message_sent_handler
,
468 session_logic_conn_except_notifier
, &(config
->src_ip
.src_ipv4
),
469 ((config
->src_pcep_port
== 0) ? PCEP_TCP_PORT
470 : config
->src_pcep_port
),
472 ((config
->dst_pcep_port
== 0) ? PCEP_TCP_PORT
473 : config
->dst_pcep_port
),
474 config
->socket_connect_timeout_millis
,
475 config
->tcp_authentication_str
, config
->is_tcp_auth_md5
,
477 if (session
->socket_comm_session
== NULL
) {
478 pcep_log(LOG_WARNING
,
479 "%s: Cannot establish socket_comm_session.", __func__
);
480 destroy_pcep_session(session
);
485 if (create_pcep_session_post_setup(session
) == false) {
492 pcep_session
*create_pcep_session_ipv6(pcep_configuration
*config
,
493 struct in6_addr
*pce_ip
)
495 if (pce_ip
== NULL
) {
496 pcep_log(LOG_WARNING
,
497 "%s: Cannot create pcep session with NULL pce_ip",
502 pcep_session
*session
= create_pcep_session_pre_setup(config
);
503 if (session
== NULL
) {
507 session
->socket_comm_session
=
508 socket_comm_session_initialize_with_src_ipv6(
509 NULL
, session_logic_msg_ready_handler
,
510 session_logic_message_sent_handler
,
511 session_logic_conn_except_notifier
,
512 &(config
->src_ip
.src_ipv6
),
513 ((config
->src_pcep_port
== 0) ? PCEP_TCP_PORT
514 : config
->src_pcep_port
),
516 ((config
->dst_pcep_port
== 0) ? PCEP_TCP_PORT
517 : config
->dst_pcep_port
),
518 config
->socket_connect_timeout_millis
,
519 config
->tcp_authentication_str
, config
->is_tcp_auth_md5
,
521 if (session
->socket_comm_session
== NULL
) {
522 pcep_log(LOG_WARNING
,
523 "%s: Cannot establish ipv6 socket_comm_session.",
525 destroy_pcep_session(session
);
530 if (create_pcep_session_post_setup(session
) == false) {
538 void session_send_message(pcep_session
*session
, struct pcep_message
*message
)
540 pcep_encode_message(message
, session
->pcc_config
.pcep_msg_versioning
);
541 socket_comm_session_send_message(session
->socket_comm_session
,
542 (char *)message
->encoded_message
,
543 message
->encoded_message_length
, true);
545 increment_message_tx_counters(session
, message
);
547 /* The message->encoded_message will be freed in
548 * socket_comm_session_send_message() once sent.
549 * Setting to NULL here so pcep_msg_free_message() does not free it */
550 message
->encoded_message
= NULL
;
551 pcep_msg_free_message(message
);
555 /* This function is also used in pcep_session_logic_states.c */
556 struct pcep_message
*create_pcep_open(pcep_session
*session
)
558 /* create and send PCEP open
559 * with PCEP, the PCC sends the config the PCE should use in the open
561 * and the PCE will send an open with the config the PCC should use. */
562 double_linked_list
*tlv_list
= dll_initialize();
563 if (session
->pcc_config
.support_stateful_pce_lsp_update
564 || session
->pcc_config
.support_pce_lsp_instantiation
565 || session
->pcc_config
.support_include_db_version
566 || session
->pcc_config
.support_lsp_triggered_resync
567 || session
->pcc_config
.support_lsp_delta_sync
568 || session
->pcc_config
.support_pce_triggered_initial_sync
) {
569 /* Prepend this TLV as the first in the list */
572 pcep_tlv_create_stateful_pce_capability(
575 .support_stateful_pce_lsp_update
,
577 session
->pcc_config
.support_include_db_version
,
580 .support_pce_lsp_instantiation
,
583 .support_lsp_triggered_resync
,
585 session
->pcc_config
.support_lsp_delta_sync
,
588 .support_pce_triggered_initial_sync
));
591 if (session
->pcc_config
.support_include_db_version
) {
592 if (session
->pcc_config
.lsp_db_version
!= 0) {
594 pcep_tlv_create_lsp_db_version(
595 session
->pcc_config
.lsp_db_version
));
599 if (session
->pcc_config
.support_sr_te_pst
) {
602 if (session
->pcc_config
.pcep_msg_versioning
603 ->draft_ietf_pce_segment_routing_07
605 flag_n
= session
->pcc_config
.pcc_can_resolve_nai_to_sid
;
606 flag_x
= (session
->pcc_config
.max_sid_depth
== 0);
609 struct pcep_object_tlv_sr_pce_capability
*sr_pce_cap_tlv
=
610 pcep_tlv_create_sr_pce_capability(
612 session
->pcc_config
.max_sid_depth
);
614 double_linked_list
*sub_tlv_list
= NULL
;
615 if (session
->pcc_config
.pcep_msg_versioning
616 ->draft_ietf_pce_segment_routing_07
618 /* With draft07, send the sr_pce_cap_tlv as a normal TLV
620 dll_append(tlv_list
, sr_pce_cap_tlv
);
622 /* With draft16, send the sr_pce_cap_tlv as a sub-TLV in
623 * the path_setup_type_capability TLV */
624 sub_tlv_list
= dll_initialize();
625 dll_append(sub_tlv_list
, sr_pce_cap_tlv
);
629 pceplib_malloc(PCEPLIB_MESSAGES
, sizeof(uint8_t));
631 double_linked_list
*pst_list
= dll_initialize();
632 dll_append(pst_list
, pst
);
633 dll_append(tlv_list
, pcep_tlv_create_path_setup_type_capability(
634 pst_list
, sub_tlv_list
));
637 struct pcep_message
*open_msg
= pcep_msg_create_open_with_tlvs(
638 session
->pcc_config
.keep_alive_seconds
,
639 session
->pcc_config
.dead_timer_seconds
, session
->session_id
,
644 "%s: [%ld-%ld] pcep_session_logic create open message: TLVs [%d] for session [%d]",
645 __func__
, time(NULL
), pthread_self(), tlv_list
->num_entries
,
646 session
->session_id
);
652 void send_pcep_open(pcep_session
*session
)
654 session_send_message(session
, create_pcep_open(session
));
657 /* This is a blocking call, since it is synchronized with destroy_pcep_session()
658 * and session_logic_loop(). It may be possible that the session has been
659 * deleted but API users havent been informed yet.
661 bool session_exists(pcep_session
*session
)
663 if (session_logic_handle_
== NULL
) {
665 "%s: session_exists session_logic_handle_ is NULL",
670 pthread_mutex_lock(&(session_logic_handle_
->session_list_mutex
));
672 (ordered_list_find(session_logic_handle_
->session_list
, session
)
674 pthread_mutex_unlock(&(session_logic_handle_
->session_list_mutex
));