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