2 * This file is part of the PCEPlib, a PCEP protocol library.
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * Author : Brady Johnson <brady@voltanet.io>
36 #include "pcep_msg_encoding.h"
37 #include "pcep_session_logic.h"
38 #include "pcep_session_logic_internals.h"
39 #include "pcep_timers.h"
40 #include "pcep_utils_counters.h"
41 #include "pcep_utils_ordered_list.h"
42 #include "pcep_utils_logging.h"
43 #include "pcep_utils_memory.h"
46 * public API function implementations for the session_logic
49 pcep_session_logic_handle
*session_logic_handle_
= NULL
;
50 pcep_event_queue
*session_logic_event_queue_
= NULL
;
53 void send_pcep_open(pcep_session
*session
); /* forward decl */
55 static bool run_session_logic_common()
57 if (session_logic_handle_
!= NULL
) {
59 "%s: Session Logic is already initialized.", __func__
);
63 session_logic_handle_
= pceplib_malloc(
64 PCEPLIB_INFRA
, sizeof(pcep_session_logic_handle
));
65 memset(session_logic_handle_
, 0, sizeof(pcep_session_logic_handle
));
67 session_logic_handle_
->active
= true;
68 session_logic_handle_
->session_list
=
69 ordered_list_initialize(pointer_compare_function
);
70 session_logic_handle_
->session_event_queue
= queue_initialize();
72 /* Initialize the event queue */
73 session_logic_event_queue_
=
74 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_event_queue
));
75 session_logic_event_queue_
->event_queue
= queue_initialize();
76 if (pthread_mutex_init(&(session_logic_event_queue_
->event_queue_mutex
),
81 "%s: Cannot initialize session_logic event queue mutex.",
86 pthread_cond_init(&(session_logic_handle_
->session_logic_cond_var
),
89 if (pthread_mutex_init(&(session_logic_handle_
->session_logic_mutex
),
92 pcep_log(LOG_ERR
, "%s: Cannot initialize session_logic mutex.",
97 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
98 session_logic_handle_
->session_logic_condition
= true;
99 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
100 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
102 if (pthread_mutex_init(&(session_logic_handle_
->session_list_mutex
),
105 pcep_log(LOG_ERR
, "%s: Cannot initialize session_list mutex.",
114 bool run_session_logic()
116 if (!run_session_logic_common()) {
120 if (pthread_create(&(session_logic_handle_
->session_logic_thread
), NULL
,
121 session_logic_loop
, session_logic_handle_
)) {
122 pcep_log(LOG_ERR
, "%s: Cannot initialize session_logic thread.",
127 if (!initialize_timers(session_logic_timer_expire_handler
)) {
128 pcep_log(LOG_ERR
, "%s: Cannot initialize session_logic timers.",
133 /* No need to call initialize_socket_comm_loop() since it will be
134 * called internally when the first socket_comm_session is created. */
140 bool run_session_logic_with_infra(pceplib_infra_config
*infra_config
)
142 if (infra_config
== NULL
) {
143 return run_session_logic();
146 /* Initialize the memory infrastructure before anything gets allocated
148 if (infra_config
->pceplib_infra_mt
!= NULL
149 && infra_config
->pceplib_messages_mt
!= NULL
) {
150 pceplib_memory_initialize(
151 infra_config
->pceplib_infra_mt
,
152 infra_config
->pceplib_messages_mt
,
153 infra_config
->malloc_func
, infra_config
->calloc_func
,
154 infra_config
->realloc_func
, infra_config
->strdup_func
,
155 infra_config
->free_func
);
158 if (!run_session_logic_common()) {
162 /* Create the pcep_session_logic pthread so it can be managed externally
164 if (infra_config
->pthread_create_func
!= NULL
) {
165 if (infra_config
->pthread_create_func(
166 &(session_logic_handle_
->session_logic_thread
),
167 NULL
, session_logic_loop
, session_logic_handle_
,
168 "pcep_session_logic")) {
171 "%s: Cannot initialize external session_logic thread.",
177 &(session_logic_handle_
->session_logic_thread
),
178 NULL
, session_logic_loop
, session_logic_handle_
)) {
180 "%s: Cannot initialize session_logic thread.",
186 session_logic_event_queue_
->event_callback
=
187 infra_config
->pcep_event_func
;
188 session_logic_event_queue_
->event_callback_data
=
189 infra_config
->external_infra_data
;
191 if (!initialize_timers_external_infra(
192 session_logic_timer_expire_handler
,
193 infra_config
->external_infra_data
,
194 infra_config
->timer_create_func
,
195 infra_config
->timer_cancel_func
,
196 infra_config
->pthread_create_func
)) {
199 "%s: Cannot initialize session_logic timers with infra.",
204 /* We found a problem with the FRR sockets, where not all the KeepAlive
205 * messages were received, so if the pthread_create_func is set, the
206 * internal PCEPlib socket infrastructure will be used. */
208 /* For the SocketComm, the socket_read/write_func and the
209 * pthread_create_func are mutually exclusive. */
210 if (infra_config
->pthread_create_func
!= NULL
) {
211 if (!initialize_socket_comm_external_infra(
212 infra_config
->external_infra_data
, NULL
, NULL
,
213 infra_config
->pthread_create_func
)) {
216 "%s: Cannot initialize session_logic socket comm with infra.",
220 } else if (infra_config
->socket_read_func
!= NULL
221 && infra_config
->socket_write_func
!= NULL
) {
222 if (!initialize_socket_comm_external_infra(
223 infra_config
->external_infra_data
,
224 infra_config
->socket_read_func
,
225 infra_config
->socket_write_func
, NULL
)) {
228 "%s: Cannot initialize session_logic socket comm with infra.",
237 bool run_session_logic_wait_for_completion()
239 if (!run_session_logic()) {
243 /* Blocking call, waits for session logic thread to complete */
244 pthread_join(session_logic_handle_
->session_logic_thread
, NULL
);
250 bool stop_session_logic()
252 if (session_logic_handle_
== NULL
) {
253 pcep_log(LOG_WARNING
, "%s: Session logic already stopped",
258 session_logic_handle_
->active
= false;
261 pthread_mutex_lock(&(session_logic_handle_
->session_logic_mutex
));
262 session_logic_handle_
->session_logic_condition
= true;
263 pthread_cond_signal(&(session_logic_handle_
->session_logic_cond_var
));
264 pthread_mutex_unlock(&(session_logic_handle_
->session_logic_mutex
));
265 pthread_join(session_logic_handle_
->session_logic_thread
, NULL
);
267 pthread_mutex_destroy(&(session_logic_handle_
->session_logic_mutex
));
268 pthread_mutex_destroy(&(session_logic_handle_
->session_list_mutex
));
269 ordered_list_destroy(session_logic_handle_
->session_list
);
270 queue_destroy(session_logic_handle_
->session_event_queue
);
272 /* destroy the event_queue */
273 pthread_mutex_destroy(&(session_logic_event_queue_
->event_queue_mutex
));
274 queue_destroy(session_logic_event_queue_
->event_queue
);
275 pceplib_free(PCEPLIB_INFRA
, session_logic_event_queue_
);
277 /* Explicitly stop the socket comm loop started by the pcep_sessions */
278 destroy_socket_comm_loop();
280 pceplib_free(PCEPLIB_INFRA
, session_logic_handle_
);
281 session_logic_handle_
= NULL
;
287 void close_pcep_session(pcep_session
*session
)
289 close_pcep_session_with_reason(session
, PCEP_CLOSE_REASON_NO
);
292 void close_pcep_session_with_reason(pcep_session
*session
,
293 enum pcep_close_reason reason
)
295 struct pcep_message
*close_msg
= pcep_msg_create_close(reason
);
299 "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]",
300 __func__
, time(NULL
), pthread_self(), session
->session_id
);
302 session_send_message(session
, close_msg
);
303 socket_comm_session_close_tcp_after_write(session
->socket_comm_session
);
304 session
->session_state
= SESSION_STATE_INITIALIZED
;
308 void destroy_pcep_session(pcep_session
*session
)
310 if (session
== NULL
) {
311 pcep_log(LOG_WARNING
, "%s: Cannot destroy NULL session",
316 /* Remove the session from the session_list and synchronize session
317 * destroy with the session_logic_loop, so that no in-flight events
318 * will be handled now that the session is destroyed. */
319 pthread_mutex_lock(&(session_logic_handle_
->session_list_mutex
));
320 ordered_list_remove_first_node_equals(
321 session_logic_handle_
->session_list
, session
);
323 "%s: destroy_pcep_session delete session_list sessionPtr %p",
326 pcep_session_cancel_timers(session
);
327 delete_counters_group(session
->pcep_session_counters
);
328 queue_destroy_with_data(session
->num_unknown_messages_time_queue
);
329 socket_comm_session_teardown(session
->socket_comm_session
);
331 if (session
->pcc_config
.pcep_msg_versioning
!= NULL
) {
332 pceplib_free(PCEPLIB_INFRA
,
333 session
->pcc_config
.pcep_msg_versioning
);
336 if (session
->pce_config
.pcep_msg_versioning
!= NULL
) {
337 pceplib_free(PCEPLIB_INFRA
,
338 session
->pce_config
.pcep_msg_versioning
);
341 int session_id
= session
->session_id
;
342 pceplib_free(PCEPLIB_INFRA
, session
);
343 pcep_log(LOG_INFO
, "%s: [%ld-%ld] session [%d] destroyed", __func__
,
344 time(NULL
), pthread_self(), session_id
);
345 pthread_mutex_unlock(&(session_logic_handle_
->session_list_mutex
));
348 void pcep_session_cancel_timers(pcep_session
*session
)
350 if (session
== NULL
) {
354 if (session
->timer_id_dead_timer
!= TIMER_ID_NOT_SET
) {
355 cancel_timer(session
->timer_id_dead_timer
);
358 if (session
->timer_id_keep_alive
!= TIMER_ID_NOT_SET
) {
359 cancel_timer(session
->timer_id_keep_alive
);
362 if (session
->timer_id_open_keep_wait
!= TIMER_ID_NOT_SET
) {
363 cancel_timer(session
->timer_id_open_keep_wait
);
366 if (session
->timer_id_open_keep_alive
!= TIMER_ID_NOT_SET
) {
367 cancel_timer(session
->timer_id_open_keep_alive
);
371 /* Internal util function */
372 static int get_next_session_id()
374 if (session_id_
== INT_MAX
) {
378 return session_id_
++;
381 /* Internal util function */
382 static pcep_session
*create_pcep_session_pre_setup(pcep_configuration
*config
)
384 if (config
== NULL
) {
385 pcep_log(LOG_WARNING
,
386 "%s: Cannot create pcep session with NULL config",
391 pcep_session
*session
=
392 pceplib_malloc(PCEPLIB_INFRA
, sizeof(pcep_session
));
393 memset(session
, 0, sizeof(pcep_session
));
394 session
->session_id
= get_next_session_id();
395 session
->session_state
= SESSION_STATE_INITIALIZED
;
396 session
->timer_id_open_keep_wait
= TIMER_ID_NOT_SET
;
397 session
->timer_id_open_keep_alive
= TIMER_ID_NOT_SET
;
398 session
->timer_id_dead_timer
= TIMER_ID_NOT_SET
;
399 session
->timer_id_keep_alive
= TIMER_ID_NOT_SET
;
400 session
->stateful_pce
= false;
401 session
->num_unknown_messages_time_queue
= queue_initialize();
402 session
->pce_open_received
= false;
403 session
->pce_open_rejected
= false;
404 session
->pce_open_keep_alive_sent
= false;
405 session
->pcc_open_rejected
= false;
406 session
->pce_open_accepted
= false;
407 session
->pcc_open_accepted
= false;
408 session
->destroy_session_after_write
= false;
409 session
->lsp_db_version
= config
->lsp_db_version
;
410 memcpy(&(session
->pcc_config
), config
, sizeof(pcep_configuration
));
411 /* copy the pcc_config to the pce_config until we receive the open
412 * keep_alive response */
413 memcpy(&(session
->pce_config
), config
, sizeof(pcep_configuration
));
414 if (config
->pcep_msg_versioning
!= NULL
) {
415 session
->pcc_config
.pcep_msg_versioning
= pceplib_malloc(
416 PCEPLIB_INFRA
, sizeof(struct pcep_versioning
));
417 memcpy(session
->pcc_config
.pcep_msg_versioning
,
418 config
->pcep_msg_versioning
,
419 sizeof(struct pcep_versioning
));
420 session
->pce_config
.pcep_msg_versioning
= pceplib_malloc(
421 PCEPLIB_INFRA
, sizeof(struct pcep_versioning
));
422 memcpy(session
->pce_config
.pcep_msg_versioning
,
423 config
->pcep_msg_versioning
,
424 sizeof(struct pcep_versioning
));
427 pthread_mutex_lock(&(session_logic_handle_
->session_list_mutex
));
428 ordered_list_add_node(session_logic_handle_
->session_list
, session
);
431 "%s: create_pcep_session_pre_setup add session_list sessionPtr %p",
433 pthread_mutex_unlock(&(session_logic_handle_
->session_list_mutex
));
438 /* Internal util function */
439 static bool create_pcep_session_post_setup(pcep_session
*session
)
441 if (!socket_comm_session_connect_tcp(session
->socket_comm_session
)) {
442 pcep_log(LOG_WARNING
, "%s: Cannot establish TCP socket.",
444 destroy_pcep_session(session
);
449 session
->time_connected
= time(NULL
);
450 create_session_counters(session
);
452 send_pcep_open(session
);
454 session
->session_state
= SESSION_STATE_PCEP_CONNECTING
;
455 session
->timer_id_open_keep_wait
=
456 create_timer(session
->pcc_config
.keep_alive_seconds
, session
);
457 // session->session_state = SESSION_STATE_OPENED;
462 pcep_session
*create_pcep_session(pcep_configuration
*config
,
463 struct in_addr
*pce_ip
)
465 if (pce_ip
== NULL
) {
466 pcep_log(LOG_WARNING
,
467 "%s: Cannot create pcep session with NULL pce_ip",
472 pcep_session
*session
= create_pcep_session_pre_setup(config
);
473 if (session
== NULL
) {
477 session
->socket_comm_session
= socket_comm_session_initialize_with_src(
478 NULL
, session_logic_msg_ready_handler
,
479 session_logic_message_sent_handler
,
480 session_logic_conn_except_notifier
, &(config
->src_ip
.src_ipv4
),
481 ((config
->src_pcep_port
== 0) ? PCEP_TCP_PORT
482 : config
->src_pcep_port
),
484 ((config
->dst_pcep_port
== 0) ? PCEP_TCP_PORT
485 : config
->dst_pcep_port
),
486 config
->socket_connect_timeout_millis
,
487 config
->tcp_authentication_str
, config
->is_tcp_auth_md5
,
489 if (session
->socket_comm_session
== NULL
) {
490 pcep_log(LOG_WARNING
,
491 "%s: Cannot establish socket_comm_session.", __func__
);
492 destroy_pcep_session(session
);
497 if (create_pcep_session_post_setup(session
) == false) {
504 pcep_session
*create_pcep_session_ipv6(pcep_configuration
*config
,
505 struct in6_addr
*pce_ip
)
507 if (pce_ip
== NULL
) {
508 pcep_log(LOG_WARNING
,
509 "%s: Cannot create pcep session with NULL pce_ip",
514 pcep_session
*session
= create_pcep_session_pre_setup(config
);
515 if (session
== NULL
) {
519 session
->socket_comm_session
=
520 socket_comm_session_initialize_with_src_ipv6(
521 NULL
, session_logic_msg_ready_handler
,
522 session_logic_message_sent_handler
,
523 session_logic_conn_except_notifier
,
524 &(config
->src_ip
.src_ipv6
),
525 ((config
->src_pcep_port
== 0) ? PCEP_TCP_PORT
526 : config
->src_pcep_port
),
528 ((config
->dst_pcep_port
== 0) ? PCEP_TCP_PORT
529 : config
->dst_pcep_port
),
530 config
->socket_connect_timeout_millis
,
531 config
->tcp_authentication_str
, config
->is_tcp_auth_md5
,
533 if (session
->socket_comm_session
== NULL
) {
534 pcep_log(LOG_WARNING
,
535 "%s: Cannot establish ipv6 socket_comm_session.",
537 destroy_pcep_session(session
);
542 if (create_pcep_session_post_setup(session
) == false) {
550 void session_send_message(pcep_session
*session
, struct pcep_message
*message
)
552 pcep_encode_message(message
, session
->pcc_config
.pcep_msg_versioning
);
553 socket_comm_session_send_message(session
->socket_comm_session
,
554 (char *)message
->encoded_message
,
555 message
->encoded_message_length
, true);
557 increment_message_tx_counters(session
, message
);
559 /* The message->encoded_message will be freed in
560 * socket_comm_session_send_message() once sent.
561 * Setting to NULL here so pcep_msg_free_message() does not free it */
562 message
->encoded_message
= NULL
;
563 pcep_msg_free_message(message
);
567 /* This function is also used in pcep_session_logic_states.c */
568 struct pcep_message
*create_pcep_open(pcep_session
*session
)
570 /* create and send PCEP open
571 * with PCEP, the PCC sends the config the PCE should use in the open
573 * and the PCE will send an open with the config the PCC should use. */
574 double_linked_list
*tlv_list
= dll_initialize();
575 if (session
->pcc_config
.support_stateful_pce_lsp_update
576 || session
->pcc_config
.support_pce_lsp_instantiation
577 || session
->pcc_config
.support_include_db_version
578 || session
->pcc_config
.support_lsp_triggered_resync
579 || session
->pcc_config
.support_lsp_delta_sync
580 || session
->pcc_config
.support_pce_triggered_initial_sync
) {
581 /* Prepend this TLV as the first in the list */
584 pcep_tlv_create_stateful_pce_capability(
587 .support_stateful_pce_lsp_update
,
589 session
->pcc_config
.support_include_db_version
,
592 .support_pce_lsp_instantiation
,
594 session
->pcc_config
.support_lsp_triggered_resync
,
596 session
->pcc_config
.support_lsp_delta_sync
,
598 session
->pcc_config
.support_pce_triggered_initial_sync
));
601 if (session
->pcc_config
.support_include_db_version
) {
602 if (session
->pcc_config
.lsp_db_version
!= 0) {
604 pcep_tlv_create_lsp_db_version(
605 session
->pcc_config
.lsp_db_version
));
609 if (session
->pcc_config
.support_sr_te_pst
) {
612 if (session
->pcc_config
.pcep_msg_versioning
613 ->draft_ietf_pce_segment_routing_07
615 flag_n
= session
->pcc_config
.pcc_can_resolve_nai_to_sid
;
616 flag_x
= (session
->pcc_config
.max_sid_depth
== 0);
619 struct pcep_object_tlv_sr_pce_capability
*sr_pce_cap_tlv
=
620 pcep_tlv_create_sr_pce_capability(
622 session
->pcc_config
.max_sid_depth
);
624 double_linked_list
*sub_tlv_list
= NULL
;
625 if (session
->pcc_config
.pcep_msg_versioning
626 ->draft_ietf_pce_segment_routing_07
628 /* With draft07, send the sr_pce_cap_tlv as a normal TLV
630 dll_append(tlv_list
, sr_pce_cap_tlv
);
632 /* With draft16, send the sr_pce_cap_tlv as a sub-TLV in
633 * the path_setup_type_capability TLV */
634 sub_tlv_list
= dll_initialize();
635 dll_append(sub_tlv_list
, sr_pce_cap_tlv
);
639 pceplib_malloc(PCEPLIB_MESSAGES
, sizeof(uint8_t));
641 double_linked_list
*pst_list
= dll_initialize();
642 dll_append(pst_list
, pst
);
643 dll_append(tlv_list
, pcep_tlv_create_path_setup_type_capability(
644 pst_list
, sub_tlv_list
));
647 struct pcep_message
*open_msg
= pcep_msg_create_open_with_tlvs(
648 session
->pcc_config
.keep_alive_seconds
,
649 session
->pcc_config
.dead_timer_seconds
, session
->session_id
,
654 "%s: [%ld-%ld] pcep_session_logic create open message: TLVs [%d] for session [%d]",
655 __func__
, time(NULL
), pthread_self(), tlv_list
->num_entries
,
656 session
->session_id
);
662 void send_pcep_open(pcep_session
*session
)
664 session_send_message(session
, create_pcep_open(session
));
667 /* This is a blocking call, since it is synchronized with destroy_pcep_session()
668 * and session_logic_loop(). It may be possible that the session has been
669 * deleted but API users havent been informed yet.
671 bool session_exists(pcep_session
*session
)
673 if (session_logic_handle_
== NULL
) {
675 "%s: session_exists session_logic_handle_ is NULL",
680 pthread_mutex_lock(&(session_logic_handle_
->session_list_mutex
));
682 (ordered_list_find(session_logic_handle_
->session_list
, session
)
684 pthread_mutex_unlock(&(session_logic_handle_
->session_list_mutex
));