]> git.proxmox.com Git - mirror_frr.git/blob - pceplib/pcep_socket_comm.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / pceplib / pcep_socket_comm.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 /*
13 * Implementation of public API functions.
14 */
15
16 #include <zebra.h>
17
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <netdb.h> // gethostbyname
21 #include <stdbool.h>
22 #include <string.h>
23 #include <unistd.h> // close
24
25 #include <arpa/inet.h> // sockets etc.
26 #include <sys/types.h> // sockets etc.
27 #include <sys/socket.h> // sockets etc.
28
29 #include "pcep.h"
30 #include "pcep_socket_comm.h"
31 #include "pcep_socket_comm_internals.h"
32 #include "pcep_utils_logging.h"
33 #include "pcep_utils_memory.h"
34 #include "pcep_utils_ordered_list.h"
35 #include "pcep_utils_queue.h"
36
37 bool initialize_socket_comm_pre(void);
38 bool socket_comm_session_initialize_post(
39 pcep_socket_comm_session *socket_comm_session);
40
41 pcep_socket_comm_handle *socket_comm_handle_ = NULL;
42
43
44 /* simple compare method callback used by pcep_utils_ordered_list
45 * for ordered list insertion. */
46 int socket_fd_node_compare(void *list_entry, void *new_entry)
47 {
48 return ((pcep_socket_comm_session *)new_entry)->socket_fd
49 - ((pcep_socket_comm_session *)list_entry)->socket_fd;
50 }
51
52
53 bool initialize_socket_comm_pre(void)
54 {
55 socket_comm_handle_ =
56 pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle));
57 memset(socket_comm_handle_, 0, sizeof(pcep_socket_comm_handle));
58
59 socket_comm_handle_->active = true;
60 socket_comm_handle_->num_active_sessions = 0;
61 socket_comm_handle_->read_list =
62 ordered_list_initialize(socket_fd_node_compare);
63 socket_comm_handle_->write_list =
64 ordered_list_initialize(socket_fd_node_compare);
65 socket_comm_handle_->session_list =
66 ordered_list_initialize(pointer_compare_function);
67 FD_ZERO(&socket_comm_handle_->except_master_set);
68 FD_ZERO(&socket_comm_handle_->read_master_set);
69 FD_ZERO(&socket_comm_handle_->write_master_set);
70
71 if (pthread_mutex_init(&(socket_comm_handle_->socket_comm_mutex), NULL)
72 != 0) {
73 pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm mutex.",
74 __func__);
75 pceplib_free(PCEPLIB_INFRA, socket_comm_handle_);
76 socket_comm_handle_ = NULL;
77
78 return false;
79 }
80
81 return true;
82 }
83
84 bool initialize_socket_comm_external_infra(
85 void *external_infra_data, ext_socket_read socket_read_cb,
86 ext_socket_write socket_write_cb,
87 ext_socket_pthread_create_callback thread_create_func)
88 {
89 if (socket_comm_handle_ != NULL) {
90 /* already initialized */
91 return true;
92 }
93
94 if (initialize_socket_comm_pre() == false) {
95 return false;
96 }
97
98 /* Notice: If the thread_create_func is set, then both the
99 * socket_read_cb and the socket_write_cb SHOULD be NULL. */
100 if (thread_create_func != NULL) {
101 if (thread_create_func(
102 &(socket_comm_handle_->socket_comm_thread), NULL,
103 socket_comm_loop, socket_comm_handle_,
104 "pceplib_timers")) {
105 pcep_log(
106 LOG_ERR,
107 "%s: Cannot initialize external socket_comm thread.",
108 __func__);
109 return false;
110 }
111 }
112
113 socket_comm_handle_->external_infra_data = external_infra_data;
114 socket_comm_handle_->socket_write_func = socket_write_cb;
115 socket_comm_handle_->socket_read_func = socket_read_cb;
116
117 return true;
118 }
119
120 bool initialize_socket_comm_loop(void)
121 {
122 if (socket_comm_handle_ != NULL) {
123 /* already initialized */
124 return true;
125 }
126
127 if (initialize_socket_comm_pre() == false) {
128 return false;
129 }
130
131 /* Launch socket comm loop pthread */
132 if (pthread_create(&(socket_comm_handle_->socket_comm_thread), NULL,
133 socket_comm_loop, socket_comm_handle_)) {
134 pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm thread.",
135 __func__);
136 return false;
137 }
138
139 return true;
140 }
141
142
143 bool destroy_socket_comm_loop(void)
144 {
145 socket_comm_handle_->active = false;
146
147 pthread_join(socket_comm_handle_->socket_comm_thread, NULL);
148 ordered_list_destroy(socket_comm_handle_->read_list);
149 ordered_list_destroy(socket_comm_handle_->write_list);
150 ordered_list_destroy(socket_comm_handle_->session_list);
151 pthread_mutex_destroy(&(socket_comm_handle_->socket_comm_mutex));
152
153 pceplib_free(PCEPLIB_INFRA, socket_comm_handle_);
154 socket_comm_handle_ = NULL;
155
156 return true;
157 }
158
159 /* Internal common init function */
160 static pcep_socket_comm_session *socket_comm_session_initialize_pre(
161 message_received_handler message_handler,
162 message_ready_to_read_handler message_ready_handler,
163 message_sent_notifier msg_sent_notifier,
164 connection_except_notifier notifier, uint32_t connect_timeout_millis,
165 const char *tcp_authentication_str, bool is_tcp_auth_md5,
166 void *session_data)
167 {
168 /* check that not both message handlers were set */
169 if (message_handler != NULL && message_ready_handler != NULL) {
170 pcep_log(
171 LOG_WARNING,
172 "%s: Only one of <message_received_handler | message_ready_to_read_handler> can be set.",
173 __func__);
174 return NULL;
175 }
176
177 /* check that at least one message handler was set */
178 if (message_handler == NULL && message_ready_handler == NULL) {
179 pcep_log(
180 LOG_WARNING,
181 "%s: At least one of <message_received_handler | message_ready_to_read_handler> must be set.",
182 __func__);
183 return NULL;
184 }
185
186 if (!initialize_socket_comm_loop()) {
187 pcep_log(LOG_WARNING,
188 "%s: ERROR: cannot initialize socket_comm_loop.",
189 __func__);
190
191 return NULL;
192 }
193
194 /* initialize everything for a pcep_session socket_comm */
195
196 pcep_socket_comm_session *socket_comm_session =
197 pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session));
198 memset(socket_comm_session, 0, sizeof(pcep_socket_comm_session));
199
200 socket_comm_handle_->num_active_sessions++;
201 socket_comm_session->close_after_write = false;
202 socket_comm_session->session_data = session_data;
203 socket_comm_session->message_handler = message_handler;
204 socket_comm_session->message_ready_to_read_handler =
205 message_ready_handler;
206 socket_comm_session->message_sent_handler = msg_sent_notifier;
207 socket_comm_session->conn_except_notifier = notifier;
208 socket_comm_session->message_queue = queue_initialize();
209 socket_comm_session->connect_timeout_millis = connect_timeout_millis;
210 socket_comm_session->external_socket_data = NULL;
211 if (tcp_authentication_str != NULL) {
212 socket_comm_session->is_tcp_auth_md5 = is_tcp_auth_md5;
213 strlcpy(socket_comm_session->tcp_authentication_str,
214 tcp_authentication_str,
215 sizeof(socket_comm_session->tcp_authentication_str));
216 }
217
218 return socket_comm_session;
219 }
220
221 /* Internal common init function */
222 bool socket_comm_session_initialize_post(
223 pcep_socket_comm_session *socket_comm_session)
224 {
225 /* If we dont use SO_REUSEADDR, the socket will take 2 TIME_WAIT
226 * periods before being closed in the kernel if bind() was called */
227 int reuse_addr = 1;
228 if (setsockopt(socket_comm_session->socket_fd, SOL_SOCKET, SO_REUSEADDR,
229 &reuse_addr, sizeof(int))
230 < 0) {
231 pcep_log(
232 LOG_WARNING,
233 "%s: Error in setsockopt() SO_REUSEADDR errno [%d %s].",
234 __func__, errno, strerror(errno));
235 socket_comm_session_teardown(socket_comm_session);
236
237 return false;
238 }
239
240 struct sockaddr *src_sock_addr =
241 (socket_comm_session->is_ipv6
242 ? (struct sockaddr *)&(
243 socket_comm_session->src_sock_addr
244 .src_sock_addr_ipv6)
245 : (struct sockaddr *)&(
246 socket_comm_session->src_sock_addr
247 .src_sock_addr_ipv4));
248 int addr_len = (socket_comm_session->is_ipv6
249 ? sizeof(socket_comm_session->src_sock_addr
250 .src_sock_addr_ipv6)
251 : sizeof(socket_comm_session->src_sock_addr
252 .src_sock_addr_ipv4));
253 if (bind(socket_comm_session->socket_fd, src_sock_addr, addr_len)
254 == -1) {
255 pcep_log(LOG_WARNING,
256 "%s: Cannot bind address to socket errno [%d %s].",
257 __func__, errno, strerror(errno));
258 socket_comm_session_teardown(socket_comm_session);
259
260 return false;
261 }
262
263 /* Register the session as active with the Socket Comm Loop */
264 pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
265 ordered_list_add_node(socket_comm_handle_->session_list,
266 socket_comm_session);
267 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
268
269 /* dont connect to the destination yet, since the PCE will have a timer
270 * for max time between TCP connect and PCEP open. we'll connect later
271 * when we send the PCEP open. */
272
273 return true;
274 }
275
276
277 pcep_socket_comm_session *socket_comm_session_initialize(
278 message_received_handler message_handler,
279 message_ready_to_read_handler message_ready_handler,
280 message_sent_notifier msg_sent_notifier,
281 connection_except_notifier notifier, struct in_addr *dest_ip,
282 short dest_port, uint32_t connect_timeout_millis,
283 const char *tcp_authentication_str, bool is_tcp_auth_md5,
284 void *session_data)
285 {
286 return socket_comm_session_initialize_with_src(
287 message_handler, message_ready_handler, msg_sent_notifier,
288 notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis,
289 tcp_authentication_str, is_tcp_auth_md5, session_data);
290 }
291
292 pcep_socket_comm_session *socket_comm_session_initialize_ipv6(
293 message_received_handler message_handler,
294 message_ready_to_read_handler message_ready_handler,
295 message_sent_notifier msg_sent_notifier,
296 connection_except_notifier notifier, struct in6_addr *dest_ip,
297 short dest_port, uint32_t connect_timeout_millis,
298 const char *tcp_authentication_str, bool is_tcp_auth_md5,
299 void *session_data)
300 {
301 return socket_comm_session_initialize_with_src_ipv6(
302 message_handler, message_ready_handler, msg_sent_notifier,
303 notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis,
304 tcp_authentication_str, is_tcp_auth_md5, session_data);
305 }
306
307
308 pcep_socket_comm_session *socket_comm_session_initialize_with_src(
309 message_received_handler message_handler,
310 message_ready_to_read_handler message_ready_handler,
311 message_sent_notifier msg_sent_notifier,
312 connection_except_notifier notifier, struct in_addr *src_ip,
313 short src_port, struct in_addr *dest_ip, short dest_port,
314 uint32_t connect_timeout_millis, const char *tcp_authentication_str,
315 bool is_tcp_auth_md5, void *session_data)
316 {
317 if (dest_ip == NULL) {
318 pcep_log(LOG_WARNING, "%s: dest_ipv4 is NULL", __func__);
319 return NULL;
320 }
321
322 pcep_socket_comm_session *socket_comm_session =
323 socket_comm_session_initialize_pre(
324 message_handler, message_ready_handler,
325 msg_sent_notifier, notifier, connect_timeout_millis,
326 tcp_authentication_str, is_tcp_auth_md5, session_data);
327 if (socket_comm_session == NULL) {
328 return NULL;
329 }
330
331 socket_comm_session->socket_fd =
332 socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
333 if (socket_comm_session->socket_fd == -1) {
334 pcep_log(LOG_WARNING,
335 "%s: Cannot create ipv4 socket errno [%d %s].",
336 __func__, errno, strerror(errno));
337 socket_comm_session_teardown(
338 socket_comm_session); // socket_comm_session freed
339 // inside fn so NOLINT next.
340
341 return NULL; // NOLINT(clang-analyzer-unix.Malloc)
342 }
343
344 socket_comm_session->is_ipv6 = false;
345 socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family =
346 AF_INET;
347 socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family =
348 AF_INET;
349 socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port =
350 htons(dest_port);
351 socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port =
352 htons(src_port);
353 socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr
354 .s_addr = dest_ip->s_addr;
355 if (src_ip != NULL) {
356 socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr
357 .s_addr = src_ip->s_addr;
358 } else {
359 socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr
360 .s_addr = INADDR_ANY;
361 }
362
363 if (socket_comm_session_initialize_post(socket_comm_session) == false) {
364 return NULL;
365 }
366
367 return socket_comm_session;
368 }
369
370 pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6(
371 message_received_handler message_handler,
372 message_ready_to_read_handler message_ready_handler,
373 message_sent_notifier msg_sent_notifier,
374 connection_except_notifier notifier, struct in6_addr *src_ip,
375 short src_port, struct in6_addr *dest_ip, short dest_port,
376 uint32_t connect_timeout_millis, const char *tcp_authentication_str,
377 bool is_tcp_auth_md5, void *session_data)
378 {
379 if (dest_ip == NULL) {
380 pcep_log(LOG_WARNING, "%s: dest_ipv6 is NULL", __func__);
381 return NULL;
382 }
383
384 pcep_socket_comm_session *socket_comm_session =
385 socket_comm_session_initialize_pre(
386 message_handler, message_ready_handler,
387 msg_sent_notifier, notifier, connect_timeout_millis,
388 tcp_authentication_str, is_tcp_auth_md5, session_data);
389 if (socket_comm_session == NULL) {
390 return NULL;
391 }
392
393 socket_comm_session->socket_fd =
394 socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
395 if (socket_comm_session->socket_fd == -1) {
396 pcep_log(LOG_WARNING,
397 "%s: Cannot create ipv6 socket errno [%d %s].",
398 __func__, errno, strerror(errno));
399 socket_comm_session_teardown(
400 socket_comm_session); // socket_comm_session freed
401 // inside fn so NOLINT next.
402
403 return NULL; // NOLINT(clang-analyzer-unix.Malloc)
404 }
405
406 socket_comm_session->is_ipv6 = true;
407 socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family =
408 AF_INET6;
409 socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family =
410 AF_INET6;
411 socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port =
412 htons(dest_port);
413 socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port =
414 htons(src_port);
415 memcpy(&socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6
416 .sin6_addr,
417 dest_ip, sizeof(struct in6_addr));
418 if (src_ip != NULL) {
419 memcpy(&socket_comm_session->src_sock_addr.src_sock_addr_ipv6
420 .sin6_addr,
421 src_ip, sizeof(struct in6_addr));
422 } else {
423 socket_comm_session->src_sock_addr.src_sock_addr_ipv6
424 .sin6_addr = in6addr_any;
425 }
426
427 if (socket_comm_session_initialize_post(socket_comm_session) == false) {
428 return NULL;
429 }
430
431 return socket_comm_session;
432 }
433
434
435 bool socket_comm_session_connect_tcp(
436 pcep_socket_comm_session *socket_comm_session)
437 {
438 if (socket_comm_session == NULL) {
439 pcep_log(
440 LOG_WARNING,
441 "%s: socket_comm_session_connect_tcp NULL socket_comm_session.",
442 __func__);
443 return NULL;
444 }
445
446 /* Set the socket to non-blocking, so connect() does not block */
447 int fcntl_arg;
448 if ((fcntl_arg = fcntl(socket_comm_session->socket_fd, F_GETFL, NULL))
449 < 0) {
450 pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_GETFL) [%d %s]",
451 __func__, errno, strerror(errno));
452 return false;
453 }
454
455 fcntl_arg |= O_NONBLOCK;
456 if (fcntl(socket_comm_session->socket_fd, F_SETFL, fcntl_arg) < 0) {
457 pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_SETFL) [%d %s]",
458 __func__, errno, strerror(errno));
459 return false;
460 }
461
462 #if HAVE_DECL_TCP_MD5SIG
463 /* TCP authentication, currently only TCP MD5 RFC2385 is supported */
464 if (socket_comm_session->tcp_authentication_str[0] != '\0') {
465 #if defined(linux) || defined(GNU_LINUX)
466 struct tcp_md5sig sig;
467 memset(&sig, 0, sizeof(sig));
468 if (socket_comm_session->is_ipv6) {
469 memcpy(&sig.tcpm_addr,
470 &socket_comm_session->dest_sock_addr
471 .dest_sock_addr_ipv6,
472 sizeof(struct sockaddr_in6));
473 } else {
474 memcpy(&sig.tcpm_addr,
475 &socket_comm_session->dest_sock_addr
476 .dest_sock_addr_ipv4,
477 sizeof(struct sockaddr_in));
478 }
479 sig.tcpm_keylen =
480 strlen(socket_comm_session->tcp_authentication_str);
481 memcpy(sig.tcpm_key,
482 socket_comm_session->tcp_authentication_str,
483 sig.tcpm_keylen);
484 #else
485 int sig = 1;
486 #endif
487 if (setsockopt(socket_comm_session->socket_fd, IPPROTO_TCP,
488 TCP_MD5SIG, &sig, sizeof(sig))
489 == -1) {
490 pcep_log(LOG_ERR, "%s: Failed to setsockopt(): [%d %s]",
491 __func__, errno, strerror(errno));
492 return false;
493 }
494 }
495 #endif
496
497 int connect_result = 0;
498 if (socket_comm_session->is_ipv6) {
499 connect_result = connect(
500 socket_comm_session->socket_fd,
501 (struct sockaddr *)&(socket_comm_session->dest_sock_addr
502 .dest_sock_addr_ipv6),
503 sizeof(socket_comm_session->dest_sock_addr
504 .dest_sock_addr_ipv6));
505 } else {
506 connect_result = connect(
507 socket_comm_session->socket_fd,
508 (struct sockaddr *)&(socket_comm_session->dest_sock_addr
509 .dest_sock_addr_ipv4),
510 sizeof(socket_comm_session->dest_sock_addr
511 .dest_sock_addr_ipv4));
512 }
513
514 if (connect_result < 0) {
515 if (errno == EINPROGRESS) {
516 /* Calculate the configured timeout in seconds and
517 * microseconds */
518 struct timeval tv;
519 if (socket_comm_session->connect_timeout_millis
520 > 1000) {
521 tv.tv_sec = socket_comm_session
522 ->connect_timeout_millis
523 / 1000;
524 tv.tv_usec = (socket_comm_session
525 ->connect_timeout_millis
526 - (tv.tv_sec * 1000))
527 * 1000;
528 } else {
529 tv.tv_sec = 0;
530 tv.tv_usec = socket_comm_session
531 ->connect_timeout_millis
532 * 1000;
533 }
534
535 /* Use select to wait a max timeout for connect
536 * https://stackoverflow.com/questions/2597608/c-socket-connection-timeout
537 */
538 fd_set fdset;
539 FD_ZERO(&fdset);
540 FD_SET(socket_comm_session->socket_fd, &fdset);
541 if (select(socket_comm_session->socket_fd + 1, NULL,
542 &fdset, NULL, &tv)
543 > 0) {
544 int so_error;
545 socklen_t len = sizeof(so_error);
546 getsockopt(socket_comm_session->socket_fd,
547 SOL_SOCKET, SO_ERROR, &so_error,
548 &len);
549 if (so_error) {
550 pcep_log(
551 LOG_WARNING,
552 "%s: TCP connect failed on socket_fd [%d].",
553 __func__,
554 socket_comm_session->socket_fd);
555 return false;
556 }
557 } else {
558 pcep_log(
559 LOG_WARNING,
560 "%s: TCP connect timed-out on socket_fd [%d].",
561 __func__,
562 socket_comm_session->socket_fd);
563 return false;
564 }
565 } else {
566 pcep_log(
567 LOG_WARNING,
568 "%s: TCP connect, error connecting on socket_fd [%d] errno [%d %s]",
569 __func__, socket_comm_session->socket_fd, errno,
570 strerror(errno));
571 return false;
572 }
573 }
574
575 pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
576 /* once the TCP connection is open, we should be ready to read at any
577 * time */
578 ordered_list_add_node(socket_comm_handle_->read_list,
579 socket_comm_session);
580
581 if (socket_comm_handle_->socket_read_func != NULL) {
582 socket_comm_handle_->socket_read_func(
583 socket_comm_handle_->external_infra_data,
584 &socket_comm_session->external_socket_data,
585 socket_comm_session->socket_fd, socket_comm_handle_);
586 }
587 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
588
589 return true;
590 }
591
592
593 bool socket_comm_session_close_tcp(
594 pcep_socket_comm_session *socket_comm_session)
595 {
596 if (socket_comm_session == NULL) {
597 pcep_log(
598 LOG_WARNING,
599 "%s: socket_comm_session_close_tcp NULL socket_comm_session.",
600 __func__);
601 return false;
602 }
603
604 pcep_log(LOG_DEBUG,
605 "%s: socket_comm_session_close_tcp close() socket fd [%d]",
606 __func__, socket_comm_session->socket_fd);
607
608 pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
609 ordered_list_remove_first_node_equals(socket_comm_handle_->read_list,
610 socket_comm_session);
611 ordered_list_remove_first_node_equals(socket_comm_handle_->write_list,
612 socket_comm_session);
613 // TODO should it be close() or shutdown()??
614 close(socket_comm_session->socket_fd);
615 socket_comm_session->socket_fd = -1;
616 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
617
618 return true;
619 }
620
621
622 bool socket_comm_session_close_tcp_after_write(
623 pcep_socket_comm_session *socket_comm_session)
624 {
625 if (socket_comm_session == NULL) {
626 pcep_log(
627 LOG_WARNING,
628 "%s: socket_comm_session_close_tcp_after_write NULL socket_comm_session.",
629 __func__);
630 return false;
631 }
632
633 pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
634 socket_comm_session->close_after_write = true;
635 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
636
637 return true;
638 }
639
640
641 bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session)
642 {
643 if (socket_comm_handle_ == NULL) {
644 pcep_log(LOG_WARNING,
645 "%s: Cannot teardown NULL socket_comm_handle",
646 __func__);
647 return false;
648 }
649
650 if (socket_comm_session == NULL) {
651 pcep_log(LOG_WARNING, "%s: Cannot teardown NULL session",
652 __func__);
653 return false;
654 }
655
656 if (comm_session_exists_locking(socket_comm_handle_,
657 socket_comm_session)
658 == false) {
659 pcep_log(LOG_WARNING,
660 "%s: Cannot teardown session that does not exist",
661 __func__);
662 return false;
663 }
664
665 if (socket_comm_session->socket_fd >= 0) {
666 shutdown(socket_comm_session->socket_fd, SHUT_RDWR);
667 close(socket_comm_session->socket_fd);
668 }
669
670 pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
671 queue_destroy(socket_comm_session->message_queue);
672 ordered_list_remove_first_node_equals(socket_comm_handle_->session_list,
673 socket_comm_session);
674 ordered_list_remove_first_node_equals(socket_comm_handle_->read_list,
675 socket_comm_session);
676 ordered_list_remove_first_node_equals(socket_comm_handle_->write_list,
677 socket_comm_session);
678 socket_comm_handle_->num_active_sessions--;
679 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
680
681 pcep_log(
682 LOG_INFO,
683 "%s: [%ld-%ld] socket_comm_session fd [%d] destroyed, [%d] sessions remaining",
684 __func__, time(NULL), pthread_self(),
685 socket_comm_session->socket_fd,
686 socket_comm_handle_->num_active_sessions);
687
688 pceplib_free(PCEPLIB_INFRA, socket_comm_session);
689
690 /* It would be nice to call destroy_socket_comm_loop() here if
691 * socket_comm_handle_->num_active_sessions == 0, but this function
692 * will usually be called from the message_sent_notifier callback,
693 * which gets called in the middle of the socket_comm_loop, and that
694 * is dangerous, so destroy_socket_comm_loop() must be called upon
695 * application exit. */
696
697 return true;
698 }
699
700
701 void socket_comm_session_send_message(
702 pcep_socket_comm_session *socket_comm_session,
703 const char *encoded_message, unsigned int msg_length,
704 bool free_after_send)
705 {
706 if (socket_comm_session == NULL) {
707 pcep_log(
708 LOG_WARNING,
709 "%s: socket_comm_session_send_message NULL socket_comm_session.",
710 __func__);
711 return;
712 }
713
714 pcep_socket_comm_queued_message *queued_message = pceplib_malloc(
715 PCEPLIB_MESSAGES, sizeof(pcep_socket_comm_queued_message));
716 queued_message->encoded_message = encoded_message;
717 queued_message->msg_length = msg_length;
718 queued_message->free_after_send = free_after_send;
719
720 pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex));
721
722 /* Do not proceed if the socket_comm_session has been deleted */
723 if (ordered_list_find(socket_comm_handle_->session_list,
724 socket_comm_session)
725 == NULL) {
726 /* Should never get here, only if the session was deleted and
727 * someone still tries to write on it */
728 pcep_log(
729 LOG_WARNING,
730 "%s: Cannot write a message on a deleted socket comm session, discarding message",
731 __func__);
732 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
733 pceplib_free(PCEPLIB_MESSAGES, queued_message);
734
735 return;
736 }
737
738 /* Do not proceed if the socket has been closed */
739 if (socket_comm_session->socket_fd < 0) {
740 /* Should never get here, only if the session was deleted and
741 * someone still tries to write on it */
742 pcep_log(
743 LOG_WARNING,
744 "%s: Cannot write a message on a closed socket, discarding message",
745 __func__);
746 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
747 pceplib_free(PCEPLIB_MESSAGES, queued_message);
748
749 return;
750 }
751
752 queue_enqueue(socket_comm_session->message_queue, queued_message);
753
754 /* Add it to the write list only if its not already there */
755 if (ordered_list_find(socket_comm_handle_->write_list,
756 socket_comm_session)
757 == NULL) {
758 ordered_list_add_node(socket_comm_handle_->write_list,
759 socket_comm_session);
760 }
761
762 if (socket_comm_handle_->socket_write_func != NULL) {
763 socket_comm_handle_->socket_write_func(
764 socket_comm_handle_->external_infra_data,
765 &socket_comm_session->external_socket_data,
766 socket_comm_session->socket_fd, socket_comm_handle_);
767 }
768 pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex));
769 }