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