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