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