]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/dplane.c
Merge pull request #7782 from kuldeepkash/multicast_pim_sm_topo2
[mirror_frr.git] / bfdd / dplane.c
CommitLineData
230aefe2
RZ
1/*
2 * BFD data plane implementation (distributed BFD).
3 *
4 * Copyright (C) 2020 Network Device Education Foundation, Inc. ("NetDEF")
5 * Rafael Zalamena
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <zebra.h>
23
24#include <netinet/in.h>
6655b43d 25#include <netinet/tcp.h>
230aefe2
RZ
26#include <sys/socket.h>
27#include <sys/un.h>
28
29#ifdef __FreeBSD__
30#include <sys/endian.h>
31#else
32#include <endian.h>
33#endif /* __FreeBSD__ */
34
35#include <errno.h>
36#include <time.h>
37
38#include "lib/hook.h"
6655b43d 39#include "lib/network.h"
230aefe2
RZ
40#include "lib/printfrr.h"
41#include "lib/stream.h"
42#include "lib/thread.h"
43
44#include "bfd.h"
45#include "bfddp_packet.h"
46
47#include "lib/openbsd-queue.h"
48
49DEFINE_MTYPE_STATIC(BFDD, BFDD_DPLANE_CTX, "Data plane client allocated memory")
50
51/** Data plane client socket buffer size. */
52#define BFD_DPLANE_CLIENT_BUF_SIZE 8192
53
54struct bfd_dplane_ctx {
55 /** Client file descriptor. */
56 int sock;
6655b43d
RZ
57 /** Is this a connected or accepted? */
58 bool client;
59 /** Is the socket still connecting? */
60 bool connecting;
61 /** Client/server address. */
62 union {
63 struct sockaddr sa;
64 struct sockaddr_in sin;
65 struct sockaddr_in6 sin6;
66 struct sockaddr_un sun;
67 } addr;
68 /** Address length. */
69 socklen_t addrlen;
230aefe2
RZ
70 /** Data plane current last used ID. */
71 uint16_t last_id;
72
73 /** Input buffer data. */
74 struct stream *inbuf;
75 /** Output buffer data. */
76 struct stream *outbuf;
77 /** Input event data. */
78 struct thread *inbufev;
79 /** Output event data. */
80 struct thread *outbufev;
6655b43d
RZ
81 /** Connection event. */
82 struct thread *connectev;
230aefe2
RZ
83
84 /** Amount of bytes read. */
85 uint64_t in_bytes;
86 /** Amount of bytes read peak. */
87 uint64_t in_bytes_peak;
88 /** Amount of bytes written. */
89 uint64_t out_bytes;
90 /** Amount of bytes written peak. */
91 uint64_t out_bytes_peak;
92 /** Amount of output buffer full events (`bfd_dplane_enqueue` failed).
93 */
94 uint64_t out_fullev;
95
96 /** Amount of messages read (full messages). */
97 uint64_t in_msgs;
98 /** Amount of messages enqueued (maybe written). */
99 uint64_t out_msgs;
100
101 TAILQ_ENTRY(bfd_dplane_ctx) entry;
102};
103
104/**
105 * Callback type for `bfd_dplane_expect`. \see bfd_dplane_expect.
106 */
107typedef void (*bfd_dplane_expect_cb)(struct bfddp_message *msg, void *arg);
108
6655b43d
RZ
109static int bfd_dplane_client_connect(struct thread *t);
110static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx *bdc);
230aefe2 111static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc);
efd04d60
RZ
112static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc,
113 struct bfd_session *bs);
230aefe2
RZ
114
115/*
116 * BFD data plane helper functions.
117 */
118static const char *bfd_dplane_messagetype2str(enum bfddp_message_type bmt)
119{
120 switch (bmt) {
121 case ECHO_REQUEST:
122 return "ECHO_REQUEST";
123 case ECHO_REPLY:
124 return "ECHO_REPLY";
125 case DP_ADD_SESSION:
126 return "DP_ADD_SESSION";
127 case DP_DELETE_SESSION:
128 return "DP_DELETE_SESSION";
129 case BFD_STATE_CHANGE:
130 return "BFD_STATE_CHANGE";
131 case DP_REQUEST_SESSION_COUNTERS:
132 return "DP_REQUEST_SESSION_COUNTERS";
133 case BFD_SESSION_COUNTERS:
134 return "BFD_SESSION_COUNTERS";
135 default:
136 return "UNKNOWN";
137 }
138}
139
140static void bfd_dplane_debug_message(const struct bfddp_message *msg)
141{
142 enum bfddp_message_type bmt;
143 char buf[256], addrs[256];
144 uint32_t flags;
145 int rv;
146
147 if (!bglobal.debug_dplane)
148 return;
149
150 bmt = ntohs(msg->header.type);
151 zlog_debug("dplane-packet: [version=%d length=%d type=%s (%d)]",
152 msg->header.version, ntohs(msg->header.length),
153 bfd_dplane_messagetype2str(bmt), bmt);
154
155 switch (bmt) {
156 case ECHO_REPLY:
157 case ECHO_REQUEST:
158 zlog_debug(" [dp_time=%" PRIu64 " bfdd_time=%" PRIu64 "]",
159 (uint64_t)be64toh(msg->data.echo.dp_time),
160 (uint64_t)be64toh(msg->data.echo.bfdd_time));
161 break;
162
163 case DP_ADD_SESSION:
164 case DP_DELETE_SESSION:
165 flags = ntohl(msg->data.session.flags);
166 if (flags & SESSION_IPV6)
167 snprintfrr(addrs, sizeof(addrs), "src=%pI6 dst=%pI6",
168 &msg->data.session.src,
169 &msg->data.session.dst);
170 else
171 snprintfrr(addrs, sizeof(addrs), "src=%pI4 dst=%pI4",
172 &msg->data.session.src,
173 &msg->data.session.dst);
174
175 buf[0] = 0;
176 if (flags & SESSION_CBIT)
177 strlcat(buf, "cpi ", sizeof(buf));
178 if (flags & SESSION_ECHO)
179 strlcat(buf, "echo ", sizeof(buf));
180 if (flags & SESSION_IPV6)
181 strlcat(buf, "ipv6 ", sizeof(buf));
182 if (flags & SESSION_DEMAND)
183 strlcat(buf, "demand ", sizeof(buf));
184 if (flags & SESSION_PASSIVE)
185 strlcat(buf, "passive ", sizeof(buf));
186 if (flags & SESSION_MULTIHOP)
187 strlcat(buf, "multihop ", sizeof(buf));
188 if (flags & SESSION_SHUTDOWN)
189 strlcat(buf, "shutdown ", sizeof(buf));
190
191 /* Remove the last space to make things prettier. */
192 rv = (int)strlen(buf);
193 if (rv > 0)
194 buf[rv - 1] = 0;
195
196 zlog_debug(
197 " [flags=0x%08x{%s} %s ttl=%d detect_mult=%d "
198 "ifindex=%d ifname=%s]",
199 flags, buf, addrs, msg->data.session.ttl,
200 msg->data.session.detect_mult,
201 ntohl(msg->data.session.ifindex),
202 msg->data.session.ifname);
203 break;
204
205 case BFD_STATE_CHANGE:
206 buf[0] = 0;
207 flags = ntohl(msg->data.state.remote_flags);
208 if (flags & RBIT_CPI)
209 strlcat(buf, "cbit ", sizeof(buf));
210 if (flags & RBIT_DEMAND)
211 strlcat(buf, "demand ", sizeof(buf));
212 if (flags & RBIT_MP)
213 strlcat(buf, "mp ", sizeof(buf));
214
215 /* Remove the last space to make things prettier. */
216 rv = (int)strlen(buf);
217 if (rv > 0)
218 buf[rv - 1] = 0;
219
220 zlog_debug(
221 " [lid=%u rid=%u flags=0x%02x{%s} state=%s "
222 "diagnostics=%s mult=%d tx=%u rx=%u erx=%u]",
223 ntohl(msg->data.state.lid), ntohl(msg->data.state.rid),
224 flags, buf, state_list[msg->data.state.state].str,
225 diag2str(msg->data.state.diagnostics),
226 msg->data.state.detection_multiplier,
227 ntohl(msg->data.state.desired_tx),
228 ntohl(msg->data.state.required_rx),
229 ntohl(msg->data.state.required_echo_rx));
230 break;
231
232 case DP_REQUEST_SESSION_COUNTERS:
233 zlog_debug(" [lid=%u]", ntohl(msg->data.counters_req.lid));
234 break;
235
236 case BFD_SESSION_COUNTERS:
237 zlog_debug(
238 " [lid=%u "
239 "control{in %" PRIu64 " bytes (%" PRIu64
240 " packets), "
241 "out %" PRIu64 " bytes (%" PRIu64
242 " packets)} "
243 "echo{in %" PRIu64 " bytes (%" PRIu64
244 " packets), "
245 "out %" PRIu64 " bytes (%" PRIu64 " packets)}]",
246 ntohl(msg->data.session_counters.lid),
247 (uint64_t)be64toh(
248 msg->data.session_counters.control_input_bytes),
249 (uint64_t)be64toh(msg->data.session_counters
250 .control_input_packets),
251 (uint64_t)be64toh(msg->data.session_counters
252 .control_output_bytes),
253 (uint64_t)be64toh(msg->data.session_counters
254 .control_output_packets),
255 (uint64_t)be64toh(msg->data.session_counters.echo_input_bytes),
256 (uint64_t)be64toh(
257 msg->data.session_counters.echo_input_packets),
258 (uint64_t)be64toh(
259 msg->data.session_counters.echo_output_bytes),
260 (uint64_t)be64toh(msg->data.session_counters
261 .echo_output_packets));
262 break;
263 }
264}
265
400632a9
RZ
266/**
267 * Gets the next unused non zero identification.
268 *
269 * \param bdc the data plane context.
270 *
271 * \returns next usable id.
272 */
273static uint16_t bfd_dplane_next_id(struct bfd_dplane_ctx *bdc)
274{
275 bdc->last_id++;
276
277 /* Don't use reserved id `0`. */
278 if (bdc->last_id == 0)
279 bdc->last_id = 1;
280
281 return bdc->last_id;
282}
283
230aefe2
RZ
284static ssize_t bfd_dplane_flush(struct bfd_dplane_ctx *bdc)
285{
286 ssize_t total = 0;
287 int rv;
288
289 while (STREAM_READABLE(bdc->outbuf)) {
290 /* Flush buffer contents to socket. */
291 rv = stream_flush(bdc->outbuf, bdc->sock);
292 if (rv == -1) {
293 /* Interruption: try again. */
294 if (errno == EAGAIN || errno == EWOULDBLOCK
295 || errno == EINTR)
296 continue;
297
298 zlog_warn("%s: socket failed: %s", __func__,
299 strerror(errno));
300 bfd_dplane_ctx_free(bdc);
301 return 0;
302 }
303 if (rv == 0) {
304 if (bglobal.debug_dplane)
305 zlog_info("%s: connection closed", __func__);
306
307 bfd_dplane_ctx_free(bdc);
308 return 0;
309 }
310
311 /* Account total written. */
312 total += rv;
313
314 /* Account output bytes. */
315 bdc->out_bytes += (uint64_t)rv;
316
317 /* Forward pointer. */
318 stream_forward_getp(bdc->outbuf, (size_t)rv);
319 }
320
321 /* Make more space for new data. */
322 stream_pulldown(bdc->outbuf);
323
324 /* Disable write ready events. */
325 THREAD_OFF(bdc->outbufev);
326
327 return total;
328}
329
330static int bfd_dplane_write(struct thread *t)
331{
6655b43d
RZ
332 struct bfd_dplane_ctx *bdc = THREAD_ARG(t);
333
334 /* Handle connection stage. */
335 if (bdc->connecting && bfd_dplane_client_connecting(bdc))
336 return 0;
337
338 bfd_dplane_flush(bdc);
339
230aefe2
RZ
340 return 0;
341}
342
343static void
344bfd_dplane_session_state_change(struct bfd_dplane_ctx *bdc,
345 const struct bfddp_state_change *state)
346{
347 struct bfd_session *bs;
348 uint32_t flags;
349 int old_state;
350
351 /* Look up session. */
352 bs = bfd_id_lookup(ntohl(state->lid));
353 if (bs == NULL) {
354 if (bglobal.debug_dplane)
355 zlog_debug("%s: failed to find session to update",
356 __func__);
357 return;
358 }
359
360 flags = ntohl(state->remote_flags);
361 old_state = bs->ses_state;
362
363 /* Update session state. */
364 bs->ses_state = state->state;
365 bs->remote_diag = state->diagnostics;
366 bs->discrs.remote_discr = ntohl(state->rid);
367 bs->remote_cbit = !!(flags & RBIT_CPI);
368 bs->remote_detect_mult = state->detection_multiplier;
369 bs->remote_timers.desired_min_tx = ntohl(state->desired_tx);
370 bs->remote_timers.required_min_rx = ntohl(state->required_rx);
371 bs->remote_timers.required_min_echo = ntohl(state->required_echo_rx);
372
373 /* Notify and update counters. */
374 control_notify(bs, bs->ses_state);
375
376 /* No state change. */
377 if (old_state == bs->ses_state)
378 return;
379
380 switch (bs->ses_state) {
381 case PTM_BFD_ADM_DOWN:
382 case PTM_BFD_DOWN:
383 /* Both states mean down. */
384 if (old_state == PTM_BFD_ADM_DOWN || old_state == PTM_BFD_DOWN)
385 break;
386
387 monotime(&bs->downtime);
388 bs->stats.session_down++;
389 break;
390 case PTM_BFD_UP:
391 monotime(&bs->uptime);
392 bs->stats.session_up++;
393 break;
394 case PTM_BFD_INIT:
395 /* NOTHING */
396 break;
397
398 default:
399 zlog_warn("%s: unhandled new state %d", __func__,
400 bs->ses_state);
401 break;
402 }
403
404 if (bglobal.debug_peer_event)
405 zlog_debug("state-change: [data plane: %s] %s -> %s",
406 bs_to_string(bs), state_list[old_state].str,
407 state_list[bs->ses_state].str);
408}
409
410/**
411 * Enqueue message in output buffer.
412 *
413 * \param[in,out] bdc data plane client context.
414 * \param[in] buf the message to buffer.
415 * \param[in] buflen the amount of bytes to buffer.
416 *
417 * \returns `-1` on failure (buffer full) or `0` on success.
418 */
419static int bfd_dplane_enqueue(struct bfd_dplane_ctx *bdc, const void *buf,
420 size_t buflen)
421{
422 size_t rlen;
423
6655b43d
RZ
424 /* Handle not connected yet client. */
425 if (bdc->client && bdc->sock == -1)
426 return -1;
427
230aefe2
RZ
428 /* Not enough space. */
429 if (buflen > STREAM_WRITEABLE(bdc->outbuf)) {
430 bdc->out_fullev++;
431 return -1;
432 }
433
434 /* Show debug message if active. */
435 bfd_dplane_debug_message((struct bfddp_message *)buf);
436
437 /* Buffer the message. */
438 stream_write(bdc->outbuf, buf, buflen);
439
440 /* Account message as sent. */
441 bdc->out_msgs++;
442 /* Register peak buffered bytes. */
443 rlen = STREAM_READABLE(bdc->outbuf);
444 if (bdc->out_bytes_peak < rlen)
445 bdc->out_bytes_peak = rlen;
446
447 /* Schedule if it is not yet. */
448 if (bdc->outbufev == NULL)
449 thread_add_write(master, bfd_dplane_write, bdc, bdc->sock,
450 &bdc->outbufev);
451
452 return 0;
453}
454
455static void bfd_dplane_echo_request_handle(struct bfd_dplane_ctx *bdc,
456 const struct bfddp_message *bm)
457{
458 struct bfddp_message msg = {};
459 uint16_t msglen = sizeof(msg.header) + sizeof(msg.data.echo);
460 struct timeval tv;
461
462 gettimeofday(&tv, NULL);
463
464 /* Prepare header. */
465 msg.header.version = BFD_DP_VERSION;
466 msg.header.type = htons(ECHO_REPLY);
467 msg.header.length = htons(msglen);
468
469 /* Prepare payload. */
470 msg.data.echo.dp_time = bm->data.echo.dp_time;
471 msg.data.echo.bfdd_time =
472 htobe64((uint64_t)((tv.tv_sec * 1000000) + tv.tv_usec));
473
474 /* Enqueue for output. */
475 bfd_dplane_enqueue(bdc, &msg, msglen);
476}
477
478static void bfd_dplane_handle_message(struct bfddp_message *msg, void *arg)
479{
480 enum bfddp_message_type bmt;
481 struct bfd_dplane_ctx *bdc = arg;
482
483 /* Call the appropriated handler. */
484 bmt = ntohs(msg->header.type);
485 switch (bmt) {
486 case ECHO_REQUEST:
487 bfd_dplane_echo_request_handle(bdc, msg);
488 break;
489 case BFD_STATE_CHANGE:
490 bfd_dplane_session_state_change(bdc, &msg->data.state);
491 break;
492 case ECHO_REPLY:
493 /* NOTHING: we don't do anything with this information. */
494 break;
495 case DP_ADD_SESSION:
496 case DP_DELETE_SESSION:
497 case DP_REQUEST_SESSION_COUNTERS:
498 /* NOTHING: we are not supposed to receive this. */
499 break;
500 case BFD_SESSION_COUNTERS:
501 /*
502 * NOTHING: caller of DP_REQUEST_SESSION_COUNTERS should
503 * handle this with `bfd_dplane_expect`.
504 */
505 break;
506
507 default:
508 zlog_debug("%s: unhandled message type %d", __func__, bmt);
509 break;
510 }
511}
512
513/**
514 * Reads the socket immediately to receive data plane answer to query.
515 *
516 * \param bdc the data plane context.
517 * \param id the message ID waiting response.
518 * \param cb the callback to call when ready.
519 * \param arg the callback argument.
520 *
521 * \return
522 * `-2` on unavailability (try again), `-1` on failure or `0` on success.
523 */
524static int bfd_dplane_expect(struct bfd_dplane_ctx *bdc, uint16_t id,
525 bfd_dplane_expect_cb cb, void *arg)
526{
527 struct bfddp_message_header *bh;
528 size_t rlen = 0, reads = 0;
529 ssize_t rv;
530
531 /*
532 * Don't attempt to read if buffer is full, otherwise we'll get a
533 * bogus 'connection closed' signal (rv == 0).
534 */
535 if (bdc->inbuf->endp == bdc->inbuf->size)
536 goto skip_read;
537
538read_again:
539 /* Attempt to read message from client. */
540 rv = stream_read_try(bdc->inbuf, bdc->sock,
541 STREAM_WRITEABLE(bdc->inbuf));
542 if (rv == 0) {
543 if (bglobal.debug_dplane)
544 zlog_info("%s: socket closed", __func__);
545
546 bfd_dplane_ctx_free(bdc);
547 return -1;
548 }
549 if (rv == -1) {
550 zlog_warn("%s: socket failed: %s", __func__, strerror(errno));
551 bfd_dplane_ctx_free(bdc);
552 return -1;
553 }
554
555 /* We got interrupted, reschedule read. */
556 if (rv == -2)
557 return -2;
558
559 /* Account read bytes. */
560 bdc->in_bytes += (uint64_t)rv;
561 /* Register peak buffered bytes. */
562 rlen = STREAM_READABLE(bdc->inbuf);
563 if (bdc->in_bytes_peak < rlen)
564 bdc->in_bytes_peak = rlen;
565
566skip_read:
567 while (rlen > 0) {
568 bh = (struct bfddp_message_header *)stream_pnt(bdc->inbuf);
569 /* Not enough data read. */
570 if (ntohs(bh->length) > rlen)
571 goto read_again;
572
573 /* Account full message read. */
574 bdc->in_msgs++;
575
576 /* Account this message as whole read for buffer reorganize. */
577 reads++;
578
579 /* Check for bad version. */
580 if (bh->version != BFD_DP_VERSION) {
581 zlog_err("%s: bad data plane client version: %d",
582 __func__, bh->version);
583 return -1;
584 }
585
586 /* Show debug message if active. */
587 bfd_dplane_debug_message((struct bfddp_message *)bh);
588
589 /*
590 * Handle incoming message with callback if the ID matches,
591 * otherwise fallback to default handler.
592 */
593 if (id && ntohs(bh->id) == id)
594 cb((struct bfddp_message *)bh, arg);
595 else
596 bfd_dplane_handle_message((struct bfddp_message *)bh,
597 bdc);
598
599 /* Advance current read pointer. */
600 stream_forward_getp(bdc->inbuf, ntohs(bh->length));
601
602 /* Reduce the buffer available bytes. */
603 rlen -= ntohs(bh->length);
604
605 /* Reorganize buffer to handle more bytes read. */
606 if (reads >= 3) {
607 stream_pulldown(bdc->inbuf);
608 reads = 0;
609 }
610
611 /* We found the message, return to caller. */
612 if (id && ntohs(bh->id) == id)
613 break;
614 }
615
616 return 0;
617}
618
619static int bfd_dplane_read(struct thread *t)
620{
621 struct bfd_dplane_ctx *bdc = THREAD_ARG(t);
622 int rv;
623
624 rv = bfd_dplane_expect(bdc, 0, bfd_dplane_handle_message, NULL);
625 if (rv == -1)
626 return 0;
627
628 stream_pulldown(bdc->inbuf);
629 thread_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev);
630 return 0;
631}
632
efd04d60
RZ
633static void _bfd_session_register_dplane(struct hash_bucket *hb, void *arg)
634{
635 struct bfd_session *bs = hb->data;
636 struct bfd_dplane_ctx *bdc = arg;
637
638 if (bs->bdc != NULL)
639 return;
640
641 /* Disable software session. */
642 bfd_session_disable(bs);
643
644 /* Move session to data plane. */
645 _bfd_dplane_add_session(bdc, bs);
646}
647
230aefe2
RZ
648static struct bfd_dplane_ctx *bfd_dplane_ctx_new(int sock)
649{
650 struct bfd_dplane_ctx *bdc;
651
652 bdc = XCALLOC(MTYPE_BFDD_DPLANE_CTX, sizeof(*bdc));
653 if (bdc == NULL)
654 return NULL;
655
656 bdc->sock = sock;
657 bdc->inbuf = stream_new(BFD_DPLANE_CLIENT_BUF_SIZE);
658 bdc->outbuf = stream_new(BFD_DPLANE_CLIENT_BUF_SIZE);
6655b43d
RZ
659
660 /* If not socket ready, skip read and session registration. */
661 if (sock == -1)
662 return bdc;
663
230aefe2
RZ
664 thread_add_read(master, bfd_dplane_read, bdc, sock, &bdc->inbufev);
665
efd04d60
RZ
666 /* Register all unattached sessions. */
667 bfd_key_iterate(_bfd_session_register_dplane, bdc);
668
230aefe2
RZ
669 return bdc;
670}
671
672static void _bfd_session_unregister_dplane(struct hash_bucket *hb, void *arg)
673{
674 struct bfd_session *bs = hb->data;
675 struct bfd_dplane_ctx *bdc = arg;
676
677 if (bs->bdc != bdc)
678 return;
679
680 bs->bdc = NULL;
efd04d60
RZ
681
682 /* Fallback to software. */
683 bfd_session_enable(bs);
230aefe2
RZ
684}
685
686static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc)
687{
688 if (bglobal.debug_dplane)
689 zlog_debug("%s: terminating data plane client %d", __func__,
690 bdc->sock);
691
6655b43d
RZ
692 /* Client mode has special treatment. */
693 if (bdc->client) {
694 /* Disable connection event if any. */
695 THREAD_OFF(bdc->connectev);
696
697 /* Normal treatment on shutdown. */
698 if (bglobal.bg_shutdown)
699 goto free_resources;
700
701 /* Attempt reconnection. */
702 socket_close(&bdc->sock);
703 THREAD_OFF(bdc->inbufev);
704 THREAD_OFF(bdc->outbufev);
705 thread_add_timer(master, bfd_dplane_client_connect, bdc, 3,
706 &bdc->connectev);
707 return;
708 }
709
710free_resources:
230aefe2
RZ
711 /* Remove from the list of attached data planes. */
712 TAILQ_REMOVE(&bglobal.bg_dplaneq, bdc, entry);
713
714 /* Detach all associated sessions. */
715 if (bglobal.bg_shutdown == false)
716 bfd_key_iterate(_bfd_session_unregister_dplane, bdc);
717
718 /* Free resources. */
719 socket_close(&bdc->sock);
720 stream_free(bdc->inbuf);
721 stream_free(bdc->outbuf);
722 THREAD_OFF(bdc->inbufev);
723 THREAD_OFF(bdc->outbufev);
724 XFREE(MTYPE_BFDD_DPLANE_CTX, bdc);
725}
726
efd04d60
RZ
727static void _bfd_dplane_session_fill(const struct bfd_session *bs,
728 struct bfddp_message *msg)
729{
730 uint16_t msglen = sizeof(msg->header) + sizeof(msg->data.session);
731
732 /* Message header. */
733 msg->header.version = BFD_DP_VERSION;
734 msg->header.length = ntohs(msglen);
735 msg->header.type = ntohs(DP_ADD_SESSION);
736
737 /* Message payload. */
738 msg->data.session.dst = bs->key.peer;
739 msg->data.session.src = bs->key.local;
740 msg->data.session.detect_mult = bs->detect_mult;
741
742 if (bs->ifp) {
743 msg->data.session.ifindex = htonl(bs->ifp->ifindex);
744 strlcpy(msg->data.session.ifname, bs->ifp->name,
745 sizeof(msg->data.session.ifname));
746 }
747 if (bs->flags & BFD_SESS_FLAG_MH) {
748 msg->data.session.flags |= SESSION_MULTIHOP;
749 msg->data.session.ttl = bs->mh_ttl;
750 } else
751 msg->data.session.ttl = BFD_TTL_VAL;
752
753 if (bs->flags & BFD_SESS_FLAG_IPV6)
754 msg->data.session.flags |= SESSION_IPV6;
755 if (bs->flags & BFD_SESS_FLAG_ECHO)
756 msg->data.session.flags |= SESSION_ECHO;
757 if (bs->flags & BFD_SESS_FLAG_CBIT)
758 msg->data.session.flags |= SESSION_CBIT;
759 if (bs->flags & BFD_SESS_FLAG_PASSIVE)
760 msg->data.session.flags |= SESSION_PASSIVE;
761 if (bs->flags & BFD_SESS_FLAG_SHUTDOWN)
762 msg->data.session.flags |= SESSION_SHUTDOWN;
763
764 msg->data.session.flags = htonl(msg->data.session.flags);
765 msg->data.session.lid = htonl(bs->discrs.my_discr);
766 msg->data.session.min_tx = htonl(bs->timers.desired_min_tx);
767 msg->data.session.min_rx = htonl(bs->timers.required_min_rx);
768 msg->data.session.min_echo_rx = htonl(bs->timers.required_min_echo);
769}
770
771static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc,
772 struct bfd_session *bs)
773{
774 int rv;
775
776 /* Associate session. */
777 bs->bdc = bdc;
778
779 /* Reset previous state. */
780 bs->remote_diag = 0;
781 bs->local_diag = 0;
782 bs->ses_state = PTM_BFD_DOWN;
783
784 /* Enqueue message to data plane client. */
785 rv = bfd_dplane_update_session(bs);
786 if (rv != 0)
787 bs->bdc = NULL;
788
789 return rv;
790}
791
400632a9
RZ
792static void _bfd_dplane_update_session_counters(struct bfddp_message *msg,
793 void *arg)
794{
795 struct bfd_session *bs = arg;
796
797 bs->stats.rx_ctrl_pkt =
798 be64toh(msg->data.session_counters.control_input_packets);
799 bs->stats.tx_ctrl_pkt =
800 be64toh(msg->data.session_counters.control_output_packets);
801 bs->stats.rx_echo_pkt =
802 be64toh(msg->data.session_counters.echo_input_packets);
803 bs->stats.tx_echo_pkt =
804 be64toh(msg->data.session_counters.echo_output_bytes);
805}
806
807/**
808 * Send message to data plane requesting the session counters.
809 *
810 * \param bs the BFD session.
811 *
812 * \returns `0` on failure or the request id.
813 */
814static uint16_t bfd_dplane_request_counters(const struct bfd_session *bs)
815{
816 struct bfddp_message msg = {};
817 size_t msglen = sizeof(msg.header) + sizeof(msg.data.counters_req);
818
819 /* Fill header information. */
820 msg.header.version = BFD_DP_VERSION;
821 msg.header.length = htons(msglen);
822 msg.header.type = htons(DP_REQUEST_SESSION_COUNTERS);
823 msg.header.id = htons(bfd_dplane_next_id(bs->bdc));
824
825 /* Session to get counters. */
826 msg.data.counters_req.lid = htonl(bs->discrs.my_discr);
827
828 /* If enqueue failed, let caller know. */
829 if (bfd_dplane_enqueue(bs->bdc, &msg, msglen) == -1)
830 return 0;
831
832 /* Flush socket. */
833 bfd_dplane_flush(bs->bdc);
834
835 return ntohs(msg.header.id);
836}
837
230aefe2
RZ
838/*
839 * Data plane listening socket.
840 */
841static int bfd_dplane_accept(struct thread *t)
842{
843 struct bfd_global *bg = THREAD_ARG(t);
844 struct bfd_dplane_ctx *bdc;
845 int sock;
846
847 /* Accept new connection. */
848 sock = accept(bg->bg_dplane_sock, NULL, 0);
849 if (sock == -1) {
850 zlog_warn("%s: accept failed: %s", __func__, strerror(errno));
851 goto reschedule_and_return;
852 }
853
854 /* Create and handle new connection. */
855 bdc = bfd_dplane_ctx_new(sock);
856 TAILQ_INSERT_TAIL(&bglobal.bg_dplaneq, bdc, entry);
857
858 if (bglobal.debug_dplane)
859 zlog_debug("%s: new data plane client connected", __func__);
860
861reschedule_and_return:
862 thread_add_read(master, bfd_dplane_accept, bg, bg->bg_dplane_sock,
863 &bglobal.bg_dplane_sockev);
864 return 0;
865}
866
6655b43d
RZ
867/*
868 * Data plane connecting socket.
869 */
870static void _bfd_dplane_client_bootstrap(struct bfd_dplane_ctx *bdc)
871{
872 bdc->connecting = false;
873
874 /* Clean up buffers. */
875 stream_reset(bdc->inbuf);
876 stream_reset(bdc->outbuf);
877
878 /* Ask for read notifications. */
879 thread_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev);
880
881 /* Remove all sessions then register again to send them all. */
882 bfd_key_iterate(_bfd_session_unregister_dplane, bdc);
883 bfd_key_iterate(_bfd_session_register_dplane, bdc);
884}
885
886static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx *bdc)
887{
888 int rv;
889 socklen_t rvlen = sizeof(rv);
890
891 /* Make sure `errno` is reset, then test `getsockopt` success. */
892 errno = 0;
893 if (getsockopt(bdc->sock, SOL_SOCKET, SO_ERROR, &rv, &rvlen) == -1)
894 rv = -1;
895
896 /* Connection successful. */
897 if (rv == 0) {
898 if (bglobal.debug_dplane)
899 zlog_debug("%s: connected to server: %d", __func__,
900 bdc->sock);
901
902 _bfd_dplane_client_bootstrap(bdc);
903 return false;
904 }
905
906 switch (rv) {
907 case EINTR:
908 case EAGAIN:
909 case EALREADY:
910 case EINPROGRESS:
911 /* non error, wait more. */
912 return true;
913
914 default:
915 zlog_warn("%s: connection failed: %s", __func__,
916 strerror(errno));
917 bfd_dplane_ctx_free(bdc);
918 return true;
919 }
920}
921
922static int bfd_dplane_client_connect(struct thread *t)
923{
924 struct bfd_dplane_ctx *bdc = THREAD_ARG(t);
925 int rv, sock;
926 socklen_t rvlen = sizeof(rv);
927
928 /* Allocate new socket. */
929 sock = socket(bdc->addr.sa.sa_family, SOCK_STREAM, 0);
930 if (sock == -1) {
931 zlog_warn("%s: failed to initialize socket: %s", __func__,
932 strerror(errno));
933 goto reschedule_connect;
934 }
935
936 /* Set non blocking socket. */
937 set_nonblocking(sock);
938
939 /* Set 'no delay' (disables nagle algorithm) for IPv4/IPv6. */
940 rv = 1;
941 if (bdc->addr.sa.sa_family != AF_UNIX
942 && setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &rv, rvlen) == -1)
943 zlog_warn("%s: TCP_NODELAY: %s", __func__, strerror(errno));
944
945 /* Attempt to connect. */
946 rv = connect(sock, &bdc->addr.sa, bdc->addrlen);
947 if (rv == -1 && (errno != EINPROGRESS && errno != EAGAIN)) {
948 zlog_warn("%s: data plane connection failed: %s", __func__,
949 strerror(errno));
950 goto reschedule_connect;
951 }
952
953 bdc->sock = sock;
954 if (rv == -1) {
955 if (bglobal.debug_dplane)
956 zlog_debug("%s: server connection in progress: %d",
957 __func__, sock);
958
959 /* If we are not connected yet, ask for write notifications. */
960 bdc->connecting = true;
961 thread_add_write(master, bfd_dplane_write, bdc, bdc->sock,
962 &bdc->outbufev);
963 } else {
964 if (bglobal.debug_dplane)
965 zlog_debug("%s: server connection: %d", __func__, sock);
966
967 /* Otherwise just start accepting data. */
968 _bfd_dplane_client_bootstrap(bdc);
969 }
970
971 return 0;
972
973reschedule_connect:
974 THREAD_OFF(bdc->inbufev);
975 THREAD_OFF(bdc->outbufev);
976 socket_close(&sock);
977 thread_add_timer(master, bfd_dplane_client_connect, bdc, 3,
978 &bdc->connectev);
979 return 0;
980}
981
982static void bfd_dplane_client_init(const struct sockaddr *sa, socklen_t salen)
983{
984 struct bfd_dplane_ctx *bdc;
985
986 /* Allocate context and copy address for reconnection. */
987 bdc = bfd_dplane_ctx_new(-1);
988 if (salen <= sizeof(bdc->addr)) {
989 memcpy(&bdc->addr, sa, salen);
990 bdc->addrlen = sizeof(bdc->addr);
991 } else {
992 memcpy(&bdc->addr, sa, sizeof(bdc->addr));
993 bdc->addrlen = sizeof(bdc->addr);
994 zlog_warn("%s: server address truncated (from %d to %d)",
995 __func__, salen, bdc->addrlen);
996 }
997
998 bdc->client = true;
999
1000 thread_add_timer(master, bfd_dplane_client_connect, bdc, 0,
1001 &bdc->connectev);
1002
1003 /* Insert into data plane lists. */
1004 TAILQ_INSERT_TAIL(&bglobal.bg_dplaneq, bdc, entry);
1005}
1006
230aefe2
RZ
1007/**
1008 * Termination phase of the distributed BFD infrastructure: free all allocated
1009 * resources.
1010 */
1011static int bfd_dplane_finish_late(void)
1012{
1013 struct bfd_dplane_ctx *bdc;
1014
1015 if (bglobal.debug_dplane)
1016 zlog_debug("%s: terminating distributed BFD", __func__);
1017
1018 /* Free all data plane client contexts. */
1019 while ((bdc = TAILQ_FIRST(&bglobal.bg_dplaneq)) != NULL)
1020 bfd_dplane_ctx_free(bdc);
1021
1022 /* Cancel accept thread and close socket. */
1023 THREAD_OFF(bglobal.bg_dplane_sockev);
1024 close(bglobal.bg_dplane_sock);
1025
1026 return 0;
1027}
1028
1029/*
1030 * Data plane exported functions.
1031 */
6655b43d 1032void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen, bool client)
230aefe2
RZ
1033{
1034 int sock;
1035
1036 zlog_info("initializing distributed BFD");
1037
6655b43d
RZ
1038 /* Initialize queue header. */
1039 TAILQ_INIT(&bglobal.bg_dplaneq);
1040
1041 /* Initialize listening socket. */
1042 bglobal.bg_dplane_sock = -1;
1043
1044 /* Observe shutdown events. */
1045 hook_register(frr_fini, bfd_dplane_finish_late);
1046
1047 /* Handle client mode. */
1048 if (client) {
1049 bfd_dplane_client_init(sa, salen);
1050 return;
1051 }
1052
230aefe2
RZ
1053 /*
1054 * Data plane socket creation:
1055 * - Set REUSEADDR option for taking over previously open socket.
1056 * - Bind to address requested (maybe IPv4, IPv6, UNIX etc...).
1057 * - Listen on that address for new connections.
1058 * - Ask to be waken up when a new connection comes.
1059 */
1060 sock = socket(sa->sa_family, SOCK_STREAM, 0);
1061 if (sock == -1) {
1062 zlog_warn("%s: failed to initialize socket: %s", __func__,
1063 strerror(errno));
1064 return;
1065 }
1066
1067 if (sockopt_reuseaddr(sock) == -1) {
1068 zlog_warn("%s: failed to set reuseaddr: %s", __func__,
1069 strerror(errno));
1070 close(sock);
1071 return;
1072 }
1073
1074 /* Handle UNIX socket: delete previous socket if any. */
1075 if (sa->sa_family == AF_UNIX)
1076 unlink(((struct sockaddr_un *)sa)->sun_path);
1077
1078 if (bind(sock, sa, salen) == -1) {
1079 zlog_warn("%s: failed to bind socket: %s", __func__,
1080 strerror(errno));
1081 close(sock);
1082 return;
1083 }
1084
1085 if (listen(sock, SOMAXCONN) == -1) {
1086 zlog_warn("%s: failed to put socket on listen: %s", __func__,
1087 strerror(errno));
1088 close(sock);
1089 return;
1090 }
1091
1092 bglobal.bg_dplane_sock = sock;
1093 thread_add_read(master, bfd_dplane_accept, &bglobal, sock,
1094 &bglobal.bg_dplane_sockev);
230aefe2 1095}
efd04d60
RZ
1096
1097int bfd_dplane_add_session(struct bfd_session *bs)
1098{
1099 struct bfd_dplane_ctx *bdc;
1100
1101 /* Select a data plane client to install session. */
1102 TAILQ_FOREACH (bdc, &bglobal.bg_dplaneq, entry) {
1103 if (_bfd_dplane_add_session(bdc, bs) == 0)
1104 return 0;
1105 }
1106
1107 return -1;
1108}
1109
1110int bfd_dplane_update_session(const struct bfd_session *bs)
1111{
1112 struct bfddp_message msg = {};
1113
1114 if (bs->bdc == NULL)
1115 return 0;
1116
1117 _bfd_dplane_session_fill(bs, &msg);
1118
1119 /* Enqueue message to data plane client. */
1120 return bfd_dplane_enqueue(bs->bdc, &msg, ntohs(msg.header.length));
1121}
1122
1123int bfd_dplane_delete_session(struct bfd_session *bs)
1124{
1125 struct bfddp_message msg = {};
1126 int rv;
1127
1128 /* Not using data plane, just return success. */
1129 if (bs->bdc == NULL)
1130 return 0;
1131
1132 /* Fill most of the common fields. */
1133 _bfd_dplane_session_fill(bs, &msg);
1134
1135 /* Change the message type. */
1136 msg.header.type = ntohs(DP_DELETE_SESSION);
1137
1138 /* Enqueue message to data plane client. */
1139 rv = bfd_dplane_enqueue(bs->bdc, &msg, ntohs(msg.header.length));
1140
1141 /* Remove association. */
1142 bs->bdc = NULL;
1143
1144 return rv;
1145}
400632a9
RZ
1146
1147/*
1148 * Data plane CLI.
1149 */
1150void bfd_dplane_show_counters(struct vty *vty)
1151{
1152 struct bfd_dplane_ctx *bdc;
1153
1154#define SHOW_COUNTER(label, counter, formatter) \
1155 vty_out(vty, "%28s: %" formatter "\n", (label), (counter))
1156
1157 vty_out(vty, "%28s\n%28s\n", "Data plane", "==========");
1158 TAILQ_FOREACH (bdc, &bglobal.bg_dplaneq, entry) {
1159 SHOW_COUNTER("File descriptor", bdc->sock, "d");
1160 SHOW_COUNTER("Input bytes", bdc->in_bytes, PRIu64);
1161 SHOW_COUNTER("Input bytes peak", bdc->in_bytes_peak, PRIu64);
1162 SHOW_COUNTER("Input messages", bdc->in_msgs, PRIu64);
1163 SHOW_COUNTER("Input current usage", STREAM_READABLE(bdc->inbuf),
1164 "zu");
1165 SHOW_COUNTER("Output bytes", bdc->out_bytes, PRIu64);
1166 SHOW_COUNTER("Output bytes peak", bdc->out_bytes_peak, PRIu64);
1167 SHOW_COUNTER("Output messages", bdc->out_msgs, PRIu64);
1168 SHOW_COUNTER("Output full events", bdc->out_fullev, PRIu64);
1169 SHOW_COUNTER("Output current usage",
1170 STREAM_READABLE(bdc->inbuf), "zu");
1171 vty_out(vty, "\n");
1172 }
1173#undef SHOW_COUNTER
1174}
1175
1176int bfd_dplane_update_session_counters(struct bfd_session *bs)
1177{
1178 uint16_t id;
1179 int rv;
1180
1181 /* If session is not using data plane, then just return success. */
1182 if (bs->bdc == NULL)
1183 return 0;
1184
1185 /* Make the request. */
1186 id = bfd_dplane_request_counters(bs);
1187 if (id == 0) {
1188 zlog_debug("%s: counters request failed", __func__);
1189 return -1;
1190 }
1191
1192 /* Handle interruptions. */
1193 do {
1194 rv = bfd_dplane_expect(bs->bdc, id,
1195 _bfd_dplane_update_session_counters, bs);
1196 } while (rv == -2);
1197
1198 return rv;
1199}