]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_session_logic.c
Merge pull request #8318 from qlyoung/improve-lua-autoconf-detection
[mirror_frr.git] / pceplib / pcep_session_logic.c
1 /*
2 * This file is part of the PCEPlib, a PCEP protocol library.
3 *
4 * Copyright (C) 2020 Volta Networks https://voltanet.io/
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author : Brady Johnson <brady@voltanet.io>
20 *
21 */
22
23
24 #include <errno.h>
25 #include <limits.h>
26 #include <pthread.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include "pcep_msg_encoding.h"
33 #include "pcep_session_logic.h"
34 #include "pcep_session_logic_internals.h"
35 #include "pcep_timers.h"
36 #include "pcep_utils_counters.h"
37 #include "pcep_utils_ordered_list.h"
38 #include "pcep_utils_logging.h"
39 #include "pcep_utils_memory.h"
40
41 /*
42 * public API function implementations for the session_logic
43 */
44
45 pcep_session_logic_handle *session_logic_handle_ = NULL;
46 pcep_event_queue *session_logic_event_queue_ = NULL;
47 int session_id_ = 0;
48
49 void send_pcep_open(pcep_session *session); /* forward decl */
50
51 static bool run_session_logic_common()
52 {
53 if (session_logic_handle_ != NULL) {
54 pcep_log(LOG_WARNING,
55 "%s: Session Logic is already initialized.", __func__);
56 return false;
57 }
58
59 session_logic_handle_ = pceplib_malloc(
60 PCEPLIB_INFRA, sizeof(pcep_session_logic_handle));
61 memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle));
62
63 session_logic_handle_->active = true;
64 session_logic_handle_->session_list =
65 ordered_list_initialize(pointer_compare_function);
66 session_logic_handle_->session_event_queue = queue_initialize();
67
68 /* Initialize the event queue */
69 session_logic_event_queue_ =
70 pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue));
71 session_logic_event_queue_->event_queue = queue_initialize();
72 if (pthread_mutex_init(&(session_logic_event_queue_->event_queue_mutex),
73 NULL)
74 != 0) {
75 pcep_log(
76 LOG_ERR,
77 "%s: Cannot initialize session_logic event queue mutex.",
78 __func__);
79 return false;
80 }
81
82 pthread_cond_init(&(session_logic_handle_->session_logic_cond_var),
83 NULL);
84
85 if (pthread_mutex_init(&(session_logic_handle_->session_logic_mutex),
86 NULL)
87 != 0) {
88 pcep_log(LOG_ERR, "%s: Cannot initialize session_logic mutex.",
89 __func__);
90 return false;
91 }
92
93 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
94 session_logic_handle_->session_logic_condition = true;
95 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
96 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
97
98 if (pthread_mutex_init(&(session_logic_handle_->session_list_mutex),
99 NULL)
100 != 0) {
101 pcep_log(LOG_ERR, "%s: Cannot initialize session_list mutex.",
102 __func__);
103 return false;
104 }
105
106 return true;
107 }
108
109
110 bool run_session_logic()
111 {
112 if (!run_session_logic_common()) {
113 return false;
114 }
115
116 if (pthread_create(&(session_logic_handle_->session_logic_thread), NULL,
117 session_logic_loop, session_logic_handle_)) {
118 pcep_log(LOG_ERR, "%s: Cannot initialize session_logic thread.",
119 __func__);
120 return false;
121 }
122
123 if (!initialize_timers(session_logic_timer_expire_handler)) {
124 pcep_log(LOG_ERR, "%s: Cannot initialize session_logic timers.",
125 __func__);
126 return false;
127 }
128
129 /* No need to call initialize_socket_comm_loop() since it will be
130 * called internally when the first socket_comm_session is created. */
131
132 return true;
133 }
134
135
136 bool run_session_logic_with_infra(pceplib_infra_config *infra_config)
137 {
138 if (infra_config == NULL) {
139 return run_session_logic();
140 }
141
142 /* Initialize the memory infrastructure before anything gets allocated
143 */
144 if (infra_config->pceplib_infra_mt != NULL
145 && infra_config->pceplib_messages_mt != NULL) {
146 pceplib_memory_initialize(
147 infra_config->pceplib_infra_mt,
148 infra_config->pceplib_messages_mt,
149 infra_config->malloc_func, infra_config->calloc_func,
150 infra_config->realloc_func, infra_config->strdup_func,
151 infra_config->free_func);
152 }
153
154 if (!run_session_logic_common()) {
155 return false;
156 }
157
158 /* Create the pcep_session_logic pthread so it can be managed externally
159 */
160 if (infra_config->pthread_create_func != NULL) {
161 if (infra_config->pthread_create_func(
162 &(session_logic_handle_->session_logic_thread),
163 NULL, session_logic_loop, session_logic_handle_,
164 "pcep_session_logic")) {
165 pcep_log(
166 LOG_ERR,
167 "%s: Cannot initialize external session_logic thread.",
168 __func__);
169 return false;
170 }
171 } else {
172 if (pthread_create(
173 &(session_logic_handle_->session_logic_thread),
174 NULL, session_logic_loop, session_logic_handle_)) {
175 pcep_log(LOG_ERR,
176 "%s: Cannot initialize session_logic thread.",
177 __func__);
178 return false;
179 }
180 }
181
182 session_logic_event_queue_->event_callback =
183 infra_config->pcep_event_func;
184 session_logic_event_queue_->event_callback_data =
185 infra_config->external_infra_data;
186
187 if (!initialize_timers_external_infra(
188 session_logic_timer_expire_handler,
189 infra_config->external_infra_data,
190 infra_config->timer_create_func,
191 infra_config->timer_cancel_func,
192 infra_config->pthread_create_func)) {
193 pcep_log(
194 LOG_ERR,
195 "%s: Cannot initialize session_logic timers with infra.",
196 __func__);
197 return false;
198 }
199
200 /* We found a problem with the FRR sockets, where not all the KeepAlive
201 * messages were received, so if the pthread_create_func is set, the
202 * internal PCEPlib socket infrastructure will be used. */
203
204 /* For the SocketComm, the socket_read/write_func and the
205 * pthread_create_func are mutually exclusive. */
206 if (infra_config->pthread_create_func != NULL) {
207 if (!initialize_socket_comm_external_infra(
208 infra_config->external_infra_data, NULL, NULL,
209 infra_config->pthread_create_func)) {
210 pcep_log(
211 LOG_ERR,
212 "%s: Cannot initialize session_logic socket comm with infra.",
213 __func__);
214 return false;
215 }
216 } else if (infra_config->socket_read_func != NULL
217 && infra_config->socket_write_func != NULL) {
218 if (!initialize_socket_comm_external_infra(
219 infra_config->external_infra_data,
220 infra_config->socket_read_func,
221 infra_config->socket_write_func, NULL)) {
222 pcep_log(
223 LOG_ERR,
224 "%s: Cannot initialize session_logic socket comm with infra.",
225 __func__);
226 return false;
227 }
228 }
229
230 return true;
231 }
232
233 bool run_session_logic_wait_for_completion()
234 {
235 if (!run_session_logic()) {
236 return false;
237 }
238
239 /* Blocking call, waits for session logic thread to complete */
240 pthread_join(session_logic_handle_->session_logic_thread, NULL);
241
242 return true;
243 }
244
245
246 bool stop_session_logic()
247 {
248 if (session_logic_handle_ == NULL) {
249 pcep_log(LOG_WARNING, "%s: Session logic already stopped",
250 __func__);
251 return false;
252 }
253
254 session_logic_handle_->active = false;
255 teardown_timers();
256
257 pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex));
258 session_logic_handle_->session_logic_condition = true;
259 pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var));
260 pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex));
261 pthread_join(session_logic_handle_->session_logic_thread, NULL);
262
263 pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex));
264 pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex));
265 ordered_list_destroy(session_logic_handle_->session_list);
266 queue_destroy(session_logic_handle_->session_event_queue);
267
268 /* destroy the event_queue */
269 pthread_mutex_destroy(&(session_logic_event_queue_->event_queue_mutex));
270 queue_destroy(session_logic_event_queue_->event_queue);
271 pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_);
272
273 /* Explicitly stop the socket comm loop started by the pcep_sessions */
274 destroy_socket_comm_loop();
275
276 pceplib_free(PCEPLIB_INFRA, session_logic_handle_);
277 session_logic_handle_ = NULL;
278
279 return true;
280 }
281
282
283 void close_pcep_session(pcep_session *session)
284 {
285 close_pcep_session_with_reason(session, PCEP_CLOSE_REASON_NO);
286 }
287
288 void close_pcep_session_with_reason(pcep_session *session,
289 enum pcep_close_reason reason)
290 {
291 struct pcep_message *close_msg = pcep_msg_create_close(reason);
292
293 pcep_log(
294 LOG_INFO,
295 "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]",
296 __func__, time(NULL), pthread_self(), session->session_id);
297
298 session_send_message(session, close_msg);
299 socket_comm_session_close_tcp_after_write(session->socket_comm_session);
300 session->session_state = SESSION_STATE_INITIALIZED;
301 }
302
303
304 void destroy_pcep_session(pcep_session *session)
305 {
306 if (session == NULL) {
307 pcep_log(LOG_WARNING, "%s: Cannot destroy NULL session",
308 __func__);
309 return;
310 }
311
312 /* Remove the session from the session_list and synchronize session
313 * destroy with the session_logic_loop, so that no in-flight events
314 * will be handled now that the session is destroyed. */
315 pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
316 ordered_list_remove_first_node_equals(
317 session_logic_handle_->session_list, session);
318 pcep_log(LOG_DEBUG,
319 "%s: destroy_pcep_session delete session_list sessionPtr %p",
320 __func__, session);
321
322 pcep_session_cancel_timers(session);
323 delete_counters_group(session->pcep_session_counters);
324 queue_destroy_with_data(session->num_unknown_messages_time_queue);
325 socket_comm_session_teardown(session->socket_comm_session);
326
327 if (session->pcc_config.pcep_msg_versioning != NULL) {
328 pceplib_free(PCEPLIB_INFRA,
329 session->pcc_config.pcep_msg_versioning);
330 }
331
332 if (session->pce_config.pcep_msg_versioning != NULL) {
333 pceplib_free(PCEPLIB_INFRA,
334 session->pce_config.pcep_msg_versioning);
335 }
336
337 int session_id = session->session_id;
338 pceplib_free(PCEPLIB_INFRA, session);
339 pcep_log(LOG_INFO, "%s: [%ld-%ld] session [%d] destroyed", __func__,
340 time(NULL), pthread_self(), session_id);
341 pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
342 }
343
344 void pcep_session_cancel_timers(pcep_session *session)
345 {
346 if (session == NULL) {
347 return;
348 }
349
350 if (session->timer_id_dead_timer != TIMER_ID_NOT_SET) {
351 cancel_timer(session->timer_id_dead_timer);
352 }
353
354 if (session->timer_id_keep_alive != TIMER_ID_NOT_SET) {
355 cancel_timer(session->timer_id_keep_alive);
356 }
357
358 if (session->timer_id_open_keep_wait != TIMER_ID_NOT_SET) {
359 cancel_timer(session->timer_id_open_keep_wait);
360 }
361
362 if (session->timer_id_open_keep_alive != TIMER_ID_NOT_SET) {
363 cancel_timer(session->timer_id_open_keep_alive);
364 }
365 }
366
367 /* Internal util function */
368 static int get_next_session_id()
369 {
370 if (session_id_ == INT_MAX) {
371 session_id_ = 0;
372 }
373
374 return session_id_++;
375 }
376
377 /* Internal util function */
378 static pcep_session *create_pcep_session_pre_setup(pcep_configuration *config)
379 {
380 if (config == NULL) {
381 pcep_log(LOG_WARNING,
382 "%s: Cannot create pcep session with NULL config",
383 __func__);
384 return NULL;
385 }
386
387 pcep_session *session =
388 pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session));
389 memset(session, 0, sizeof(pcep_session));
390 session->session_id = get_next_session_id();
391 session->session_state = SESSION_STATE_INITIALIZED;
392 session->timer_id_open_keep_wait = TIMER_ID_NOT_SET;
393 session->timer_id_open_keep_alive = TIMER_ID_NOT_SET;
394 session->timer_id_dead_timer = TIMER_ID_NOT_SET;
395 session->timer_id_keep_alive = TIMER_ID_NOT_SET;
396 session->stateful_pce = false;
397 session->num_unknown_messages_time_queue = queue_initialize();
398 session->pce_open_received = false;
399 session->pce_open_rejected = false;
400 session->pce_open_keep_alive_sent = false;
401 session->pcc_open_rejected = false;
402 session->pce_open_accepted = false;
403 session->pcc_open_accepted = false;
404 session->destroy_session_after_write = false;
405 session->lsp_db_version = config->lsp_db_version;
406 memcpy(&(session->pcc_config), config, sizeof(pcep_configuration));
407 /* copy the pcc_config to the pce_config until we receive the open
408 * keep_alive response */
409 memcpy(&(session->pce_config), config, sizeof(pcep_configuration));
410 if (config->pcep_msg_versioning != NULL) {
411 session->pcc_config.pcep_msg_versioning = pceplib_malloc(
412 PCEPLIB_INFRA, sizeof(struct pcep_versioning));
413 memcpy(session->pcc_config.pcep_msg_versioning,
414 config->pcep_msg_versioning,
415 sizeof(struct pcep_versioning));
416 session->pce_config.pcep_msg_versioning = pceplib_malloc(
417 PCEPLIB_INFRA, sizeof(struct pcep_versioning));
418 memcpy(session->pce_config.pcep_msg_versioning,
419 config->pcep_msg_versioning,
420 sizeof(struct pcep_versioning));
421 }
422
423 pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
424 ordered_list_add_node(session_logic_handle_->session_list, session);
425 pcep_log(
426 LOG_DEBUG,
427 "%s: create_pcep_session_pre_setup add session_list sessionPtr %p",
428 __func__, session);
429 pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
430
431 return session;
432 }
433
434 /* Internal util function */
435 static bool create_pcep_session_post_setup(pcep_session *session)
436 {
437 if (!socket_comm_session_connect_tcp(session->socket_comm_session)) {
438 pcep_log(LOG_WARNING, "%s: Cannot establish TCP socket.",
439 __func__);
440 destroy_pcep_session(session);
441
442 return false;
443 }
444
445 session->time_connected = time(NULL);
446 create_session_counters(session);
447
448 send_pcep_open(session);
449
450 session->session_state = SESSION_STATE_PCEP_CONNECTING;
451 session->timer_id_open_keep_wait =
452 create_timer(session->pcc_config.keep_alive_seconds, session);
453 // session->session_state = SESSION_STATE_OPENED;
454
455 return true;
456 }
457
458 pcep_session *create_pcep_session(pcep_configuration *config,
459 struct in_addr *pce_ip)
460 {
461 if (pce_ip == NULL) {
462 pcep_log(LOG_WARNING,
463 "%s: Cannot create pcep session with NULL pce_ip",
464 __func__);
465 return NULL;
466 }
467
468 pcep_session *session = create_pcep_session_pre_setup(config);
469 if (session == NULL) {
470 return NULL;
471 }
472
473 session->socket_comm_session = socket_comm_session_initialize_with_src(
474 NULL, session_logic_msg_ready_handler,
475 session_logic_message_sent_handler,
476 session_logic_conn_except_notifier, &(config->src_ip.src_ipv4),
477 ((config->src_pcep_port == 0) ? PCEP_TCP_PORT
478 : config->src_pcep_port),
479 pce_ip,
480 ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT
481 : config->dst_pcep_port),
482 config->socket_connect_timeout_millis,
483 config->tcp_authentication_str, config->is_tcp_auth_md5,
484 session);
485 if (session->socket_comm_session == NULL) {
486 pcep_log(LOG_WARNING,
487 "%s: Cannot establish socket_comm_session.", __func__);
488 destroy_pcep_session(session);
489
490 return NULL;
491 }
492
493 if (create_pcep_session_post_setup(session) == false) {
494 return NULL;
495 }
496
497 return session;
498 }
499
500 pcep_session *create_pcep_session_ipv6(pcep_configuration *config,
501 struct in6_addr *pce_ip)
502 {
503 if (pce_ip == NULL) {
504 pcep_log(LOG_WARNING,
505 "%s: Cannot create pcep session with NULL pce_ip",
506 __func__);
507 return NULL;
508 }
509
510 pcep_session *session = create_pcep_session_pre_setup(config);
511 if (session == NULL) {
512 return NULL;
513 }
514
515 session->socket_comm_session =
516 socket_comm_session_initialize_with_src_ipv6(
517 NULL, session_logic_msg_ready_handler,
518 session_logic_message_sent_handler,
519 session_logic_conn_except_notifier,
520 &(config->src_ip.src_ipv6),
521 ((config->src_pcep_port == 0) ? PCEP_TCP_PORT
522 : config->src_pcep_port),
523 pce_ip,
524 ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT
525 : config->dst_pcep_port),
526 config->socket_connect_timeout_millis,
527 config->tcp_authentication_str, config->is_tcp_auth_md5,
528 session);
529 if (session->socket_comm_session == NULL) {
530 pcep_log(LOG_WARNING,
531 "%s: Cannot establish ipv6 socket_comm_session.",
532 __func__);
533 destroy_pcep_session(session);
534
535 return NULL;
536 }
537
538 if (create_pcep_session_post_setup(session) == false) {
539 return NULL;
540 }
541
542 return session;
543 }
544
545
546 void session_send_message(pcep_session *session, struct pcep_message *message)
547 {
548 pcep_encode_message(message, session->pcc_config.pcep_msg_versioning);
549 socket_comm_session_send_message(session->socket_comm_session,
550 (char *)message->encoded_message,
551 message->encoded_message_length, true);
552
553 increment_message_tx_counters(session, message);
554
555 /* The message->encoded_message will be freed in
556 * socket_comm_session_send_message() once sent.
557 * Setting to NULL here so pcep_msg_free_message() does not free it */
558 message->encoded_message = NULL;
559 pcep_msg_free_message(message);
560 }
561
562
563 /* This function is also used in pcep_session_logic_states.c */
564 struct pcep_message *create_pcep_open(pcep_session *session)
565 {
566 /* create and send PCEP open
567 * with PCEP, the PCC sends the config the PCE should use in the open
568 * message,
569 * and the PCE will send an open with the config the PCC should use. */
570 double_linked_list *tlv_list = dll_initialize();
571 if (session->pcc_config.support_stateful_pce_lsp_update
572 || session->pcc_config.support_pce_lsp_instantiation
573 || session->pcc_config.support_include_db_version
574 || session->pcc_config.support_lsp_triggered_resync
575 || session->pcc_config.support_lsp_delta_sync
576 || session->pcc_config.support_pce_triggered_initial_sync) {
577 /* Prepend this TLV as the first in the list */
578 dll_append(
579 tlv_list,
580 pcep_tlv_create_stateful_pce_capability(
581 /* U flag */
582 session->pcc_config
583 .support_stateful_pce_lsp_update,
584 /* S flag */
585 session->pcc_config.support_include_db_version,
586 /* I flag */
587 session->pcc_config
588 .support_pce_lsp_instantiation,
589 /* T flag */
590 session->pcc_config.support_lsp_triggered_resync,
591 /* D flag */
592 session->pcc_config.support_lsp_delta_sync,
593 /* F flag */
594 session->pcc_config.support_pce_triggered_initial_sync));
595 }
596
597 if (session->pcc_config.support_include_db_version) {
598 if (session->pcc_config.lsp_db_version != 0) {
599 dll_append(tlv_list,
600 pcep_tlv_create_lsp_db_version(
601 session->pcc_config.lsp_db_version));
602 }
603 }
604
605 if (session->pcc_config.support_sr_te_pst) {
606 bool flag_n = false;
607 bool flag_x = false;
608 if (session->pcc_config.pcep_msg_versioning
609 ->draft_ietf_pce_segment_routing_07
610 == false) {
611 flag_n = session->pcc_config.pcc_can_resolve_nai_to_sid;
612 flag_x = (session->pcc_config.max_sid_depth == 0);
613 }
614
615 struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv =
616 pcep_tlv_create_sr_pce_capability(
617 flag_n, flag_x,
618 session->pcc_config.max_sid_depth);
619
620 double_linked_list *sub_tlv_list = NULL;
621 if (session->pcc_config.pcep_msg_versioning
622 ->draft_ietf_pce_segment_routing_07
623 == true) {
624 /* With draft07, send the sr_pce_cap_tlv as a normal TLV
625 */
626 dll_append(tlv_list, sr_pce_cap_tlv);
627 } else {
628 /* With draft16, send the sr_pce_cap_tlv as a sub-TLV in
629 * the path_setup_type_capability TLV */
630 sub_tlv_list = dll_initialize();
631 dll_append(sub_tlv_list, sr_pce_cap_tlv);
632 }
633
634 uint8_t *pst =
635 pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t));
636 *pst = SR_TE_PST;
637 double_linked_list *pst_list = dll_initialize();
638 dll_append(pst_list, pst);
639 dll_append(tlv_list, pcep_tlv_create_path_setup_type_capability(
640 pst_list, sub_tlv_list));
641 }
642
643 struct pcep_message *open_msg = pcep_msg_create_open_with_tlvs(
644 session->pcc_config.keep_alive_seconds,
645 session->pcc_config.dead_timer_seconds, session->session_id,
646 tlv_list);
647
648 pcep_log(
649 LOG_INFO,
650 "%s: [%ld-%ld] pcep_session_logic create open message: TLVs [%d] for session [%d]",
651 __func__, time(NULL), pthread_self(), tlv_list->num_entries,
652 session->session_id);
653
654 return (open_msg);
655 }
656
657
658 void send_pcep_open(pcep_session *session)
659 {
660 session_send_message(session, create_pcep_open(session));
661 }
662
663 /* This is a blocking call, since it is synchronized with destroy_pcep_session()
664 * and session_logic_loop(). It may be possible that the session has been
665 * deleted but API users havent been informed yet.
666 */
667 bool session_exists(pcep_session *session)
668 {
669 if (session_logic_handle_ == NULL) {
670 pcep_log(LOG_DEBUG,
671 "%s: session_exists session_logic_handle_ is NULL",
672 __func__);
673 return false;
674 }
675
676 pthread_mutex_lock(&(session_logic_handle_->session_list_mutex));
677 bool retval =
678 (ordered_list_find(session_logic_handle_->session_list, session)
679 != NULL);
680 pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex));
681
682 return retval;
683 }