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