back to topotato report
topotato coverage report
Current view: top level - bfdd - dplane.c (source / functions) Hit Total Coverage
Test: test_pim_bfd.py::PIMBFDTest Lines: 8 511 1.6 %
Date: 2023-02-24 18:39:40 Functions: 4 32 12.5 %

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

Generated by: LCOV version v1.16-topotato