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