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