]> git.proxmox.com Git - mirror_frr.git/blob - bfdd/ptm_adapter.c
Merge pull request #4296 from adharkar/frr-master-fib_prefix
[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 }
611 }
612
613 void bfdd_sessions_enable_vrf(struct vrf *vrf)
614 {
615 struct bfd_session_observer *bso;
616 struct bfd_session *bs;
617
618 /* it may affect configs without interfaces */
619 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
620 bs = bso->bso_bs;
621 if (bs->vrf)
622 continue;
623 if (bs->key.vrfname[0] &&
624 strcmp(vrf->name, bs->key.vrfname))
625 continue;
626 /* need to update the vrf information on
627 * bs so that callbacks are handled
628 */
629 bs->vrf = vrf;
630 /* Skip enabled sessions. */
631 if (bs->sock != -1)
632 continue;
633 /* Try to enable it. */
634 bfd_session_enable(bs);
635 }
636 }
637
638 void bfdd_sessions_disable_vrf(struct vrf *vrf)
639 {
640 struct bfd_session_observer *bso;
641 struct bfd_session *bs;
642
643 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
644 if (bso->bso_isinterface)
645 continue;
646 bs = bso->bso_bs;
647 if (bs->key.vrfname[0] &&
648 strcmp(vrf->name, bs->key.vrfname))
649 continue;
650 /* Skip disabled sessions. */
651 if (bs->sock == -1)
652 continue;
653
654 /* Try to enable it. */
655 bfd_session_disable(bs);
656 }
657 }
658
659 static int bfdd_interface_update(ZAPI_CALLBACK_ARGS)
660 {
661 struct interface *ifp;
662
663 /*
664 * `zebra_interface_add_read` will handle the interface creation
665 * on `lib/if.c`. We'll use that data structure instead of
666 * rolling our own.
667 */
668 if (cmd == ZEBRA_INTERFACE_ADD) {
669 ifp = zebra_interface_add_read(zclient->ibuf, vrf_id);
670 if (ifp == NULL)
671 return 0;
672
673 bfdd_sessions_enable_interface(ifp);
674 return 0;
675 }
676
677 /* Update interface information. */
678 ifp = zebra_interface_state_read(zclient->ibuf, vrf_id);
679 if (ifp == NULL)
680 return 0;
681
682 bfdd_sessions_disable_interface(ifp);
683
684 if_set_index(ifp, IFINDEX_INTERNAL);
685
686 return 0;
687 }
688
689 static int bfdd_interface_vrf_update(ZAPI_CALLBACK_ARGS)
690 {
691 struct interface *ifp;
692 vrf_id_t nvrfid;
693
694 ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &nvrfid);
695 if (ifp == NULL)
696 return 0;
697
698 if_update_to_new_vrf(ifp, nvrfid);
699
700 return 0;
701 }
702
703 static void bfdd_sessions_enable_address(struct connected *ifc)
704 {
705 struct bfd_session_observer *bso;
706 struct bfd_session *bs;
707 struct prefix prefix;
708
709 TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
710 if (bso->bso_isaddress == false)
711 continue;
712
713 /* Skip enabled sessions. */
714 bs = bso->bso_bs;
715 if (bs->sock != -1)
716 continue;
717
718 /* Check address. */
719 prefix = bso->bso_addr;
720 prefix.prefixlen = ifc->address->prefixlen;
721 if (prefix_cmp(&prefix, ifc->address))
722 continue;
723
724 /* Try to enable it. */
725 bfd_session_enable(bs);
726 }
727 }
728
729 static int bfdd_interface_address_update(ZAPI_CALLBACK_ARGS)
730 {
731 struct connected *ifc;
732
733 ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
734 if (ifc == NULL)
735 return 0;
736
737 bfdd_sessions_enable_address(ifc);
738
739 return 0;
740 }
741
742 void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
743 {
744 zclient = zclient_new(master, &zclient_options_default);
745 assert(zclient != NULL);
746 zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
747
748 /*
749 * We'll receive all messages through replay, however it will
750 * contain a special field with the real command inside so we
751 * avoid having to create too many handlers.
752 */
753 zclient->bfd_dest_replay = bfdd_replay;
754
755 /* Send replay request on zebra connect. */
756 zclient->zebra_connected = bfdd_zebra_connected;
757
758 /* Learn interfaces from zebra instead of the OS. */
759 zclient->interface_add = bfdd_interface_update;
760 zclient->interface_delete = bfdd_interface_update;
761
762 /* Learn about interface VRF. */
763 zclient->interface_vrf_update = bfdd_interface_vrf_update;
764
765 /* Learn about new addresses being registered. */
766 zclient->interface_address_add = bfdd_interface_address_update;
767 zclient->interface_address_delete = bfdd_interface_address_update;
768 }
769
770 void bfdd_zclient_register(vrf_id_t vrf_id)
771 {
772 if (!zclient || zclient->sock < 0)
773 return;
774 zclient_send_reg_requests(zclient, vrf_id);
775 }
776
777 void bfdd_zclient_unregister(vrf_id_t vrf_id)
778 {
779 if (!zclient || zclient->sock < 0)
780 return;
781 zclient_send_dereg_requests(zclient, vrf_id);
782 }
783
784 void bfdd_zclient_stop(void)
785 {
786 zclient_stop(zclient);
787
788 /* Clean-up and free ptm clients data memory. */
789 pc_free_all();
790 }
791
792
793 /*
794 * Client handling.
795 */
796 static struct ptm_client *pc_lookup(uint32_t pid)
797 {
798 struct ptm_client *pc;
799
800 TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
801 if (pc->pc_pid != pid)
802 continue;
803
804 break;
805 }
806
807 return pc;
808 }
809
810 static struct ptm_client *pc_new(uint32_t pid)
811 {
812 struct ptm_client *pc;
813
814 /* Look up first, if not found create the client. */
815 pc = pc_lookup(pid);
816 if (pc != NULL)
817 return pc;
818
819 /* Allocate the client data and save it. */
820 pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
821
822 pc->pc_pid = pid;
823 TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
824 return pc;
825 }
826
827 static void pc_free(struct ptm_client *pc)
828 {
829 struct ptm_client_notification *pcn;
830
831 if (pc == NULL)
832 return;
833
834 TAILQ_REMOVE(&pcqueue, pc, pc_entry);
835
836 while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) {
837 pcn = TAILQ_FIRST(&pc->pc_pcnqueue);
838 pcn_free(pcn);
839 }
840
841 XFREE(MTYPE_BFDD_CONTROL, pc);
842 }
843
844 static void pc_free_all(void)
845 {
846 struct ptm_client *pc;
847
848 while (!TAILQ_EMPTY(&pcqueue)) {
849 pc = TAILQ_FIRST(&pcqueue);
850 pc_free(pc);
851 }
852 }
853
854 static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
855 struct bfd_session *bs)
856 {
857 struct ptm_client_notification *pcn;
858
859 /* Try to find an existing pcn fist. */
860 pcn = pcn_lookup(pc, bs);
861 if (pcn != NULL)
862 return pcn;
863
864 /* Save the client notification data. */
865 pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
866
867 TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
868 pcn->pcn_pc = pc;
869 pcn->pcn_bs = bs;
870 bs->refcount++;
871
872 return pcn;
873 }
874
875 static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
876 struct bfd_session *bs)
877 {
878 struct ptm_client_notification *pcn;
879
880 TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
881 if (pcn->pcn_bs != bs)
882 continue;
883
884 break;
885 }
886
887 return pcn;
888 }
889
890 static void pcn_free(struct ptm_client_notification *pcn)
891 {
892 struct ptm_client *pc;
893 struct bfd_session *bs;
894
895 if (pcn == NULL)
896 return;
897
898 /* Handle session de-registration. */
899 bs = pcn->pcn_bs;
900 pcn->pcn_bs = NULL;
901 bs->refcount--;
902
903 /* Handle ptm_client deregistration. */
904 pc = pcn->pcn_pc;
905 pcn->pcn_pc = NULL;
906 TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
907
908 XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
909 }