back to topotato report
topotato coverage report
Current view: top level - bfdd - control.c (source / functions) Hit Total Coverage
Test: test_pim_basic2.py::PIMTopo2Test Lines: 34 394 8.6 %
Date: 2023-02-24 18:39:36 Functions: 4 30 13.3 %

          Line data    Source code
       1             : /*********************************************************************
       2             :  * Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF")
       3             :  *
       4             :  * This program is free software; you can redistribute it and/or modify it
       5             :  * under the terms of the GNU General Public License as published by the Free
       6             :  * Software Foundation; either version 2 of the License, or (at your option)
       7             :  * any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      10             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      11             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      12             :  * more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License along
      15             :  * with this program; see the file COPYING; if not, write to the Free Software
      16             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      17             :  *
      18             :  * control.c: implements the BFD daemon control socket. It will be used
      19             :  * to talk with clients daemon/scripts/consumers.
      20             :  *
      21             :  * Authors
      22             :  * -------
      23             :  * Rafael Zalamena <rzalamena@opensourcerouting.org>
      24             :  */
      25             : 
      26             : #include <zebra.h>
      27             : 
      28             : #include <sys/un.h>
      29             : 
      30             : #include "bfd.h"
      31             : 
      32             : /*
      33             :  * Prototypes
      34             :  */
      35             : static int sock_set_nonblock(int fd);
      36             : struct bfd_control_queue *control_queue_new(struct bfd_control_socket *bcs);
      37             : static void control_queue_free(struct bfd_control_socket *bcs,
      38             :                                struct bfd_control_queue *bcq);
      39             : static int control_queue_dequeue(struct bfd_control_socket *bcs);
      40             : static int control_queue_enqueue(struct bfd_control_socket *bcs,
      41             :                                  struct bfd_control_msg *bcm);
      42             : static int control_queue_enqueue_first(struct bfd_control_socket *bcs,
      43             :                                        struct bfd_control_msg *bcm);
      44             : struct bfd_notify_peer *control_notifypeer_new(struct bfd_control_socket *bcs,
      45             :                                                struct bfd_session *bs);
      46             : static void control_notifypeer_free(struct bfd_control_socket *bcs,
      47             :                                     struct bfd_notify_peer *bnp);
      48             : struct bfd_notify_peer *control_notifypeer_find(struct bfd_control_socket *bcs,
      49             :                                                 struct bfd_session *bs);
      50             : 
      51             : 
      52             : struct bfd_control_socket *control_new(int sd);
      53             : static void control_free(struct bfd_control_socket *bcs);
      54             : static void control_reset_buf(struct bfd_control_buffer *bcb);
      55             : static void control_read(struct thread *t);
      56             : static void control_write(struct thread *t);
      57             : 
      58             : static void control_handle_request_add(struct bfd_control_socket *bcs,
      59             :                                        struct bfd_control_msg *bcm);
      60             : static void control_handle_request_del(struct bfd_control_socket *bcs,
      61             :                                        struct bfd_control_msg *bcm);
      62             : static int notify_add_cb(struct bfd_peer_cfg *bpc, void *arg);
      63             : static int notify_del_cb(struct bfd_peer_cfg *bpc, void *arg);
      64             : static void control_handle_notify_add(struct bfd_control_socket *bcs,
      65             :                                       struct bfd_control_msg *bcm);
      66             : static void control_handle_notify_del(struct bfd_control_socket *bcs,
      67             :                                       struct bfd_control_msg *bcm);
      68             : static void _control_handle_notify(struct hash_bucket *hb, void *arg);
      69             : static void control_handle_notify(struct bfd_control_socket *bcs,
      70             :                                   struct bfd_control_msg *bcm);
      71             : static void control_response(struct bfd_control_socket *bcs, uint16_t id,
      72             :                              const char *status, const char *error);
      73             : 
      74             : static void _control_notify_config(struct bfd_control_socket *bcs,
      75             :                                    const char *op, struct bfd_session *bs);
      76             : static void _control_notify(struct bfd_control_socket *bcs,
      77             :                             struct bfd_session *bs);
      78             : 
      79             : 
      80             : /*
      81             :  * Functions
      82             :  */
      83           4 : static int sock_set_nonblock(int fd)
      84             : {
      85           4 :         int flags;
      86             : 
      87           4 :         flags = fcntl(fd, F_GETFL, 0);
      88           4 :         if (flags == -1) {
      89           0 :                 zlog_warn("%s: fcntl F_GETFL: %s", __func__, strerror(errno));
      90           0 :                 return -1;
      91             :         }
      92             : 
      93           4 :         flags |= O_NONBLOCK;
      94           4 :         if (fcntl(fd, F_SETFL, flags) == -1) {
      95           0 :                 zlog_warn("%s: fcntl F_SETFL: %s", __func__, strerror(errno));
      96           0 :                 return -1;
      97             :         }
      98             : 
      99             :         return 0;
     100             : }
     101             : 
     102           4 : int control_init(const char *path)
     103             : {
     104           4 :         int sd;
     105           4 :         mode_t umval;
     106           4 :         struct sockaddr_un sun_ = {
     107             :                 .sun_family = AF_UNIX,
     108             :                 .sun_path = BFDD_CONTROL_SOCKET,
     109             :         };
     110             : 
     111           4 :         if (path)
     112           4 :                 strlcpy(sun_.sun_path, path, sizeof(sun_.sun_path));
     113             : 
     114             :         /* Remove previously created sockets. */
     115           4 :         unlink(sun_.sun_path);
     116             : 
     117           4 :         sd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
     118           4 :         if (sd == -1) {
     119           0 :                 zlog_err("%s: socket: %s", __func__, strerror(errno));
     120           0 :                 return -1;
     121             :         }
     122             : 
     123           4 :         umval = umask(0);
     124           4 :         if (bind(sd, (struct sockaddr *)&sun_, sizeof(sun_)) == -1) {
     125           0 :                 zlog_err("%s: bind: %s", __func__, strerror(errno));
     126           0 :                 close(sd);
     127           0 :                 return -1;
     128             :         }
     129           4 :         umask(umval);
     130             : 
     131           4 :         if (listen(sd, SOMAXCONN) == -1) {
     132           0 :                 zlog_err("%s: listen: %s", __func__, strerror(errno));
     133           0 :                 close(sd);
     134           0 :                 return -1;
     135             :         }
     136             : 
     137           4 :         sock_set_nonblock(sd);
     138             : 
     139           4 :         bglobal.bg_csock = sd;
     140             : 
     141           4 :         return 0;
     142             : }
     143             : 
     144           4 : void control_shutdown(void)
     145             : {
     146           4 :         struct bfd_control_socket *bcs;
     147             : 
     148           4 :         thread_cancel(&bglobal.bg_csockev);
     149             : 
     150           4 :         socket_close(&bglobal.bg_csock);
     151             : 
     152           4 :         while (!TAILQ_EMPTY(&bglobal.bg_bcslist)) {
     153           0 :                 bcs = TAILQ_FIRST(&bglobal.bg_bcslist);
     154           0 :                 control_free(bcs);
     155             :         }
     156           4 : }
     157             : 
     158           0 : void control_accept(struct thread *t)
     159             : {
     160           0 :         int csock, sd = THREAD_FD(t);
     161             : 
     162           0 :         csock = accept(sd, NULL, 0);
     163           0 :         if (csock == -1) {
     164           0 :                 zlog_warn("%s: accept: %s", __func__, strerror(errno));
     165           0 :                 return;
     166             :         }
     167             : 
     168           0 :         control_new(csock);
     169             : 
     170           0 :         thread_add_read(master, control_accept, NULL, sd, &bglobal.bg_csockev);
     171             : }
     172             : 
     173             : 
     174             : /*
     175             :  * Client handling
     176             :  */
     177           0 : struct bfd_control_socket *control_new(int sd)
     178             : {
     179           0 :         struct bfd_control_socket *bcs;
     180             : 
     181           0 :         bcs = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*bcs));
     182             : 
     183             :         /* Disable notifications by default. */
     184           0 :         bcs->bcs_notify = 0;
     185             : 
     186           0 :         bcs->bcs_sd = sd;
     187           0 :         thread_add_read(master, control_read, bcs, sd, &bcs->bcs_ev);
     188             : 
     189           0 :         TAILQ_INIT(&bcs->bcs_bcqueue);
     190           0 :         TAILQ_INIT(&bcs->bcs_bnplist);
     191           0 :         TAILQ_INSERT_TAIL(&bglobal.bg_bcslist, bcs, bcs_entry);
     192             : 
     193           0 :         return bcs;
     194             : }
     195             : 
     196           0 : static void control_free(struct bfd_control_socket *bcs)
     197             : {
     198           0 :         struct bfd_control_queue *bcq;
     199           0 :         struct bfd_notify_peer *bnp;
     200             : 
     201           0 :         thread_cancel(&(bcs->bcs_ev));
     202           0 :         thread_cancel(&(bcs->bcs_outev));
     203             : 
     204           0 :         close(bcs->bcs_sd);
     205             : 
     206           0 :         TAILQ_REMOVE(&bglobal.bg_bcslist, bcs, bcs_entry);
     207             : 
     208             :         /* Empty output queue. */
     209           0 :         while (!TAILQ_EMPTY(&bcs->bcs_bcqueue)) {
     210           0 :                 bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
     211           0 :                 control_queue_free(bcs, bcq);
     212             :         }
     213             : 
     214             :         /* Empty notification list. */
     215           0 :         while (!TAILQ_EMPTY(&bcs->bcs_bnplist)) {
     216           0 :                 bnp = TAILQ_FIRST(&bcs->bcs_bnplist);
     217           0 :                 control_notifypeer_free(bcs, bnp);
     218             :         }
     219             : 
     220           0 :         control_reset_buf(&bcs->bcs_bin);
     221           0 :         XFREE(MTYPE_BFDD_CONTROL, bcs);
     222           0 : }
     223             : 
     224           0 : struct bfd_notify_peer *control_notifypeer_new(struct bfd_control_socket *bcs,
     225             :                                                struct bfd_session *bs)
     226             : {
     227           0 :         struct bfd_notify_peer *bnp;
     228             : 
     229           0 :         bnp = control_notifypeer_find(bcs, bs);
     230           0 :         if (bnp)
     231             :                 return bnp;
     232             : 
     233           0 :         bnp = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*bnp));
     234             : 
     235           0 :         TAILQ_INSERT_TAIL(&bcs->bcs_bnplist, bnp, bnp_entry);
     236           0 :         bnp->bnp_bs = bs;
     237           0 :         bs->refcount++;
     238             : 
     239           0 :         return bnp;
     240             : }
     241             : 
     242           0 : static void control_notifypeer_free(struct bfd_control_socket *bcs,
     243             :                                     struct bfd_notify_peer *bnp)
     244             : {
     245           0 :         TAILQ_REMOVE(&bcs->bcs_bnplist, bnp, bnp_entry);
     246           0 :         bnp->bnp_bs->refcount--;
     247           0 :         XFREE(MTYPE_BFDD_CONTROL, bnp);
     248           0 : }
     249             : 
     250           0 : struct bfd_notify_peer *control_notifypeer_find(struct bfd_control_socket *bcs,
     251             :                                                 struct bfd_session *bs)
     252             : {
     253           0 :         struct bfd_notify_peer *bnp;
     254             : 
     255           0 :         TAILQ_FOREACH (bnp, &bcs->bcs_bnplist, bnp_entry) {
     256           0 :                 if (bnp->bnp_bs == bs)
     257           0 :                         return bnp;
     258             :         }
     259             : 
     260             :         return NULL;
     261             : }
     262             : 
     263           0 : struct bfd_control_queue *control_queue_new(struct bfd_control_socket *bcs)
     264             : {
     265           0 :         struct bfd_control_queue *bcq;
     266             : 
     267           0 :         bcq = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*bcq));
     268             : 
     269           0 :         control_reset_buf(&bcq->bcq_bcb);
     270           0 :         TAILQ_INSERT_TAIL(&bcs->bcs_bcqueue, bcq, bcq_entry);
     271             : 
     272           0 :         return bcq;
     273             : }
     274             : 
     275           0 : static void control_queue_free(struct bfd_control_socket *bcs,
     276             :                                struct bfd_control_queue *bcq)
     277             : {
     278           0 :         control_reset_buf(&bcq->bcq_bcb);
     279           0 :         TAILQ_REMOVE(&bcs->bcs_bcqueue, bcq, bcq_entry);
     280           0 :         XFREE(MTYPE_BFDD_NOTIFICATION, bcq);
     281           0 : }
     282             : 
     283           0 : static int control_queue_dequeue(struct bfd_control_socket *bcs)
     284             : {
     285           0 :         struct bfd_control_queue *bcq;
     286             : 
     287             :         /* List is empty, nothing to do. */
     288           0 :         if (TAILQ_EMPTY(&bcs->bcs_bcqueue))
     289           0 :                 goto empty_list;
     290             : 
     291           0 :         bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
     292           0 :         control_queue_free(bcs, bcq);
     293             : 
     294             :         /* Get the next buffer to send. */
     295           0 :         if (TAILQ_EMPTY(&bcs->bcs_bcqueue))
     296           0 :                 goto empty_list;
     297             : 
     298           0 :         bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
     299           0 :         bcs->bcs_bout = &bcq->bcq_bcb;
     300             : 
     301           0 :         bcs->bcs_outev = NULL;
     302           0 :         thread_add_write(master, control_write, bcs, bcs->bcs_sd,
     303             :                          &bcs->bcs_outev);
     304             : 
     305           0 :         return 1;
     306             : 
     307           0 : empty_list:
     308           0 :         thread_cancel(&(bcs->bcs_outev));
     309           0 :         bcs->bcs_bout = NULL;
     310           0 :         return 0;
     311             : }
     312             : 
     313           0 : static int control_queue_enqueue(struct bfd_control_socket *bcs,
     314             :                                  struct bfd_control_msg *bcm)
     315             : {
     316           0 :         struct bfd_control_queue *bcq;
     317           0 :         struct bfd_control_buffer *bcb;
     318             : 
     319           0 :         bcq = control_queue_new(bcs);
     320             : 
     321           0 :         bcb = &bcq->bcq_bcb;
     322           0 :         bcb->bcb_left = sizeof(struct bfd_control_msg) + ntohl(bcm->bcm_length);
     323           0 :         bcb->bcb_pos = 0;
     324           0 :         bcb->bcb_bcm = bcm;
     325             : 
     326             :         /* If this is the first item, then dequeue and start using it. */
     327           0 :         if (bcs->bcs_bout == NULL) {
     328           0 :                 bcs->bcs_bout = bcb;
     329             : 
     330             :                 /* New messages, active write events. */
     331           0 :                 thread_add_write(master, control_write, bcs, bcs->bcs_sd,
     332             :                                  &bcs->bcs_outev);
     333             :         }
     334             : 
     335           0 :         return 0;
     336             : }
     337             : 
     338           0 : static int control_queue_enqueue_first(struct bfd_control_socket *bcs,
     339             :                                        struct bfd_control_msg *bcm)
     340             : {
     341           0 :         struct bfd_control_queue *bcq, *bcqn;
     342           0 :         struct bfd_control_buffer *bcb;
     343             : 
     344             :         /* Enqueue it somewhere. */
     345           0 :         if (control_queue_enqueue(bcs, bcm) == -1)
     346             :                 return -1;
     347             : 
     348             :         /*
     349             :          * The item is either the first or the last. So we must first
     350             :          * check the best case where the item is already the first.
     351             :          */
     352           0 :         bcq = TAILQ_FIRST(&bcs->bcs_bcqueue);
     353           0 :         bcb = &bcq->bcq_bcb;
     354           0 :         if (bcm == bcb->bcb_bcm)
     355             :                 return 0;
     356             : 
     357             :         /*
     358             :          * The item was not the first, so it is the last. We'll try to
     359             :          * assign it to the head of the queue, however if there is a
     360             :          * transfer in progress, then we have to make the item as the
     361             :          * next one.
     362             :          *
     363             :          * Interrupting the transfer of in progress message will cause
     364             :          * the client to lose track of the message position/data.
     365             :          */
     366           0 :         bcqn = TAILQ_LAST(&bcs->bcs_bcqueue, bcqueue);
     367           0 :         TAILQ_REMOVE(&bcs->bcs_bcqueue, bcqn, bcq_entry);
     368           0 :         if (bcb->bcb_pos != 0) {
     369             :                 /*
     370             :                  * First position is already being sent, insert into
     371             :                  * second position.
     372             :                  */
     373           0 :                 TAILQ_INSERT_AFTER(&bcs->bcs_bcqueue, bcq, bcqn, bcq_entry);
     374             :         } else {
     375             :                 /*
     376             :                  * Old message didn't start being sent, we still have
     377             :                  * time to put this one in the head of the queue.
     378             :                  */
     379           0 :                 TAILQ_INSERT_HEAD(&bcs->bcs_bcqueue, bcqn, bcq_entry);
     380           0 :                 bcb = &bcqn->bcq_bcb;
     381           0 :                 bcs->bcs_bout = bcb;
     382             :         }
     383             : 
     384             :         return 0;
     385             : }
     386             : 
     387           0 : static void control_reset_buf(struct bfd_control_buffer *bcb)
     388             : {
     389             :         /* Get ride of old data. */
     390           0 :         XFREE(MTYPE_BFDD_NOTIFICATION, bcb->bcb_buf);
     391           0 :         bcb->bcb_pos = 0;
     392           0 :         bcb->bcb_left = 0;
     393           0 : }
     394             : 
     395           0 : static void control_read(struct thread *t)
     396             : {
     397           0 :         struct bfd_control_socket *bcs = THREAD_ARG(t);
     398           0 :         struct bfd_control_buffer *bcb = &bcs->bcs_bin;
     399           0 :         int sd = bcs->bcs_sd;
     400           0 :         struct bfd_control_msg bcm;
     401           0 :         ssize_t bread;
     402           0 :         size_t plen;
     403             : 
     404             :         /*
     405             :          * Check if we have already downloaded message content, if so then skip
     406             :          * to
     407             :          * download the rest of it and process.
     408             :          *
     409             :          * Otherwise download a new message header and allocate the necessary
     410             :          * memory.
     411             :          */
     412           0 :         if (bcb->bcb_buf != NULL)
     413           0 :                 goto skip_header;
     414             : 
     415           0 :         bread = read(sd, &bcm, sizeof(bcm));
     416           0 :         if (bread == 0) {
     417           0 :                 control_free(bcs);
     418           0 :                 return;
     419             :         }
     420           0 :         if (bread < 0) {
     421           0 :                 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
     422           0 :                         goto schedule_next_read;
     423             : 
     424           0 :                 zlog_warn("%s: read: %s", __func__, strerror(errno));
     425           0 :                 control_free(bcs);
     426           0 :                 return;
     427             :         }
     428             : 
     429             :         /* Validate header fields. */
     430           0 :         plen = ntohl(bcm.bcm_length);
     431           0 :         if (plen < 2) {
     432           0 :                 zlog_debug("%s: client closed due small message length: %d",
     433             :                            __func__, bcm.bcm_length);
     434           0 :                 control_free(bcs);
     435           0 :                 return;
     436             :         }
     437             : 
     438             : #define FRR_BFD_MAXLEN 10 * 1024
     439             : 
     440           0 :         if (plen > FRR_BFD_MAXLEN) {
     441           0 :                 zlog_debug("%s: client closed, invalid message length: %d",
     442             :                            __func__, bcm.bcm_length);
     443           0 :                 control_free(bcs);
     444           0 :                 return;
     445             :         }
     446             : 
     447           0 :         if (bcm.bcm_ver != BMV_VERSION_1) {
     448           0 :                 zlog_debug("%s: client closed due bad version: %d", __func__,
     449             :                            bcm.bcm_ver);
     450           0 :                 control_free(bcs);
     451           0 :                 return;
     452             :         }
     453             : 
     454             :         /* Prepare the buffer to load the message. */
     455           0 :         bcs->bcs_version = bcm.bcm_ver;
     456           0 :         bcs->bcs_type = bcm.bcm_type;
     457             : 
     458           0 :         bcb->bcb_pos = sizeof(bcm);
     459           0 :         bcb->bcb_left = plen;
     460           0 :         bcb->bcb_buf = XMALLOC(MTYPE_BFDD_NOTIFICATION,
     461             :                                sizeof(bcm) + bcb->bcb_left + 1);
     462           0 :         if (bcb->bcb_buf == NULL) {
     463             :                 zlog_warn("%s: not enough memory for message size: %zu",
     464             :                           __func__, bcb->bcb_left);
     465             :                 control_free(bcs);
     466             :                 return;
     467             :         }
     468             : 
     469           0 :         memcpy(bcb->bcb_buf, &bcm, sizeof(bcm));
     470             : 
     471             :         /* Terminate data string with NULL for later processing. */
     472           0 :         bcb->bcb_buf[sizeof(bcm) + bcb->bcb_left] = 0;
     473             : 
     474           0 : skip_header:
     475             :         /* Download the remaining data of the message and process it. */
     476           0 :         bread = read(sd, &bcb->bcb_buf[bcb->bcb_pos], bcb->bcb_left);
     477           0 :         if (bread == 0) {
     478           0 :                 control_free(bcs);
     479           0 :                 return;
     480             :         }
     481           0 :         if (bread < 0) {
     482           0 :                 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
     483           0 :                         goto schedule_next_read;
     484             : 
     485           0 :                 zlog_warn("%s: read: %s", __func__, strerror(errno));
     486           0 :                 control_free(bcs);
     487           0 :                 return;
     488             :         }
     489             : 
     490           0 :         bcb->bcb_pos += bread;
     491           0 :         bcb->bcb_left -= bread;
     492             :         /* We need more data, return to wait more. */
     493           0 :         if (bcb->bcb_left > 0)
     494           0 :                 goto schedule_next_read;
     495             : 
     496           0 :         switch (bcb->bcb_bcm->bcm_type) {
     497           0 :         case BMT_REQUEST_ADD:
     498           0 :                 control_handle_request_add(bcs, bcb->bcb_bcm);
     499           0 :                 break;
     500           0 :         case BMT_REQUEST_DEL:
     501           0 :                 control_handle_request_del(bcs, bcb->bcb_bcm);
     502           0 :                 break;
     503           0 :         case BMT_NOTIFY:
     504           0 :                 control_handle_notify(bcs, bcb->bcb_bcm);
     505           0 :                 break;
     506           0 :         case BMT_NOTIFY_ADD:
     507           0 :                 control_handle_notify_add(bcs, bcb->bcb_bcm);
     508           0 :                 break;
     509           0 :         case BMT_NOTIFY_DEL:
     510           0 :                 control_handle_notify_del(bcs, bcb->bcb_bcm);
     511           0 :                 break;
     512             : 
     513           0 :         default:
     514           0 :                 zlog_debug("%s: unhandled message type: %d", __func__,
     515             :                            bcb->bcb_bcm->bcm_type);
     516           0 :                 control_response(bcs, bcb->bcb_bcm->bcm_id, BCM_RESPONSE_ERROR,
     517             :                                  "invalid message type");
     518           0 :                 break;
     519             :         }
     520             : 
     521           0 :         bcs->bcs_version = 0;
     522           0 :         bcs->bcs_type = 0;
     523           0 :         control_reset_buf(bcb);
     524             : 
     525           0 : schedule_next_read:
     526           0 :         bcs->bcs_ev = NULL;
     527           0 :         thread_add_read(master, control_read, bcs, sd, &bcs->bcs_ev);
     528             : }
     529             : 
     530           0 : static void control_write(struct thread *t)
     531             : {
     532           0 :         struct bfd_control_socket *bcs = THREAD_ARG(t);
     533           0 :         struct bfd_control_buffer *bcb = bcs->bcs_bout;
     534           0 :         int sd = bcs->bcs_sd;
     535           0 :         ssize_t bwrite;
     536             : 
     537           0 :         bwrite = write(sd, &bcb->bcb_buf[bcb->bcb_pos], bcb->bcb_left);
     538           0 :         if (bwrite == 0) {
     539           0 :                 control_free(bcs);
     540           0 :                 return;
     541             :         }
     542           0 :         if (bwrite < 0) {
     543           0 :                 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
     544           0 :                         bcs->bcs_outev = NULL;
     545           0 :                         thread_add_write(master, control_write, bcs,
     546             :                                          bcs->bcs_sd, &bcs->bcs_outev);
     547           0 :                         return;
     548             :                 }
     549             : 
     550           0 :                 zlog_warn("%s: write: %s", __func__, strerror(errno));
     551           0 :                 control_free(bcs);
     552           0 :                 return;
     553             :         }
     554             : 
     555           0 :         bcb->bcb_pos += bwrite;
     556           0 :         bcb->bcb_left -= bwrite;
     557           0 :         if (bcb->bcb_left > 0) {
     558           0 :                 bcs->bcs_outev = NULL;
     559           0 :                 thread_add_write(master, control_write, bcs, bcs->bcs_sd,
     560             :                                  &bcs->bcs_outev);
     561           0 :                 return;
     562             :         }
     563             : 
     564           0 :         control_queue_dequeue(bcs);
     565             : }
     566             : 
     567             : 
     568             : /*
     569             :  * Message processing
     570             :  */
     571           0 : static void control_handle_request_add(struct bfd_control_socket *bcs,
     572             :                                        struct bfd_control_msg *bcm)
     573             : {
     574           0 :         const char *json = (const char *)bcm->bcm_data;
     575             : 
     576           0 :         if (config_request_add(json) == 0)
     577           0 :                 control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
     578             :         else
     579           0 :                 control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
     580             :                                  "request add failed");
     581           0 : }
     582             : 
     583           0 : static void control_handle_request_del(struct bfd_control_socket *bcs,
     584             :                                        struct bfd_control_msg *bcm)
     585             : {
     586           0 :         const char *json = (const char *)bcm->bcm_data;
     587             : 
     588           0 :         if (config_request_del(json) == 0)
     589           0 :                 control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
     590             :         else
     591           0 :                 control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
     592             :                                  "request del failed");
     593           0 : }
     594             : 
     595           0 : static struct bfd_session *_notify_find_peer(struct bfd_peer_cfg *bpc)
     596             : {
     597           0 :         struct peer_label *pl;
     598             : 
     599           0 :         if (bpc->bpc_has_label) {
     600           0 :                 pl = pl_find(bpc->bpc_label);
     601           0 :                 if (pl)
     602           0 :                         return pl->pl_bs;
     603             :         }
     604             : 
     605           0 :         return bs_peer_find(bpc);
     606             : }
     607             : 
     608           0 : static void _control_handle_notify(struct hash_bucket *hb, void *arg)
     609             : {
     610           0 :         struct bfd_control_socket *bcs = arg;
     611           0 :         struct bfd_session *bs = hb->data;
     612             : 
     613             :         /* Notify peer configuration. */
     614           0 :         if (bcs->bcs_notify & BCM_NOTIFY_CONFIG)
     615           0 :                 _control_notify_config(bcs, BCM_NOTIFY_CONFIG_ADD, bs);
     616             : 
     617             :         /* Notify peer status. */
     618           0 :         if (bcs->bcs_notify & BCM_NOTIFY_PEER_STATE)
     619           0 :                 _control_notify(bcs, bs);
     620           0 : }
     621             : 
     622           0 : static void control_handle_notify(struct bfd_control_socket *bcs,
     623             :                                   struct bfd_control_msg *bcm)
     624             : {
     625           0 :         memcpy(&bcs->bcs_notify, bcm->bcm_data, sizeof(bcs->bcs_notify));
     626             : 
     627           0 :         control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
     628             : 
     629             :         /*
     630             :          * If peer asked for notification configuration, send everything that
     631             :          * was configured until the moment to sync up.
     632             :          */
     633           0 :         if (bcs->bcs_notify & (BCM_NOTIFY_CONFIG | BCM_NOTIFY_PEER_STATE))
     634           0 :                 bfd_id_iterate(_control_handle_notify, bcs);
     635           0 : }
     636             : 
     637           0 : static int notify_add_cb(struct bfd_peer_cfg *bpc, void *arg)
     638             : {
     639           0 :         struct bfd_control_socket *bcs = arg;
     640           0 :         struct bfd_session *bs = _notify_find_peer(bpc);
     641             : 
     642           0 :         if (bs == NULL)
     643             :                 return -1;
     644             : 
     645           0 :         control_notifypeer_new(bcs, bs);
     646             : 
     647             :         /* Notify peer status. */
     648           0 :         _control_notify(bcs, bs);
     649             : 
     650           0 :         return 0;
     651             : }
     652             : 
     653           0 : static int notify_del_cb(struct bfd_peer_cfg *bpc, void *arg)
     654             : {
     655           0 :         struct bfd_control_socket *bcs = arg;
     656           0 :         struct bfd_session *bs = _notify_find_peer(bpc);
     657           0 :         struct bfd_notify_peer *bnp;
     658             : 
     659           0 :         if (bs == NULL)
     660             :                 return -1;
     661             : 
     662           0 :         bnp = control_notifypeer_find(bcs, bs);
     663           0 :         if (bnp)
     664           0 :                 control_notifypeer_free(bcs, bnp);
     665             : 
     666             :         return 0;
     667             : }
     668             : 
     669           0 : static void control_handle_notify_add(struct bfd_control_socket *bcs,
     670             :                                       struct bfd_control_msg *bcm)
     671             : {
     672           0 :         const char *json = (const char *)bcm->bcm_data;
     673             : 
     674           0 :         if (config_notify_request(bcs, json, notify_add_cb) == 0) {
     675           0 :                 control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
     676           0 :                 return;
     677             :         }
     678             : 
     679           0 :         control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
     680             :                          "failed to parse notify data");
     681             : }
     682             : 
     683           0 : static void control_handle_notify_del(struct bfd_control_socket *bcs,
     684             :                                       struct bfd_control_msg *bcm)
     685             : {
     686           0 :         const char *json = (const char *)bcm->bcm_data;
     687             : 
     688           0 :         if (config_notify_request(bcs, json, notify_del_cb) == 0) {
     689           0 :                 control_response(bcs, bcm->bcm_id, BCM_RESPONSE_OK, NULL);
     690           0 :                 return;
     691             :         }
     692             : 
     693           0 :         control_response(bcs, bcm->bcm_id, BCM_RESPONSE_ERROR,
     694             :                          "failed to parse notify data");
     695             : }
     696             : 
     697             : 
     698             : /*
     699             :  * Internal functions used by the BFD daemon.
     700             :  */
     701           0 : static void control_response(struct bfd_control_socket *bcs, uint16_t id,
     702             :                              const char *status, const char *error)
     703             : {
     704           0 :         struct bfd_control_msg *bcm;
     705           0 :         char *jsonstr;
     706           0 :         size_t jsonstrlen;
     707             : 
     708             :         /* Generate JSON response. */
     709           0 :         jsonstr = config_response(status, error);
     710           0 :         if (jsonstr == NULL) {
     711           0 :                 zlog_warn("%s: config_response: failed to get JSON str",
     712             :                           __func__);
     713           0 :                 return;
     714             :         }
     715             : 
     716             :         /* Allocate data and answer. */
     717           0 :         jsonstrlen = strlen(jsonstr);
     718           0 :         bcm = XMALLOC(MTYPE_BFDD_NOTIFICATION,
     719             :                       sizeof(struct bfd_control_msg) + jsonstrlen);
     720             : 
     721           0 :         bcm->bcm_length = htonl(jsonstrlen);
     722           0 :         bcm->bcm_ver = BMV_VERSION_1;
     723           0 :         bcm->bcm_type = BMT_RESPONSE;
     724           0 :         bcm->bcm_id = id;
     725           0 :         memcpy(bcm->bcm_data, jsonstr, jsonstrlen);
     726           0 :         XFREE(MTYPE_BFDD_NOTIFICATION, jsonstr);
     727             : 
     728           0 :         control_queue_enqueue_first(bcs, bcm);
     729             : }
     730             : 
     731           0 : static void _control_notify(struct bfd_control_socket *bcs,
     732             :                             struct bfd_session *bs)
     733             : {
     734           0 :         struct bfd_control_msg *bcm;
     735           0 :         char *jsonstr;
     736           0 :         size_t jsonstrlen;
     737             : 
     738             :         /* Generate JSON response. */
     739           0 :         jsonstr = config_notify(bs);
     740           0 :         if (jsonstr == NULL) {
     741           0 :                 zlog_warn("%s: config_notify: failed to get JSON str",
     742             :                           __func__);
     743           0 :                 return;
     744             :         }
     745             : 
     746             :         /* Allocate data and answer. */
     747           0 :         jsonstrlen = strlen(jsonstr);
     748           0 :         bcm = XMALLOC(MTYPE_BFDD_NOTIFICATION,
     749             :                       sizeof(struct bfd_control_msg) + jsonstrlen);
     750             : 
     751           0 :         bcm->bcm_length = htonl(jsonstrlen);
     752           0 :         bcm->bcm_ver = BMV_VERSION_1;
     753           0 :         bcm->bcm_type = BMT_NOTIFY;
     754           0 :         bcm->bcm_id = htons(BCM_NOTIFY_ID);
     755           0 :         memcpy(bcm->bcm_data, jsonstr, jsonstrlen);
     756           0 :         XFREE(MTYPE_BFDD_NOTIFICATION, jsonstr);
     757             : 
     758           0 :         control_queue_enqueue(bcs, bcm);
     759             : }
     760             : 
     761           0 : int control_notify(struct bfd_session *bs, uint8_t notify_state)
     762             : {
     763           0 :         struct bfd_control_socket *bcs;
     764           0 :         struct bfd_notify_peer *bnp;
     765             : 
     766             :         /* Notify zebra listeners as well. */
     767           0 :         ptm_bfd_notify(bs, notify_state);
     768             : 
     769             :         /*
     770             :          * PERFORMANCE: reuse the bfd_control_msg allocated data for
     771             :          * all control sockets to avoid wasting memory.
     772             :          */
     773           0 :         TAILQ_FOREACH (bcs, &bglobal.bg_bcslist, bcs_entry) {
     774             :                 /*
     775             :                  * Test for all notifications first, then search for
     776             :                  * specific peers.
     777             :                  */
     778           0 :                 if ((bcs->bcs_notify & BCM_NOTIFY_PEER_STATE) == 0) {
     779           0 :                         bnp = control_notifypeer_find(bcs, bs);
     780             :                         /*
     781             :                          * If the notification is not configured here,
     782             :                          * don't send it.
     783             :                          */
     784           0 :                         if (bnp == NULL)
     785           0 :                                 continue;
     786             :                 }
     787             : 
     788           0 :                 _control_notify(bcs, bs);
     789             :         }
     790             : 
     791           0 :         return 0;
     792             : }
     793             : 
     794           0 : static void _control_notify_config(struct bfd_control_socket *bcs,
     795             :                                    const char *op, struct bfd_session *bs)
     796             : {
     797           0 :         struct bfd_control_msg *bcm;
     798           0 :         char *jsonstr;
     799           0 :         size_t jsonstrlen;
     800             : 
     801             :         /* Generate JSON response. */
     802           0 :         jsonstr = config_notify_config(op, bs);
     803           0 :         if (jsonstr == NULL) {
     804           0 :                 zlog_warn("%s: config_notify_config: failed to get JSON str",
     805             :                           __func__);
     806           0 :                 return;
     807             :         }
     808             : 
     809             :         /* Allocate data and answer. */
     810           0 :         jsonstrlen = strlen(jsonstr);
     811           0 :         bcm = XMALLOC(MTYPE_BFDD_NOTIFICATION,
     812             :                       sizeof(struct bfd_control_msg) + jsonstrlen);
     813             : 
     814           0 :         bcm->bcm_length = htonl(jsonstrlen);
     815           0 :         bcm->bcm_ver = BMV_VERSION_1;
     816           0 :         bcm->bcm_type = BMT_NOTIFY;
     817           0 :         bcm->bcm_id = htons(BCM_NOTIFY_ID);
     818           0 :         memcpy(bcm->bcm_data, jsonstr, jsonstrlen);
     819           0 :         XFREE(MTYPE_BFDD_NOTIFICATION, jsonstr);
     820             : 
     821           0 :         control_queue_enqueue(bcs, bcm);
     822             : }
     823             : 
     824          16 : int control_notify_config(const char *op, struct bfd_session *bs)
     825             : {
     826          16 :         struct bfd_control_socket *bcs;
     827          16 :         struct bfd_notify_peer *bnp;
     828             : 
     829             :         /* Remove the control sockets notification for this peer. */
     830          16 :         if (strcmp(op, BCM_NOTIFY_CONFIG_DELETE) == 0 && bs->refcount > 0) {
     831           0 :                 TAILQ_FOREACH (bcs, &bglobal.bg_bcslist, bcs_entry) {
     832           0 :                         bnp = control_notifypeer_find(bcs, bs);
     833           0 :                         if (bnp)
     834           0 :                                 control_notifypeer_free(bcs, bnp);
     835             :                 }
     836             :         }
     837             : 
     838             :         /*
     839             :          * PERFORMANCE: reuse the bfd_control_msg allocated data for
     840             :          * all control sockets to avoid wasting memory.
     841             :          */
     842          16 :         TAILQ_FOREACH (bcs, &bglobal.bg_bcslist, bcs_entry) {
     843             :                 /*
     844             :                  * Test for all notifications first, then search for
     845             :                  * specific peers.
     846             :                  */
     847           0 :                 if ((bcs->bcs_notify & BCM_NOTIFY_CONFIG) == 0)
     848           0 :                         continue;
     849             : 
     850           0 :                 _control_notify_config(bcs, op, bs);
     851             :         }
     852             : 
     853          16 :         return 0;
     854             : }

Generated by: LCOV version v1.16-topotato