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