]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/ptm_adapter.c
bfdd: check bfd on upon interface presence from separate vrf
[mirror_frr.git] / bfdd / ptm_adapter.c
1 /*
2 * BFD PTM adapter code
3 * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
4 *
5 * FRR is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2, or (at your option) any
8 * later version.
9 *
10 * FRR is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with FRR; see the file COPYING. If not, write to the Free
17 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 * 02111-1307, USA.
19 */
20
21 #include <zebra.h>
22
23 #include "lib/libfrr.h"
24 #include "lib/queue.h"
25 #include "lib/stream.h"
26 #include "lib/zclient.h"
27
28 #include "lib/bfd.h"
29
30 #include "bfd.h"
31
32 /*
33 * Data structures
34 */
35 struct ptm_client_notification {
36 struct bfd_session *pcn_bs;
37 struct ptm_client *pcn_pc;
38
39 TAILQ_ENTRY(ptm_client_notification) pcn_entry;
40 };
41 TAILQ_HEAD(pcnqueue, ptm_client_notification);
42
43 struct ptm_client {
44 uint32_t pc_pid;
45 struct pcnqueue pc_pcnqueue;
46
47 TAILQ_ENTRY(ptm_client) pc_entry;
48 };
49 TAILQ_HEAD(pcqueue, ptm_client);
50
51 static struct pcqueue pcqueue;
52 static struct zclient *zclient;
53
54
55 /*
56 * Prototypes
57 */
58 static int _ptm_msg_address(struct stream *msg, int family, const void *addr);
59
60 static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
61 static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
62 struct bfd_peer_cfg *bpc, struct ptm_client **pc);
63
64 static struct ptm_client *pc_lookup(uint32_t pid);
65 static struct ptm_client *pc_new(uint32_t pid);
66 static void pc_free(struct ptm_client *pc);
67 static void pc_free_all(void);
68 static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
69 struct bfd_session *bs);
70 static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
71 struct bfd_session *bs);
72 static void pcn_free(struct ptm_client_notification *pcn);
73
74
75 static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id);
76 static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id);
77 static void bfdd_client_register(struct stream *msg);
78 static void bfdd_client_deregister(struct stream *msg);
79
80 /*
81 * Functions
82 */
83 #ifdef BFD_DEBUG
84 static void debug_printbpc(const char *func, unsigned int line,
85 struct bfd_peer_cfg *bpc);
86
87 static void debug_printbpc(const char *func, unsigned int line,
88 struct bfd_peer_cfg *bpc)
89 {
90 char addr[3][128];
91 char timers[3][128];
92
93 addr[0][0] = addr[1][0] = addr[2][0] = timers[0][0] = timers[1][0] =
94 timers[2][0] = 0;
95
96 snprintf(addr[0], sizeof(addr[0]), "peer:%s", satostr(&bpc->bpc_peer));
97 if (bpc->bpc_local.sa_sin.sin_family)
98 snprintf(addr[1], sizeof(addr[1]), " local:%s",
99 satostr(&bpc->bpc_local));
100
101 if (bpc->bpc_has_localif)
102 snprintf(addr[2], sizeof(addr[2]), " ifname:%s",
103 bpc->bpc_localif);
104
105 if (bpc->bpc_has_vrfname)
106 snprintf(addr[2], sizeof(addr[2]), " vrf:%s", bpc->bpc_vrfname);
107
108 if (bpc->bpc_has_recvinterval)
109 snprintf(timers[0], sizeof(timers[0]), " rx:%lu",
110 bpc->bpc_recvinterval);
111
112 if (bpc->bpc_has_txinterval)
113 snprintf(timers[1], sizeof(timers[1]), " tx:%lu",
114 bpc->bpc_recvinterval);
115
116 if (bpc->bpc_has_detectmultiplier)
117 snprintf(timers[2], sizeof(timers[2]), " detect-multiplier:%d",
118 bpc->bpc_detectmultiplier);
119
120 log_debug("%s:%d: %s %s%s%s%s%s%s", func, line,
121 bpc->bpc_mhop ? "multi-hop" : "single-hop", addr[0], addr[1],
122 addr[2], timers[0], timers[1], timers[2]);
123 }
124
125 #define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc))
126 #else
127 #define DEBUG_PRINTBPC(bpc)
128 #endif /* BFD_DEBUG */
129
130 static int _ptm_msg_address(struct stream *msg, int family, const void *addr)
131 {
132 stream_putc(msg, family);
133
134 switch (family) {
135 case AF_INET:
136 stream_put(msg, addr, sizeof(struct in_addr));
137 stream_putc(msg, 32);
138 break;
139
140 case AF_INET6:
141 stream_put(msg, addr, sizeof(struct in6_addr));
142 stream_putc(msg, 128);
143 break;
144
145 default:
146 assert(0);
147 break;
148 }
149
150 return 0;
151 }
152
153 int ptm_bfd_notify(struct bfd_session *bs)
154 {
155 struct stream *msg;
156
157 bs->stats.znotification++;
158
159 /*
160 * Message format:
161 * - header: command, vrf
162 * - l: interface index
163 * - c: family
164 * - AF_INET:
165 * - 4 bytes: ipv4
166 * - AF_INET6:
167 * - 16 bytes: ipv6
168 * - c: prefix length
169 * - l: bfd status
170 * - c: family
171 * - AF_INET:
172 * - 4 bytes: ipv4
173 * - AF_INET6:
174 * - 16 bytes: ipv6
175 * - c: prefix length
176 *
177 * Commands: ZEBRA_BFD_DEST_REPLAY
178 *
179 * q(64), l(32), w(16), c(8)
180 */
181 msg = zclient->obuf;
182 stream_reset(msg);
183
184 /* TODO: VRF handling */
185 if (bs->vrf)
186 zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, bs->vrf->vrf_id);
187 else
188 zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
189
190 /* This header will be handled by `zebra_ptm.c`. */
191 stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE);
192
193 /* NOTE: Interface is a shortcut to avoid comparing source address. */
194 if (bs->ifp != NULL)
195 stream_putl(msg, bs->ifp->ifindex);
196 else
197 stream_putl(msg, IFINDEX_INTERNAL);
198
199 /* BFD destination prefix information. */
200 _ptm_msg_address(msg, bs->key.family, &bs->key.peer);
201
202 /* BFD status */
203 switch (bs->ses_state) {
204 case PTM_BFD_UP:
205 stream_putl(msg, BFD_STATUS_UP);
206 break;
207
208 case PTM_BFD_ADM_DOWN:
209 case PTM_BFD_DOWN:
210 case PTM_BFD_INIT:
211 stream_putl(msg, BFD_STATUS_DOWN);
212 break;
213
214 default:
215 stream_putl(msg, BFD_STATUS_UNKNOWN);
216 break;
217 }
218
219 /* BFD source prefix information. */
220 _ptm_msg_address(msg, bs->key.family, &bs->key.local);
221
222 /* Write packet size. */
223 stream_putw_at(msg, 0, stream_get_endp(msg));
224
225 return zclient_send_message(zclient);
226 }
227
228 static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa)
229 {
230 uint16_t family;
231
232 STREAM_GETW(msg, family);
233
234 switch (family) {
235 case AF_INET:
236 sa->sa_sin.sin_family = family;
237 STREAM_GET(&sa->sa_sin.sin_addr, msg,
238 sizeof(sa->sa_sin.sin_addr));
239 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
240 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
241 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
242 return;
243
244 case AF_INET6:
245 sa->sa_sin6.sin6_family = family;
246 STREAM_GET(&sa->sa_sin6.sin6_addr, msg,
247 sizeof(sa->sa_sin6.sin6_addr));
248 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
249 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
250 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
251 return;
252
253 default:
254 log_warning("ptm-read-address: invalid family: %d", family);
255 break;
256 }
257
258 stream_failure:
259 memset(sa, 0, sizeof(*sa));
260 }
261
262 static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id,
263 struct bfd_peer_cfg *bpc, struct ptm_client **pc)
264 {
265 uint32_t pid;
266 uint8_t ttl __attribute__((unused));
267 size_t ifnamelen;
268
269 /*
270 * Register/Deregister/Update Message format:
271 * - header: Command, VRF
272 * - l: pid
273 * - w: family
274 * - AF_INET:
275 * - l: destination ipv4
276 * - AF_INET6:
277 * - 16 bytes: destination IPv6
278 * - command != ZEBRA_BFD_DEST_DEREGISTER
279 * - l: min_rx
280 * - l: min_tx
281 * - c: detect multiplier
282 * - c: is_multihop?
283 * - multihop:
284 * - w: family
285 * - AF_INET:
286 * - l: destination ipv4
287 * - AF_INET6:
288 * - 16 bytes: destination IPv6
289 * - c: ttl
290 * - no multihop
291 * - AF_INET6:
292 * - w: family
293 * - 16 bytes: ipv6 address
294 * - c: ifname length
295 * - X bytes: interface name
296 *
297 * q(64), l(32), w(16), c(8)
298 */
299
300 /* Initialize parameters return values. */
301 memset(bpc, 0, sizeof(*bpc));
302 *pc = NULL;
303
304 /* Find or allocate process context data. */
305 STREAM_GETL(msg, pid);
306
307 *pc = pc_new(pid);
308 if (*pc == NULL) {
309 log_debug("ptm-read: failed to allocate memory");
310 return -1;
311 }
312
313 /* Register/update peer information. */
314 _ptm_msg_read_address(msg, &bpc->bpc_peer);
315
316 /* Determine IP type from peer destination. */
317 bpc->bpc_ipv4 = (bpc->bpc_peer.sa_sin.sin_family == AF_INET);
318
319 /* Get peer configuration. */
320 if (command != ZEBRA_BFD_DEST_DEREGISTER) {
321 STREAM_GETL(msg, bpc->bpc_recvinterval);
322 bpc->bpc_has_recvinterval =
323 (bpc->bpc_recvinterval != BPC_DEF_RECEIVEINTERVAL);
324
325 STREAM_GETL(msg, bpc->bpc_txinterval);
326 bpc->bpc_has_txinterval =
327 (bpc->bpc_txinterval != BPC_DEF_TRANSMITINTERVAL);
328
329 STREAM_GETC(msg, bpc->bpc_detectmultiplier);
330 bpc->bpc_has_detectmultiplier =
331 (bpc->bpc_detectmultiplier != BPC_DEF_DETECTMULTIPLIER);
332 }
333
334 /* Read (single|multi)hop and its options. */
335 STREAM_GETC(msg, bpc->bpc_mhop);
336 if (bpc->bpc_mhop) {
337 /* Read multihop source address and TTL. */
338 _ptm_msg_read_address(msg, &bpc->bpc_local);
339 STREAM_GETC(msg, ttl);
340 } else {
341 /* If target is IPv6, then we must obtain local address. */
342 if (bpc->bpc_ipv4 == false)
343 _ptm_msg_read_address(msg, &bpc->bpc_local);
344
345 /*
346 * Read interface name and make sure it fits our data
347 * structure, otherwise fail.
348 */
349 STREAM_GETC(msg, ifnamelen);
350 if (ifnamelen >= sizeof(bpc->bpc_localif)) {
351 log_error("ptm-read: interface name is too big");
352 return -1;
353 }
354
355 bpc->bpc_has_localif = ifnamelen > 0;
356 if (bpc->bpc_has_localif) {
357 STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
358 bpc->bpc_localif[ifnamelen] = 0;
359 }
360 }
361 if (vrf_id != VRF_DEFAULT) {
362 struct vrf *vrf;
363
364 vrf = vrf_lookup_by_id(vrf_id);
365 if (vrf) {
366 bpc->bpc_has_vrfname = true;
367 strlcpy(bpc->bpc_vrfname, vrf->name, sizeof(bpc->bpc_vrfname));
368 } else {
369 log_error("ptm-read: vrf id %u could not be identified", vrf_id);
370 return -1;
371 }
372 }
373
374 /* Sanity check: peer and local address must match IP types. */
375 if (bpc->bpc_local.sa_sin.sin_family != 0
376 && (bpc->bpc_local.sa_sin.sin_family
377 != bpc->bpc_peer.sa_sin.sin_family)) {
378 log_warning("ptm-read: peer family doesn't match local type");
379 return -1;
380 }
381
382 return 0;
383
384 stream_failure:
385 return -1;
386 }
387
388 static void bfdd_dest_register(struct stream *msg, vrf_id_t vrf_id)
389 {
390 struct ptm_client *pc;
391 struct ptm_client_notification *pcn;
392 struct bfd_session *bs;
393 struct bfd_peer_cfg bpc;
394
395 /* Read the client context and peer data. */
396 if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, vrf_id, &bpc, &pc) == -1)
397 return;
398
399 DEBUG_PRINTBPC(&bpc);
400
401 /* Find or start new BFD session. */
402 bs = bs_peer_find(&bpc);
403 if (bs == NULL) {
404 bs = ptm_bfd_sess_new(&bpc);
405 if (bs == NULL) {
406 log_debug("ptm-add-dest: failed to create BFD session");
407 return;
408 }
409 } else {
410 /* Don't try to change echo/shutdown state. */
411 bpc.bpc_echo = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
412 bpc.bpc_shutdown =
413 BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
414 }
415
416 /* Create client peer notification register. */
417 pcn = pcn_new(pc, bs);
418 if (pcn == NULL) {
419 log_error("ptm-add-dest: failed to registrate notifications");
420 return;
421 }
422
423 ptm_bfd_notify(bs);
424 }
425
426 static void bfdd_dest_deregister(struct stream *msg, vrf_id_t vrf_id)
427 {
428 struct ptm_client *pc;
429 struct ptm_client_notification *pcn;
430 struct bfd_session *bs;
431 struct bfd_peer_cfg bpc;
432
433 /* Read the client context and peer data. */
434 if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, vrf_id, &bpc, &pc) == -1)
435 return;
436
437 DEBUG_PRINTBPC(&bpc);
438
439 /* Find or start new BFD session. */
440 bs = bs_peer_find(&bpc);
441 if (bs == NULL) {
442 log_debug("ptm-del-dest: failed to find BFD session");
443 return;
444 }
445
446 /* Unregister client peer notification. */
447 pcn = pcn_lookup(pc, bs);
448 pcn_free(pcn);
449 if (bs->refcount ||
450 BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG))
451 return;
452 ptm_bfd_sess_del(&bpc);
453 }
454
455 /*
456 * header: command, VRF
457 * l: pid
458 */
459 static void bfdd_client_register(struct stream *msg)
460 {
461 struct ptm_client *pc;
462 uint32_t pid;
463
464 /* Find or allocate process context data. */
465 STREAM_GETL(msg, pid);
466
467 pc = pc_new(pid);
468 if (pc == NULL) {
469 log_error("ptm-add-client: failed to register client: %u", pid);
470 return;
471 }
472
473 return;
474
475 stream_failure:
476 log_error("ptm-add-client: failed to register client");
477 }
478
479 /*
480 * header: command, VRF
481 * l: pid
482 */
483 static void bfdd_client_deregister(struct stream *msg)
484 {
485 struct ptm_client *pc;
486 uint32_t pid;
487
488 /* Find or allocate process context data. */
489 STREAM_GETL(msg, pid);
490
491 pc = pc_lookup(pid);
492 if (pc == NULL) {
493 log_debug("ptm-del-client: failed to find client: %u", pid);
494 return;
495 }
496
497 pc_free(pc);
498
499 return;
500
501 stream_failure:
502 log_error("ptm-del-client: failed to deregister client");
503 }
504
505 static int bfdd_replay(ZAPI_CALLBACK_ARGS)
506 {
507 struct stream *msg = zclient->ibuf;
508 uint32_t rcmd;
509
510 STREAM_GETL(msg, rcmd);
511
512 switch (rcmd) {
513 case ZEBRA_BFD_DEST_REGISTER:
514 case ZEBRA_BFD_DEST_UPDATE:
515 bfdd_dest_register(msg, vrf_id);
516 break;
517 case ZEBRA_BFD_DEST_DEREGISTER:
518 bfdd_dest_deregister(msg, vrf_id);
519 break;
520 case ZEBRA_BFD_CLIENT_REGISTER:
521 bfdd_client_register(msg);
522 break;
523 case ZEBRA_BFD_CLIENT_DEREGISTER:
524 bfdd_client_deregister(msg);
525 break;
526
527 default:
528 log_debug("ptm-replay: invalid message type %u", rcmd);
529 return -1;
530 }
531
532 return 0;
533
534 stream_failure:
535 log_error("ptm-replay: failed to find command");
536 return -1;
537 }
538
539 static void bfdd_zebra_connected(struct zclient *zc)
540 {
541 struct stream *msg = zc->obuf;
542
543 /* Clean-up and free ptm clients data memory. */
544 pc_free_all();
545
546 /*
547 * The replay is an empty message just to trigger client daemons
548 * configuration replay.
549 */
550 stream_reset(msg);
551 zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
552 stream_putl(msg, ZEBRA_BFD_DEST_REPLAY);
553 stream_putw_at(msg, 0, stream_get_endp(msg));
554
555 /* Ask for interfaces information. */
556 zclient_create_header(msg, ZEBRA_INTERFACE_ADD, VRF_DEFAULT);
557
558 /* Send requests. */
559 zclient_send_message(zclient);
560 }
561
562 static void bfdd_sessions_enable_interface(struct interface *ifp)
563 {
564 struct bfd_session_observer *bso;
565 struct bfd_session *bs;
566 struct vrf *vrf;
567
568 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
569 bs = bso->bso_bs;
570 if (bso->bso_isinterface == false)
571 continue;
572 /* Interface name mismatch. */
573 if (strcmp(ifp->name, bs->key.ifname))
574 continue;
575 vrf = vrf_lookup_by_id(ifp->vrf_id);
576 if (!vrf)
577 continue;
578 if (bs->key.vrfname[0] &&
579 strcmp(vrf->name, bs->key.vrfname))
580 continue;
581 /* Skip enabled sessions. */
582 if (bs->sock != -1)
583 continue;
584
585 /* Try to enable it. */
586 bfd_session_enable(bs);
587 }
588 }
589
590 static void bfdd_sessions_disable_interface(struct interface *ifp)
591 {
592 struct bfd_session_observer *bso;
593 struct bfd_session *bs;
594
595 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
596 if (bso->bso_isinterface == false)
597 continue;
598
599 /* Interface name mismatch. */
600 bs = bso->bso_bs;
601 if (strcmp(ifp->name, bs->key.ifname))
602 continue;
603 /* Skip disabled sessions. */
604 if (bs->sock == -1)
605 continue;
606
607 /* Try to enable it. */
608 bfd_session_disable(bs);
609
610 TAILQ_INSERT_HEAD(&bglobal.bg_obslist, bso, bso_entry);
611 }
612 }
613
614 void bfdd_sessions_enable_vrf(struct vrf *vrf)
615 {
616 struct bfd_session_observer *bso;
617 struct bfd_session *bs;
618
619 /* it may affect configs without interfaces */
620 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
621 bs = bso->bso_bs;
622 if (bs->vrf)
623 continue;
624 if (bs->key.vrfname[0] &&
625 strcmp(vrf->name, bs->key.vrfname))
626 continue;
627 /* need to update the vrf information on
628 * bs so that callbacks are handled
629 */
630 bs->vrf = vrf;
631 /* Skip enabled sessions. */
632 if (bs->sock != -1)
633 continue;
634 /* Try to enable it. */
635 bfd_session_enable(bs);
636 }
637 }
638
639 void bfdd_sessions_disable_vrf(struct vrf *vrf)
640 {
641 struct bfd_session_observer *bso;
642 struct bfd_session *bs;
643
644 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
645 if (bso->bso_isinterface)
646 continue;
647 bs = bso->bso_bs;
648 if (bs->key.vrfname[0] &&
649 strcmp(vrf->name, bs->key.vrfname))
650 continue;
651 /* Skip disabled sessions. */
652 if (bs->sock == -1)
653 continue;
654
655 /* Try to enable it. */
656 bfd_session_disable(bs);
657 }
658 }
659
660 static int bfdd_interface_update(ZAPI_CALLBACK_ARGS)
661 {
662 struct interface *ifp;
663
664 /*
665 * `zebra_interface_add_read` will handle the interface creation
666 * on `lib/if.c`. We'll use that data structure instead of
667 * rolling our own.
668 */
669 if (cmd == ZEBRA_INTERFACE_ADD) {
670 ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
671 if (ifp == NULL)
672 return 0;
673
674 bfdd_sessions_enable_interface(ifp);
675 return 0;
676 }
677
678 /* Update interface information. */
679 ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
680 if (ifp == NULL)
681 return 0;
682
683 bfdd_sessions_disable_interface(ifp);
684
685 if_set_index(ifp, IFINDEX_INTERNAL);
686
687 return 0;
688 }
689
690 static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS)
691 {
692 struct interface *ifp;
693 vrf_id_t nvrfid;
694
695 ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid);
696 if (ifp == NULL)
697 return 0;
698
699 if_update_to_new_vrf(ifp, nvrfid);
700
701 return 0;
702 }
703
704 static void bfdd_sessions_enable_address(struct connected *ifc)
705 {
706 struct bfd_session_observer *bso;
707 struct bfd_session *bs;
708 struct prefix prefix;
709
710 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
711 if (bso->bso_isaddress == false)
712 continue;
713
714 /* Skip enabled sessions. */
715 bs = bso->bso_bs;
716 if (bs->sock != -1)
717 continue;
718
719 /* Check address. */
720 prefix = bso->bso_addr;
721 prefix.prefixlen = ifc->address->prefixlen;
722 if (prefix_cmp(&prefix, ifc->address))
723 continue;
724
725 /* Try to enable it. */
726 bfd_session_enable(bs);
727 }
728 }
729
730 static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
731 {
732 struct connected *ifc;
733
734 ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
735 if (ifc == NULL)
736 return 0;
737
738 bfdd_sessions_enable_address(ifc);
739
740 return 0;
741 }
742
743 void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
744 {
745 zclient = zclient_new(master, &zclient_options_default);
746 assert(zclient != NULL);
747 zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
748
749 /*
750 * We'll receive all messages through replay, however it will
751 * contain a special field with the real command inside so we
752 * avoid having to create too many handlers.
753 */
754 zclient->bfd_dest_replay = bfdd_replay;
755
756 /* Send replay request on zebra connect. */
757 zclient->zebra_connected = bfdd_zebra_connected;
758
759 /* Learn interfaces from zebra instead of the OS. */
760 zclient->interface_add = bfdd_interface_update;
761 zclient->interface_delete = bfdd_interface_update;
762
763 /* Learn about interface VRF. */
764 zclient->interface_vrf_update = bfdd_interface_vrf_update;
765
766 /* Learn about new addresses being registered. */
767 zclient->interface_address_add = bfdd_interface_address_update;
768 zclient->interface_address_delete = bfdd_interface_address_update;
769 }
770
771 void bfdd_zclient_register(vrf_id_t vrf_id)
772 {
773 if (!zclient || zclient->sock < 0)
774 return;
775 zclient_send_reg_requests(zclient, vrf_id);
776 }
777
778 void bfdd_zclient_unregister(vrf_id_t vrf_id)
779 {
780 if (!zclient || zclient->sock < 0)
781 return;
782 zclient_send_dereg_requests(zclient, vrf_id);
783 }
784
785 void bfdd_zclient_stop(void)
786 {
787 zclient_stop(zclient);
788
789 /* Clean-up and free ptm clients data memory. */
790 pc_free_all();
791 }
792
793
794 /*
795 * Client handling.
796 */
797 static struct ptm_client *pc_lookup(uint32_t pid)
798 {
799 struct ptm_client *pc;
800
801 TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
802 if (pc->pc_pid != pid)
803 continue;
804
805 break;
806 }
807
808 return pc;
809 }
810
811 static struct ptm_client *pc_new(uint32_t pid)
812 {
813 struct ptm_client *pc;
814
815 /* Look up first, if not found create the client. */
816 pc = pc_lookup(pid);
817 if (pc != NULL)
818 return pc;
819
820 /* Allocate the client data and save it. */
821 pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
822
823 pc->pc_pid = pid;
824 TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
825 return pc;
826 }
827
828 static void pc_free(struct ptm_client *pc)
829 {
830 struct ptm_client_notification *pcn;
831
832 if (pc == NULL)
833 return;
834
835 TAILQ_REMOVE(&pcqueue, pc, pc_entry);
836
837 while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) {
838 pcn = TAILQ_FIRST(&pc->pc_pcnqueue);
839 pcn_free(pcn);
840 }
841
842 XFREE(MTYPE_BFDD_CONTROL, pc);
843 }
844
845 static void pc_free_all(void)
846 {
847 struct ptm_client *pc;
848
849 while (!TAILQ_EMPTY(&pcqueue)) {
850 pc = TAILQ_FIRST(&pcqueue);
851 pc_free(pc);
852 }
853 }
854
855 static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
856 struct bfd_session *bs)
857 {
858 struct ptm_client_notification *pcn;
859
860 /* Try to find an existing pcn fist. */
861 pcn = pcn_lookup(pc, bs);
862 if (pcn != NULL)
863 return pcn;
864
865 /* Save the client notification data. */
866 pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
867
868 TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
869 pcn->pcn_pc = pc;
870 pcn->pcn_bs = bs;
871 bs->refcount++;
872
873 return pcn;
874 }
875
876 static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
877 struct bfd_session *bs)
878 {
879 struct ptm_client_notification *pcn;
880
881 TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
882 if (pcn->pcn_bs != bs)
883 continue;
884
885 break;
886 }
887
888 return pcn;
889 }
890
891 static void pcn_free(struct ptm_client_notification *pcn)
892 {
893 struct ptm_client *pc;
894 struct bfd_session *bs;
895
896 if (pcn == NULL)
897 return;
898
899 /* Handle session de-registration. */
900 bs = pcn->pcn_bs;
901 pcn->pcn_bs = NULL;
902 bs->refcount--;
903
904 /* Handle ptm_client deregistration. */
905 pc = pcn->pcn_pc;
906 pcn->pcn_pc = NULL;
907 TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
908
909 XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
910 }