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