]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_session_logic.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / pceplib / pcep_session_logic.c
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /*
3 * This file is part of the PCEPlib, a PCEP protocol library.
4 *
5 * Copyright (C) 2020 Volta Networks https://voltanet.io/
6 *
7 * Author : Brady Johnson <brady@voltanet.io>
8 *
9 */
10
11
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #include <errno.h>
17 #include <limits.h>
18 #include <pthread.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <time.h>
23
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"
32
33 /*
34 * public API function implementations for the session_logic
35 */
36
37 pcep_session_logic_handle *session_logic_handle_ = NULL;
38 pcep_event_queue *session_logic_event_queue_ = NULL;
39 int session_id_ = 0;
40
41 void send_pcep_open(pcep_session *session); /* forward decl */
42
43 static bool run_session_logic_common(void)
44 {
45 if (session_logic_handle_ != NULL) {
46 pcep_log(LOG_WARNING,
47 "%s: Session Logic is already initialized.", __func__);
48 return false;
49 }
50
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));
54
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();
59
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),
65 NULL)
66 != 0) {
67 pcep_log(
68 LOG_ERR,
69 "%s: Cannot initialize session_logic event queue mutex.",
70 __func__);
71 return false;
72 }
73
74 pthread_cond_init(&(session_logic_handle_->session_logic_cond_var),
75 NULL);
76
77 if (pthread_mutex_init(&(session_logic_handle_->session_logic_mutex),
78 NULL)
79 != 0) {
80 pcep_log(LOG_ERR, "%s: Cannot initialize session_logic mutex.",
81 __func__);
82 return false;
83 }
84
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));
89
90 if (pthread_mutex_init(&(session_logic_handle_->session_list_mutex),
91 NULL)
92 != 0) {
93 pcep_log(LOG_ERR, "%s: Cannot initialize session_list mutex.",
94 __func__);
95 return false;
96 }
97
98 return true;
99 }
100
101
102 bool run_session_logic(void)
103 {
104 if (!run_session_logic_common()) {
105 return false;
106 }
107
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.",
111 __func__);
112 return false;
113 }
114
115 if (!initialize_timers(session_logic_timer_expire_handler)) {
116 pcep_log(LOG_ERR, "%s: Cannot initialize session_logic timers.",
117 __func__);
118 return false;
119 }
120
121 /* No need to call initialize_socket_comm_loop() since it will be
122 * called internally when the first socket_comm_session is created. */
123
124 return true;
125 }
126
127
128 bool run_session_logic_with_infra(pceplib_infra_config *infra_config)
129 {
130 if (infra_config == NULL) {
131 return run_session_logic();
132 }
133
134 /* Initialize the memory infrastructure before anything gets allocated
135 */
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);
144 }
145
146 if (!run_session_logic_common()) {
147 return false;
148 }
149
150 /* Create the pcep_session_logic pthread so it can be managed externally
151 */
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")) {
157 pcep_log(
158 LOG_ERR,
159 "%s: Cannot initialize external session_logic thread.",
160 __func__);
161 return false;
162 }
163 } else {
164 if (pthread_create(
165 &(session_logic_handle_->session_logic_thread),
166 NULL, session_logic_loop, session_logic_handle_)) {
167 pcep_log(LOG_ERR,
168 "%s: Cannot initialize session_logic thread.",
169 __func__);
170 return false;
171 }
172 }
173
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;
178
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)) {
185 pcep_log(
186 LOG_ERR,
187 "%s: Cannot initialize session_logic timers with infra.",
188 __func__);
189 return false;
190 }
191
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. */
195
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)) {
202 pcep_log(
203 LOG_ERR,
204 "%s: Cannot initialize session_logic socket comm with infra.",
205 __func__);
206 return false;
207 }
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)) {
214 pcep_log(
215 LOG_ERR,
216 "%s: Cannot initialize session_logic socket comm with infra.",
217 __func__);
218 return false;
219 }
220 }
221
222 return true;
223 }
224
225 bool run_session_logic_wait_for_completion(void)
226 {
227 if (!run_session_logic()) {
228 return false;
229 }
230
231 /* Blocking call, waits for session logic thread to complete */
232 pthread_join(session_logic_handle_->session_logic_thread, NULL);
233
234 return true;
235 }
236
237
238 bool stop_session_logic(void)
239 {
240 if (session_logic_handle_ == NULL) {
241 pcep_log(LOG_WARNING, "%s: Session logic already stopped",
242 __func__);
243 return false;
244 }
245
246 session_logic_handle_->active = false;
247 teardown_timers();
248
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);
254
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);
259
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_);
264
265 /* Explicitly stop the socket comm loop started by the pcep_sessions */
266 destroy_socket_comm_loop();
267
268 pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
269 session_logic_handle_ = NULL;
270
271 return true;
272 }
273
274
275 void close_pcep_session(pcep_session *session)
276 {
277 close_pcep_session_with_reason(session, PCEP_CLOSE_REASON_NO);
278 }
279
280 void close_pcep_session_with_reason(pcep_session *session,
281 enum pcep_close_reason reason)
282 {
283 struct pcep_message *close_msg = pcep_msg_create_close(reason);
284
285 pcep_log(
286 LOG_INFO,
287 "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]",
288 __func__, time(NULL), pthread_self(), session->session_id);
289
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;
293 }
294
295
296 void destroy_pcep_session(pcep_session *session)
297 {
298 if (session == NULL) {
299 pcep_log(LOG_WARNING, "%s: Cannot destroy NULL session",
300 __func__);
301 return;
302 }
303
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);
310 pcep_log(LOG_DEBUG,
311 "%s: destroy_pcep_session delete session_list sessionPtr %p",
312 __func__, session);
313
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);
318
319 if (session->pcc_config.pcep_msg_versioning != NULL) {
320 pceplib_free(PCEPLIB_INFRA,
321 session->pcc_config.pcep_msg_versioning);
322 }
323
324 if (session->pce_config.pcep_msg_versioning != NULL) {
325 pceplib_free(PCEPLIB_INFRA,
326 session->pce_config.pcep_msg_versioning);
327 }
328
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));
334 }
335
336 void pcep_session_cancel_timers(pcep_session *session)
337 {
338 if (session == NULL) {
339 return;
340 }
341
342 if (session->timer_id_dead_timer != TIMER_ID_NOT_SET) {
343 cancel_timer(session->timer_id_dead_timer);
344 }
345
346 if (session->timer_id_keep_alive != TIMER_ID_NOT_SET) {
347 cancel_timer(session->timer_id_keep_alive);
348 }
349
350 if (session->timer_id_open_keep_wait != TIMER_ID_NOT_SET) {
351 cancel_timer(session->timer_id_open_keep_wait);
352 }
353
354 if (session->timer_id_open_keep_alive != TIMER_ID_NOT_SET) {
355 cancel_timer(session->timer_id_open_keep_alive);
356 }
357 }
358
359 /* Internal util function */
360 static int get_next_session_id(void)
361 {
362 if (session_id_ == INT_MAX) {
363 session_id_ = 0;
364 }
365
366 return session_id_++;
367 }
368
369 /* Internal util function */
370 static pcep_session *create_pcep_session_pre_setup(pcep_configuration *config)
371 {
372 if (config == NULL) {
373 pcep_log(LOG_WARNING,
374 "%s: Cannot create pcep session with NULL config",
375 __func__);
376 return NULL;
377 }
378
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));
413 }
414
415 pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
416 ordered_list_add_node(session_logic_handle_->session_list, session);
417 pcep_log(
418 LOG_DEBUG,
419 "%s: create_pcep_session_pre_setup add session_list sessionPtr %p",
420 __func__, session);
421 pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
422
423 return session;
424 }
425
426 /* Internal util function */
427 static bool create_pcep_session_post_setup(pcep_session *session)
428 {
429 if (!socket_comm_session_connect_tcp(session->socket_comm_session)) {
430 pcep_log(LOG_WARNING, "%s: Cannot establish TCP socket.",
431 __func__);
432 destroy_pcep_session(session);
433
434 return false;
435 }
436
437 session->time_connected = time(NULL);
438 create_session_counters(session);
439
440 send_pcep_open(session);
441
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;
446
447 return true;
448 }
449
450 pcep_session *create_pcep_session(pcep_configuration *config,
451 struct in_addr *pce_ip)
452 {
453 if (pce_ip == NULL) {
454 pcep_log(LOG_WARNING,
455 "%s: Cannot create pcep session with NULL pce_ip",
456 __func__);
457 return NULL;
458 }
459
460 pcep_session *session = create_pcep_session_pre_setup(config);
461 if (session == NULL) {
462 return NULL;
463 }
464
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),
471 pce_ip,
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,
476 session);
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);
481
482 return NULL;
483 }
484
485 if (create_pcep_session_post_setup(session) == false) {
486 return NULL;
487 }
488
489 return session;
490 }
491
492 pcep_session *create_pcep_session_ipv6(pcep_configuration *config,
493 struct in6_addr *pce_ip)
494 {
495 if (pce_ip == NULL) {
496 pcep_log(LOG_WARNING,
497 "%s: Cannot create pcep session with NULL pce_ip",
498 __func__);
499 return NULL;
500 }
501
502 pcep_session *session = create_pcep_session_pre_setup(config);
503 if (session == NULL) {
504 return NULL;
505 }
506
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),
515 pce_ip,
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,
520 session);
521 if (session->socket_comm_session == NULL) {
522 pcep_log(LOG_WARNING,
523 "%s: Cannot establish ipv6 socket_comm_session.",
524 __func__);
525 destroy_pcep_session(session);
526
527 return NULL;
528 }
529
530 if (create_pcep_session_post_setup(session) == false) {
531 return NULL;
532 }
533
534 return session;
535 }
536
537
538 void session_send_message(pcep_session *session, struct pcep_message *message)
539 {
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);
544
545 increment_message_tx_counters(session, message);
546
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);
552 }
553
554
555 /* This function is also used in pcep_session_logic_states.c */
556 struct pcep_message *create_pcep_open(pcep_session *session)
557 {
558 /* create and send PCEP open
559 * with PCEP, the PCC sends the config the PCE should use in the open
560 * message,
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 */
570 dll_append(
571 tlv_list,
572 pcep_tlv_create_stateful_pce_capability(
573 /* U flag */
574 session->pcc_config
575 .support_stateful_pce_lsp_update,
576 /* S flag */
577 session->pcc_config.support_include_db_version,
578 /* I flag */
579 session->pcc_config
580 .support_pce_lsp_instantiation,
581 /* T flag */
582 session->pcc_config
583 .support_lsp_triggered_resync,
584 /* D flag */
585 session->pcc_config.support_lsp_delta_sync,
586 /* F flag */
587 session->pcc_config
588 .support_pce_triggered_initial_sync));
589 }
590
591 if (session->pcc_config.support_include_db_version) {
592 if (session->pcc_config.lsp_db_version != 0) {
593 dll_append(tlv_list,
594 pcep_tlv_create_lsp_db_version(
595 session->pcc_config.lsp_db_version));
596 }
597 }
598
599 if (session->pcc_config.support_sr_te_pst) {
600 bool flag_n = false;
601 bool flag_x = false;
602 if (session->pcc_config.pcep_msg_versioning
603 ->draft_ietf_pce_segment_routing_07
604 == false) {
605 flag_n = session->pcc_config.pcc_can_resolve_nai_to_sid;
606 flag_x = (session->pcc_config.max_sid_depth == 0);
607 }
608
609 struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv =
610 pcep_tlv_create_sr_pce_capability(
611 flag_n, flag_x,
612 session->pcc_config.max_sid_depth);
613
614 double_linked_list *sub_tlv_list = NULL;
615 if (session->pcc_config.pcep_msg_versioning
616 ->draft_ietf_pce_segment_routing_07
617 == true) {
618 /* With draft07, send the sr_pce_cap_tlv as a normal TLV
619 */
620 dll_append(tlv_list, sr_pce_cap_tlv);
621 } else {
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);
626 }
627
628 uint8_t *pst =
629 pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t));
630 *pst = SR_TE_PST;
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));
635 }
636
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,
640 tlv_list);
641
642 pcep_log(
643 LOG_INFO,
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);
647
648 return (open_msg);
649 }
650
651
652 void send_pcep_open(pcep_session *session)
653 {
654 session_send_message(session, create_pcep_open(session));
655 }
656
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.
660 */
661 bool session_exists(pcep_session *session)
662 {
663 if (session_logic_handle_ == NULL) {
664 pcep_log(LOG_DEBUG,
665 "%s: session_exists session_logic_handle_ is NULL",
666 __func__);
667 return false;
668 }
669
670 pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
671 bool retval =
672 (ordered_list_find(session_logic_handle_->session_list, session)
673 != NULL);
674 pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
675
676 return retval;
677 }