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