1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * BFD data plane implementation (distributed BFD).
5 * Copyright (C) 2020 Network Device Education Foundation, Inc. ("NetDEF")
11 #include <netinet/in.h>
12 #include <netinet/tcp.h>
13 #include <sys/socket.h>
17 #include <sys/endian.h>
20 #endif /* __FreeBSD__ */
26 #include "lib/network.h"
27 #include "lib/printfrr.h"
28 #include "lib/stream.h"
29 #include "lib/frrevent.h"
32 #include "bfddp_packet.h"
34 #include "lib/openbsd-queue.h"
36 DEFINE_MTYPE_STATIC(BFDD
, BFDD_DPLANE_CTX
,
37 "Data plane client allocated memory");
39 /** Data plane client socket buffer size. */
40 #define BFD_DPLANE_CLIENT_BUF_SIZE 8192
42 struct bfd_dplane_ctx
{
43 /** Client file descriptor. */
45 /** Is this a connected or accepted? */
47 /** Is the socket still connecting? */
49 /** Client/server address. */
52 struct sockaddr_in sin
;
53 struct sockaddr_in6 sin6
;
54 struct sockaddr_un sun
;
56 /** Address length. */
58 /** Data plane current last used ID. */
61 /** Input buffer data. */
63 /** Output buffer data. */
64 struct stream
*outbuf
;
65 /** Input event data. */
66 struct event
*inbufev
;
67 /** Output event data. */
68 struct event
*outbufev
;
69 /** Connection event. */
70 struct event
*connectev
;
72 /** Amount of bytes read. */
74 /** Amount of bytes read peak. */
75 uint64_t in_bytes_peak
;
76 /** Amount of bytes written. */
78 /** Amount of bytes written peak. */
79 uint64_t out_bytes_peak
;
80 /** Amount of output buffer full events (`bfd_dplane_enqueue` failed).
84 /** Amount of messages read (full messages). */
86 /** Amount of messages enqueued (maybe written). */
89 TAILQ_ENTRY(bfd_dplane_ctx
) entry
;
93 * Callback type for `bfd_dplane_expect`. \see bfd_dplane_expect.
95 typedef void (*bfd_dplane_expect_cb
)(struct bfddp_message
*msg
, void *arg
);
97 static void bfd_dplane_client_connect(struct event
*t
);
98 static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx
*bdc
);
99 static void bfd_dplane_ctx_free(struct bfd_dplane_ctx
*bdc
);
100 static int _bfd_dplane_add_session(struct bfd_dplane_ctx
*bdc
,
101 struct bfd_session
*bs
);
104 * BFD data plane helper functions.
106 static const char *bfd_dplane_messagetype2str(enum bfddp_message_type bmt
)
110 return "ECHO_REQUEST";
114 return "DP_ADD_SESSION";
115 case DP_DELETE_SESSION
:
116 return "DP_DELETE_SESSION";
117 case BFD_STATE_CHANGE
:
118 return "BFD_STATE_CHANGE";
119 case DP_REQUEST_SESSION_COUNTERS
:
120 return "DP_REQUEST_SESSION_COUNTERS";
121 case BFD_SESSION_COUNTERS
:
122 return "BFD_SESSION_COUNTERS";
128 static void bfd_dplane_debug_message(const struct bfddp_message
*msg
)
130 enum bfddp_message_type bmt
;
131 char buf
[256], addrs
[256];
135 if (!bglobal
.debug_dplane
)
138 bmt
= ntohs(msg
->header
.type
);
139 zlog_debug("dplane-packet: [version=%d length=%d type=%s (%d)]",
140 msg
->header
.version
, ntohs(msg
->header
.length
),
141 bfd_dplane_messagetype2str(bmt
), bmt
);
146 zlog_debug(" [dp_time=%" PRIu64
" bfdd_time=%" PRIu64
"]",
147 be64toh(msg
->data
.echo
.dp_time
),
148 be64toh(msg
->data
.echo
.bfdd_time
));
152 case DP_DELETE_SESSION
:
153 flags
= ntohl(msg
->data
.session
.flags
);
154 if (flags
& SESSION_IPV6
)
155 snprintfrr(addrs
, sizeof(addrs
), "src=%pI6 dst=%pI6",
156 &msg
->data
.session
.src
,
157 &msg
->data
.session
.dst
);
159 snprintfrr(addrs
, sizeof(addrs
), "src=%pI4 dst=%pI4",
160 (struct in_addr
*)&msg
->data
.session
.src
,
161 (struct in_addr
*)&msg
->data
.session
.dst
);
164 if (flags
& SESSION_CBIT
)
165 strlcat(buf
, "cpi ", sizeof(buf
));
166 if (flags
& SESSION_ECHO
)
167 strlcat(buf
, "echo ", sizeof(buf
));
168 if (flags
& SESSION_IPV6
)
169 strlcat(buf
, "ipv6 ", sizeof(buf
));
170 if (flags
& SESSION_DEMAND
)
171 strlcat(buf
, "demand ", sizeof(buf
));
172 if (flags
& SESSION_PASSIVE
)
173 strlcat(buf
, "passive ", sizeof(buf
));
174 if (flags
& SESSION_MULTIHOP
)
175 strlcat(buf
, "multihop ", sizeof(buf
));
176 if (flags
& SESSION_SHUTDOWN
)
177 strlcat(buf
, "shutdown ", sizeof(buf
));
179 /* Remove the last space to make things prettier. */
180 rv
= (int)strlen(buf
);
185 " [flags=0x%08x{%s} %s ttl=%d detect_mult=%d "
186 "ifindex=%d ifname=%s]",
187 flags
, buf
, addrs
, msg
->data
.session
.ttl
,
188 msg
->data
.session
.detect_mult
,
189 ntohl(msg
->data
.session
.ifindex
),
190 msg
->data
.session
.ifname
);
193 case BFD_STATE_CHANGE
:
195 flags
= ntohl(msg
->data
.state
.remote_flags
);
196 if (flags
& RBIT_CPI
)
197 strlcat(buf
, "cbit ", sizeof(buf
));
198 if (flags
& RBIT_DEMAND
)
199 strlcat(buf
, "demand ", sizeof(buf
));
201 strlcat(buf
, "mp ", sizeof(buf
));
203 /* Remove the last space to make things prettier. */
204 rv
= (int)strlen(buf
);
209 " [lid=%u rid=%u flags=0x%02x{%s} state=%s "
210 "diagnostics=%s mult=%d tx=%u rx=%u erx=%u]",
211 ntohl(msg
->data
.state
.lid
), ntohl(msg
->data
.state
.rid
),
212 flags
, buf
, state_list
[msg
->data
.state
.state
].str
,
213 diag2str(msg
->data
.state
.diagnostics
),
214 msg
->data
.state
.detection_multiplier
,
215 ntohl(msg
->data
.state
.desired_tx
),
216 ntohl(msg
->data
.state
.required_rx
),
217 ntohl(msg
->data
.state
.required_echo_rx
));
220 case DP_REQUEST_SESSION_COUNTERS
:
221 zlog_debug(" [lid=%u]", ntohl(msg
->data
.counters_req
.lid
));
224 case BFD_SESSION_COUNTERS
:
227 "control{in %" PRIu64
" bytes (%" PRIu64
229 "out %" PRIu64
" bytes (%" PRIu64
231 "echo{in %" PRIu64
" bytes (%" PRIu64
233 "out %" PRIu64
" bytes (%" PRIu64
" packets)}]",
234 ntohl(msg
->data
.session_counters
.lid
),
235 be64toh(msg
->data
.session_counters
.control_input_bytes
),
236 be64toh(msg
->data
.session_counters
237 .control_input_packets
),
238 be64toh(msg
->data
.session_counters
239 .control_output_bytes
),
240 be64toh(msg
->data
.session_counters
241 .control_output_packets
),
242 be64toh(msg
->data
.session_counters
.echo_input_bytes
),
243 be64toh(msg
->data
.session_counters
.echo_input_packets
),
244 be64toh(msg
->data
.session_counters
.echo_output_bytes
),
245 be64toh(msg
->data
.session_counters
246 .echo_output_packets
));
252 * Gets the next unused non zero identification.
254 * \param bdc the data plane context.
256 * \returns next usable id.
258 static uint16_t bfd_dplane_next_id(struct bfd_dplane_ctx
*bdc
)
262 /* Don't use reserved id `0`. */
263 if (bdc
->last_id
== 0)
269 static ssize_t
bfd_dplane_flush(struct bfd_dplane_ctx
*bdc
)
274 while (STREAM_READABLE(bdc
->outbuf
)) {
275 /* Flush buffer contents to socket. */
276 rv
= stream_flush(bdc
->outbuf
, bdc
->sock
);
278 /* Interruption: try again. */
279 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
283 zlog_warn("%s: socket failed: %s", __func__
,
285 bfd_dplane_ctx_free(bdc
);
289 if (bglobal
.debug_dplane
)
290 zlog_info("%s: connection closed", __func__
);
292 bfd_dplane_ctx_free(bdc
);
296 /* Account total written. */
299 /* Account output bytes. */
300 bdc
->out_bytes
+= (uint64_t)rv
;
302 /* Forward pointer. */
303 stream_forward_getp(bdc
->outbuf
, (size_t)rv
);
306 /* Make more space for new data. */
307 stream_pulldown(bdc
->outbuf
);
309 /* Disable write ready events. */
310 EVENT_OFF(bdc
->outbufev
);
315 static void bfd_dplane_write(struct event
*t
)
317 struct bfd_dplane_ctx
*bdc
= EVENT_ARG(t
);
319 /* Handle connection stage. */
320 if (bdc
->connecting
&& bfd_dplane_client_connecting(bdc
))
323 bfd_dplane_flush(bdc
);
327 bfd_dplane_session_state_change(struct bfd_dplane_ctx
*bdc
,
328 const struct bfddp_state_change
*state
)
330 struct bfd_session
*bs
;
334 /* Look up session. */
335 bs
= bfd_id_lookup(ntohl(state
->lid
));
337 if (bglobal
.debug_dplane
)
338 zlog_debug("%s: failed to find session to update",
343 flags
= ntohl(state
->remote_flags
);
344 old_state
= bs
->ses_state
;
346 /* Update session state. */
347 bs
->ses_state
= state
->state
;
348 bs
->remote_diag
= state
->diagnostics
;
349 bs
->discrs
.remote_discr
= ntohl(state
->rid
);
350 bs
->remote_cbit
= !!(flags
& RBIT_CPI
);
351 bs
->remote_detect_mult
= state
->detection_multiplier
;
352 bs
->remote_timers
.desired_min_tx
= ntohl(state
->desired_tx
);
353 bs
->remote_timers
.required_min_rx
= ntohl(state
->required_rx
);
354 bs
->remote_timers
.required_min_echo
= ntohl(state
->required_echo_rx
);
356 /* Notify and update counters. */
357 control_notify(bs
, bs
->ses_state
);
359 /* No state change. */
360 if (old_state
== bs
->ses_state
)
363 switch (bs
->ses_state
) {
364 case PTM_BFD_ADM_DOWN
:
366 /* Both states mean down. */
367 if (old_state
== PTM_BFD_ADM_DOWN
|| old_state
== PTM_BFD_DOWN
)
370 monotime(&bs
->downtime
);
371 bs
->stats
.session_down
++;
374 monotime(&bs
->uptime
);
375 bs
->stats
.session_up
++;
382 zlog_warn("%s: unhandled new state %d", __func__
,
387 if (bglobal
.debug_peer_event
)
388 zlog_debug("state-change: [data plane: %s] %s -> %s",
389 bs_to_string(bs
), state_list
[old_state
].str
,
390 state_list
[bs
->ses_state
].str
);
394 * Enqueue message in output buffer.
396 * \param[in,out] bdc data plane client context.
397 * \param[in] buf the message to buffer.
398 * \param[in] buflen the amount of bytes to buffer.
400 * \returns `-1` on failure (buffer full) or `0` on success.
402 static int bfd_dplane_enqueue(struct bfd_dplane_ctx
*bdc
, const void *buf
,
407 /* Handle not connected yet client. */
408 if (bdc
->client
&& bdc
->sock
== -1)
411 /* Not enough space. */
412 if (buflen
> STREAM_WRITEABLE(bdc
->outbuf
)) {
417 /* Show debug message if active. */
418 bfd_dplane_debug_message((struct bfddp_message
*)buf
);
420 /* Buffer the message. */
421 stream_write(bdc
->outbuf
, buf
, buflen
);
423 /* Account message as sent. */
425 /* Register peak buffered bytes. */
426 rlen
= STREAM_READABLE(bdc
->outbuf
);
427 if (bdc
->out_bytes_peak
< rlen
)
428 bdc
->out_bytes_peak
= rlen
;
430 /* Schedule if it is not yet. */
431 if (bdc
->outbufev
== NULL
)
432 event_add_write(master
, bfd_dplane_write
, bdc
, bdc
->sock
,
438 static void bfd_dplane_echo_request_handle(struct bfd_dplane_ctx
*bdc
,
439 const struct bfddp_message
*bm
)
441 struct bfddp_message msg
= {};
442 uint16_t msglen
= sizeof(msg
.header
) + sizeof(msg
.data
.echo
);
445 gettimeofday(&tv
, NULL
);
447 /* Prepare header. */
448 msg
.header
.version
= BFD_DP_VERSION
;
449 msg
.header
.type
= htons(ECHO_REPLY
);
450 msg
.header
.length
= htons(msglen
);
452 /* Prepare payload. */
453 msg
.data
.echo
.dp_time
= bm
->data
.echo
.dp_time
;
454 msg
.data
.echo
.bfdd_time
=
455 htobe64((uint64_t)((tv
.tv_sec
* 1000000) + tv
.tv_usec
));
457 /* Enqueue for output. */
458 bfd_dplane_enqueue(bdc
, &msg
, msglen
);
461 static void bfd_dplane_handle_message(struct bfddp_message
*msg
, void *arg
)
463 enum bfddp_message_type bmt
;
464 struct bfd_dplane_ctx
*bdc
= arg
;
466 /* Call the appropriated handler. */
467 bmt
= ntohs(msg
->header
.type
);
470 bfd_dplane_echo_request_handle(bdc
, msg
);
472 case BFD_STATE_CHANGE
:
473 bfd_dplane_session_state_change(bdc
, &msg
->data
.state
);
476 /* NOTHING: we don't do anything with this information. */
479 case DP_DELETE_SESSION
:
480 case DP_REQUEST_SESSION_COUNTERS
:
481 /* NOTHING: we are not supposed to receive this. */
483 case BFD_SESSION_COUNTERS
:
485 * NOTHING: caller of DP_REQUEST_SESSION_COUNTERS should
486 * handle this with `bfd_dplane_expect`.
491 zlog_debug("%s: unhandled message type %d", __func__
, bmt
);
497 * Reads the socket immediately to receive data plane answer to query.
499 * \param bdc the data plane context.
500 * \param id the message ID waiting response.
501 * \param cb the callback to call when ready.
502 * \param arg the callback argument.
505 * `-2` on unavailability (try again), `-1` on failure or `0` on success.
507 static int bfd_dplane_expect(struct bfd_dplane_ctx
*bdc
, uint16_t id
,
508 bfd_dplane_expect_cb cb
, void *arg
)
510 struct bfddp_message_header
*bh
;
511 size_t rlen
= 0, reads
= 0;
515 * Don't attempt to read if buffer is full, otherwise we'll get a
516 * bogus 'connection closed' signal (rv == 0).
518 if (bdc
->inbuf
->endp
== bdc
->inbuf
->size
)
522 /* Attempt to read message from client. */
523 rv
= stream_read_try(bdc
->inbuf
, bdc
->sock
,
524 STREAM_WRITEABLE(bdc
->inbuf
));
526 if (bglobal
.debug_dplane
)
527 zlog_info("%s: socket closed", __func__
);
529 bfd_dplane_ctx_free(bdc
);
533 zlog_warn("%s: socket failed: %s", __func__
, strerror(errno
));
534 bfd_dplane_ctx_free(bdc
);
538 /* We got interrupted, reschedule read. */
542 /* Account read bytes. */
543 bdc
->in_bytes
+= (uint64_t)rv
;
544 /* Register peak buffered bytes. */
545 rlen
= STREAM_READABLE(bdc
->inbuf
);
546 if (bdc
->in_bytes_peak
< rlen
)
547 bdc
->in_bytes_peak
= rlen
;
551 bh
= (struct bfddp_message_header
*)stream_pnt(bdc
->inbuf
);
552 /* Not enough data read. */
553 if (ntohs(bh
->length
) > rlen
)
556 /* Account full message read. */
559 /* Account this message as whole read for buffer reorganize. */
562 /* Check for bad version. */
563 if (bh
->version
!= BFD_DP_VERSION
) {
564 zlog_err("%s: bad data plane client version: %d",
565 __func__
, bh
->version
);
569 /* Show debug message if active. */
570 bfd_dplane_debug_message((struct bfddp_message
*)bh
);
573 * Handle incoming message with callback if the ID matches,
574 * otherwise fallback to default handler.
576 if (id
&& ntohs(bh
->id
) == id
)
577 cb((struct bfddp_message
*)bh
, arg
);
579 bfd_dplane_handle_message((struct bfddp_message
*)bh
,
582 /* Advance current read pointer. */
583 stream_forward_getp(bdc
->inbuf
, ntohs(bh
->length
));
585 /* Reduce the buffer available bytes. */
586 rlen
-= ntohs(bh
->length
);
588 /* Reorganize buffer to handle more bytes read. */
590 stream_pulldown(bdc
->inbuf
);
594 /* We found the message, return to caller. */
595 if (id
&& ntohs(bh
->id
) == id
)
602 static void bfd_dplane_read(struct event
*t
)
604 struct bfd_dplane_ctx
*bdc
= EVENT_ARG(t
);
607 rv
= bfd_dplane_expect(bdc
, 0, bfd_dplane_handle_message
, NULL
);
611 stream_pulldown(bdc
->inbuf
);
612 event_add_read(master
, bfd_dplane_read
, bdc
, bdc
->sock
, &bdc
->inbufev
);
615 static void _bfd_session_register_dplane(struct hash_bucket
*hb
, void *arg
)
617 struct bfd_session
*bs
= hb
->data
;
618 struct bfd_dplane_ctx
*bdc
= arg
;
623 /* Disable software session. */
624 bfd_session_disable(bs
);
626 /* Move session to data plane. */
627 _bfd_dplane_add_session(bdc
, bs
);
630 static struct bfd_dplane_ctx
*bfd_dplane_ctx_new(int sock
)
632 struct bfd_dplane_ctx
*bdc
;
634 bdc
= XCALLOC(MTYPE_BFDD_DPLANE_CTX
, sizeof(*bdc
));
637 bdc
->inbuf
= stream_new(BFD_DPLANE_CLIENT_BUF_SIZE
);
638 bdc
->outbuf
= stream_new(BFD_DPLANE_CLIENT_BUF_SIZE
);
640 /* If not socket ready, skip read and session registration. */
644 event_add_read(master
, bfd_dplane_read
, bdc
, sock
, &bdc
->inbufev
);
646 /* Register all unattached sessions. */
647 bfd_key_iterate(_bfd_session_register_dplane
, bdc
);
652 static void _bfd_session_unregister_dplane(struct hash_bucket
*hb
, void *arg
)
654 struct bfd_session
*bs
= hb
->data
;
655 struct bfd_dplane_ctx
*bdc
= arg
;
662 /* Fallback to software. */
663 bfd_session_enable(bs
);
666 static void bfd_dplane_ctx_free(struct bfd_dplane_ctx
*bdc
)
668 if (bglobal
.debug_dplane
)
669 zlog_debug("%s: terminating data plane client %d", __func__
,
672 /* Client mode has special treatment. */
674 /* Disable connection event if any. */
675 EVENT_OFF(bdc
->connectev
);
677 /* Normal treatment on shutdown. */
678 if (bglobal
.bg_shutdown
)
681 /* Attempt reconnection. */
682 socket_close(&bdc
->sock
);
683 EVENT_OFF(bdc
->inbufev
);
684 EVENT_OFF(bdc
->outbufev
);
685 event_add_timer(master
, bfd_dplane_client_connect
, bdc
, 3,
691 /* Remove from the list of attached data planes. */
692 TAILQ_REMOVE(&bglobal
.bg_dplaneq
, bdc
, entry
);
694 /* Detach all associated sessions. */
695 if (bglobal
.bg_shutdown
== false)
696 bfd_key_iterate(_bfd_session_unregister_dplane
, bdc
);
698 /* Free resources. */
699 socket_close(&bdc
->sock
);
700 stream_free(bdc
->inbuf
);
701 stream_free(bdc
->outbuf
);
702 EVENT_OFF(bdc
->inbufev
);
703 EVENT_OFF(bdc
->outbufev
);
704 XFREE(MTYPE_BFDD_DPLANE_CTX
, bdc
);
707 static void _bfd_dplane_session_fill(const struct bfd_session
*bs
,
708 struct bfddp_message
*msg
)
710 uint16_t msglen
= sizeof(msg
->header
) + sizeof(msg
->data
.session
);
712 /* Message header. */
713 msg
->header
.version
= BFD_DP_VERSION
;
714 msg
->header
.length
= ntohs(msglen
);
715 msg
->header
.type
= ntohs(DP_ADD_SESSION
);
717 /* Message payload. */
718 msg
->data
.session
.dst
= bs
->key
.peer
;
719 msg
->data
.session
.src
= bs
->key
.local
;
720 msg
->data
.session
.detect_mult
= bs
->detect_mult
;
723 msg
->data
.session
.ifindex
= htonl(bs
->ifp
->ifindex
);
724 strlcpy(msg
->data
.session
.ifname
, bs
->ifp
->name
,
725 sizeof(msg
->data
.session
.ifname
));
727 if (bs
->flags
& BFD_SESS_FLAG_MH
) {
728 msg
->data
.session
.flags
|= SESSION_MULTIHOP
;
729 msg
->data
.session
.ttl
= bs
->mh_ttl
;
731 msg
->data
.session
.ttl
= BFD_TTL_VAL
;
733 if (bs
->flags
& BFD_SESS_FLAG_IPV6
)
734 msg
->data
.session
.flags
|= SESSION_IPV6
;
735 if (bs
->flags
& BFD_SESS_FLAG_ECHO
)
736 msg
->data
.session
.flags
|= SESSION_ECHO
;
737 if (bs
->flags
& BFD_SESS_FLAG_CBIT
)
738 msg
->data
.session
.flags
|= SESSION_CBIT
;
739 if (bs
->flags
& BFD_SESS_FLAG_PASSIVE
)
740 msg
->data
.session
.flags
|= SESSION_PASSIVE
;
741 if (bs
->flags
& BFD_SESS_FLAG_SHUTDOWN
)
742 msg
->data
.session
.flags
|= SESSION_SHUTDOWN
;
744 msg
->data
.session
.flags
= htonl(msg
->data
.session
.flags
);
745 msg
->data
.session
.lid
= htonl(bs
->discrs
.my_discr
);
746 msg
->data
.session
.min_tx
= htonl(bs
->timers
.desired_min_tx
);
747 msg
->data
.session
.min_rx
= htonl(bs
->timers
.required_min_rx
);
748 msg
->data
.session
.min_echo_tx
= htonl(bs
->timers
.desired_min_echo_tx
);
749 msg
->data
.session
.min_echo_rx
= htonl(bs
->timers
.required_min_echo_rx
);
752 static int _bfd_dplane_add_session(struct bfd_dplane_ctx
*bdc
,
753 struct bfd_session
*bs
)
757 /* Associate session. */
760 /* Reset previous state. */
763 bs
->ses_state
= PTM_BFD_DOWN
;
765 /* Enqueue message to data plane client. */
766 rv
= bfd_dplane_update_session(bs
);
773 static void _bfd_dplane_update_session_counters(struct bfddp_message
*msg
,
776 struct bfd_session
*bs
= arg
;
778 bs
->stats
.rx_ctrl_pkt
=
779 be64toh(msg
->data
.session_counters
.control_input_packets
);
780 bs
->stats
.tx_ctrl_pkt
=
781 be64toh(msg
->data
.session_counters
.control_output_packets
);
782 bs
->stats
.rx_echo_pkt
=
783 be64toh(msg
->data
.session_counters
.echo_input_packets
);
784 bs
->stats
.tx_echo_pkt
=
785 be64toh(msg
->data
.session_counters
.echo_output_bytes
);
789 * Send message to data plane requesting the session counters.
791 * \param bs the BFD session.
793 * \returns `0` on failure or the request id.
795 static uint16_t bfd_dplane_request_counters(const struct bfd_session
*bs
)
797 struct bfddp_message msg
= {};
798 size_t msglen
= sizeof(msg
.header
) + sizeof(msg
.data
.counters_req
);
800 /* Fill header information. */
801 msg
.header
.version
= BFD_DP_VERSION
;
802 msg
.header
.length
= htons(msglen
);
803 msg
.header
.type
= htons(DP_REQUEST_SESSION_COUNTERS
);
804 msg
.header
.id
= htons(bfd_dplane_next_id(bs
->bdc
));
806 /* Session to get counters. */
807 msg
.data
.counters_req
.lid
= htonl(bs
->discrs
.my_discr
);
809 /* If enqueue failed, let caller know. */
810 if (bfd_dplane_enqueue(bs
->bdc
, &msg
, msglen
) == -1)
814 bfd_dplane_flush(bs
->bdc
);
816 return ntohs(msg
.header
.id
);
820 * Data plane listening socket.
822 static void bfd_dplane_accept(struct event
*t
)
824 struct bfd_global
*bg
= EVENT_ARG(t
);
825 struct bfd_dplane_ctx
*bdc
;
828 /* Accept new connection. */
829 sock
= accept(bg
->bg_dplane_sock
, NULL
, 0);
831 zlog_warn("%s: accept failed: %s", __func__
, strerror(errno
));
832 goto reschedule_and_return
;
835 /* Create and handle new connection. */
836 bdc
= bfd_dplane_ctx_new(sock
);
837 TAILQ_INSERT_TAIL(&bglobal
.bg_dplaneq
, bdc
, entry
);
839 if (bglobal
.debug_dplane
)
840 zlog_debug("%s: new data plane client connected", __func__
);
842 reschedule_and_return
:
843 event_add_read(master
, bfd_dplane_accept
, bg
, bg
->bg_dplane_sock
,
844 &bglobal
.bg_dplane_sockev
);
848 * Data plane connecting socket.
850 static void _bfd_dplane_client_bootstrap(struct bfd_dplane_ctx
*bdc
)
852 bdc
->connecting
= false;
854 /* Clean up buffers. */
855 stream_reset(bdc
->inbuf
);
856 stream_reset(bdc
->outbuf
);
858 /* Ask for read notifications. */
859 event_add_read(master
, bfd_dplane_read
, bdc
, bdc
->sock
, &bdc
->inbufev
);
861 /* Remove all sessions then register again to send them all. */
862 bfd_key_iterate(_bfd_session_unregister_dplane
, bdc
);
863 bfd_key_iterate(_bfd_session_register_dplane
, bdc
);
866 static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx
*bdc
)
869 socklen_t rvlen
= sizeof(rv
);
871 /* Make sure `errno` is reset, then test `getsockopt` success. */
873 if (getsockopt(bdc
->sock
, SOL_SOCKET
, SO_ERROR
, &rv
, &rvlen
) == -1)
876 /* Connection successful. */
878 if (bglobal
.debug_dplane
)
879 zlog_debug("%s: connected to server: %d", __func__
,
882 _bfd_dplane_client_bootstrap(bdc
);
891 /* non error, wait more. */
895 zlog_warn("%s: connection failed: %s", __func__
,
897 bfd_dplane_ctx_free(bdc
);
902 static void bfd_dplane_client_connect(struct event
*t
)
904 struct bfd_dplane_ctx
*bdc
= EVENT_ARG(t
);
906 socklen_t rvlen
= sizeof(rv
);
908 /* Allocate new socket. */
909 sock
= socket(bdc
->addr
.sa
.sa_family
, SOCK_STREAM
, 0);
911 zlog_warn("%s: failed to initialize socket: %s", __func__
,
913 goto reschedule_connect
;
916 /* Set non blocking socket. */
917 set_nonblocking(sock
);
919 /* Set 'no delay' (disables nagle algorithm) for IPv4/IPv6. */
921 if (bdc
->addr
.sa
.sa_family
!= AF_UNIX
922 && setsockopt(sock
, IPPROTO_TCP
, TCP_NODELAY
, &rv
, rvlen
) == -1)
923 zlog_warn("%s: TCP_NODELAY: %s", __func__
, strerror(errno
));
925 /* Attempt to connect. */
926 rv
= connect(sock
, &bdc
->addr
.sa
, bdc
->addrlen
);
927 if (rv
== -1 && (errno
!= EINPROGRESS
&& errno
!= EAGAIN
)) {
928 zlog_warn("%s: data plane connection failed: %s", __func__
,
930 goto reschedule_connect
;
935 if (bglobal
.debug_dplane
)
936 zlog_debug("%s: server connection in progress: %d",
939 /* If we are not connected yet, ask for write notifications. */
940 bdc
->connecting
= true;
941 event_add_write(master
, bfd_dplane_write
, bdc
, bdc
->sock
,
944 if (bglobal
.debug_dplane
)
945 zlog_debug("%s: server connection: %d", __func__
, sock
);
947 /* Otherwise just start accepting data. */
948 _bfd_dplane_client_bootstrap(bdc
);
952 EVENT_OFF(bdc
->inbufev
);
953 EVENT_OFF(bdc
->outbufev
);
955 event_add_timer(master
, bfd_dplane_client_connect
, bdc
, 3,
959 static void bfd_dplane_client_init(const struct sockaddr
*sa
, socklen_t salen
)
961 struct bfd_dplane_ctx
*bdc
;
963 /* Allocate context and copy address for reconnection. */
964 bdc
= bfd_dplane_ctx_new(-1);
965 if (salen
<= sizeof(bdc
->addr
)) {
966 memcpy(&bdc
->addr
, sa
, salen
);
967 bdc
->addrlen
= sizeof(bdc
->addr
);
969 memcpy(&bdc
->addr
, sa
, sizeof(bdc
->addr
));
970 bdc
->addrlen
= sizeof(bdc
->addr
);
971 zlog_warn("%s: server address truncated (from %d to %d)",
972 __func__
, salen
, bdc
->addrlen
);
977 event_add_timer(master
, bfd_dplane_client_connect
, bdc
, 0,
980 /* Insert into data plane lists. */
981 TAILQ_INSERT_TAIL(&bglobal
.bg_dplaneq
, bdc
, entry
);
985 * Termination phase of the distributed BFD infrastructure: free all allocated
988 static int bfd_dplane_finish_late(void)
990 struct bfd_dplane_ctx
*bdc
;
992 if (bglobal
.debug_dplane
)
993 zlog_debug("%s: terminating distributed BFD", __func__
);
995 /* Free all data plane client contexts. */
996 while ((bdc
= TAILQ_FIRST(&bglobal
.bg_dplaneq
)) != NULL
)
997 bfd_dplane_ctx_free(bdc
);
999 /* Cancel accept thread and close socket. */
1000 EVENT_OFF(bglobal
.bg_dplane_sockev
);
1001 close(bglobal
.bg_dplane_sock
);
1007 * Data plane exported functions.
1009 void bfd_dplane_init(const struct sockaddr
*sa
, socklen_t salen
, bool client
)
1013 zlog_info("initializing distributed BFD");
1015 /* Initialize queue header. */
1016 TAILQ_INIT(&bglobal
.bg_dplaneq
);
1018 /* Initialize listening socket. */
1019 bglobal
.bg_dplane_sock
= -1;
1021 /* Observe shutdown events. */
1022 hook_register(frr_fini
, bfd_dplane_finish_late
);
1024 /* Handle client mode. */
1026 bfd_dplane_client_init(sa
, salen
);
1031 * Data plane socket creation:
1032 * - Set REUSEADDR option for taking over previously open socket.
1033 * - Bind to address requested (maybe IPv4, IPv6, UNIX etc...).
1034 * - Listen on that address for new connections.
1035 * - Ask to be waken up when a new connection comes.
1037 sock
= socket(sa
->sa_family
, SOCK_STREAM
, 0);
1039 zlog_warn("%s: failed to initialize socket: %s", __func__
,
1044 if (sockopt_reuseaddr(sock
) == -1) {
1045 zlog_warn("%s: failed to set reuseaddr: %s", __func__
,
1051 /* Handle UNIX socket: delete previous socket if any. */
1052 if (sa
->sa_family
== AF_UNIX
)
1053 unlink(((struct sockaddr_un
*)sa
)->sun_path
);
1055 if (bind(sock
, sa
, salen
) == -1) {
1056 zlog_warn("%s: failed to bind socket: %s", __func__
,
1062 if (listen(sock
, SOMAXCONN
) == -1) {
1063 zlog_warn("%s: failed to put socket on listen: %s", __func__
,
1069 bglobal
.bg_dplane_sock
= sock
;
1070 event_add_read(master
, bfd_dplane_accept
, &bglobal
, sock
,
1071 &bglobal
.bg_dplane_sockev
);
1074 int bfd_dplane_add_session(struct bfd_session
*bs
)
1076 struct bfd_dplane_ctx
*bdc
;
1078 /* Select a data plane client to install session. */
1079 TAILQ_FOREACH (bdc
, &bglobal
.bg_dplaneq
, entry
) {
1080 if (_bfd_dplane_add_session(bdc
, bs
) == 0)
1087 int bfd_dplane_update_session(const struct bfd_session
*bs
)
1089 struct bfddp_message msg
= {};
1091 if (bs
->bdc
== NULL
)
1094 _bfd_dplane_session_fill(bs
, &msg
);
1096 /* Enqueue message to data plane client. */
1097 return bfd_dplane_enqueue(bs
->bdc
, &msg
, ntohs(msg
.header
.length
));
1100 int bfd_dplane_delete_session(struct bfd_session
*bs
)
1102 struct bfddp_message msg
= {};
1105 /* Not using data plane, just return success. */
1106 if (bs
->bdc
== NULL
)
1109 /* Fill most of the common fields. */
1110 _bfd_dplane_session_fill(bs
, &msg
);
1112 /* Change the message type. */
1113 msg
.header
.type
= ntohs(DP_DELETE_SESSION
);
1115 /* Enqueue message to data plane client. */
1116 rv
= bfd_dplane_enqueue(bs
->bdc
, &msg
, ntohs(msg
.header
.length
));
1118 /* Remove association. */
1127 void bfd_dplane_show_counters(struct vty
*vty
)
1129 struct bfd_dplane_ctx
*bdc
;
1131 #define SHOW_COUNTER(label, counter, formatter) \
1132 vty_out(vty, "%28s: %" formatter "\n", (label), (counter))
1134 vty_out(vty
, "%28s\n%28s\n", "Data plane", "==========");
1135 TAILQ_FOREACH (bdc
, &bglobal
.bg_dplaneq
, entry
) {
1136 SHOW_COUNTER("File descriptor", bdc
->sock
, "d");
1137 SHOW_COUNTER("Input bytes", bdc
->in_bytes
, PRIu64
);
1138 SHOW_COUNTER("Input bytes peak", bdc
->in_bytes_peak
, PRIu64
);
1139 SHOW_COUNTER("Input messages", bdc
->in_msgs
, PRIu64
);
1140 SHOW_COUNTER("Input current usage", STREAM_READABLE(bdc
->inbuf
),
1142 SHOW_COUNTER("Output bytes", bdc
->out_bytes
, PRIu64
);
1143 SHOW_COUNTER("Output bytes peak", bdc
->out_bytes_peak
, PRIu64
);
1144 SHOW_COUNTER("Output messages", bdc
->out_msgs
, PRIu64
);
1145 SHOW_COUNTER("Output full events", bdc
->out_fullev
, PRIu64
);
1146 SHOW_COUNTER("Output current usage",
1147 STREAM_READABLE(bdc
->inbuf
), "zu");
1153 int bfd_dplane_update_session_counters(struct bfd_session
*bs
)
1158 /* If session is not using data plane, then just return success. */
1159 if (bs
->bdc
== NULL
)
1162 /* Make the request. */
1163 id
= bfd_dplane_request_counters(bs
);
1165 zlog_debug("%s: counters request failed", __func__
);
1169 /* Handle interruptions. */
1171 rv
= bfd_dplane_expect(bs
->bdc
, id
,
1172 _bfd_dplane_update_session_counters
, bs
);