3 * Copyright (C) 2018 Network Device Education Foundation, Inc. ("NetDEF")
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
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.
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
23 #include "lib/libfrr.h"
24 #include "lib/queue.h"
25 #include "lib/stream.h"
26 #include "lib/zclient.h"
35 struct ptm_client_notification
{
36 struct bfd_session
*pcn_bs
;
37 struct ptm_client
*pcn_pc
;
39 TAILQ_ENTRY(ptm_client_notification
) pcn_entry
;
41 TAILQ_HEAD(pcnqueue
, ptm_client_notification
);
45 struct pcnqueue pc_pcnqueue
;
47 TAILQ_ENTRY(ptm_client
) pc_entry
;
49 TAILQ_HEAD(pcqueue
, ptm_client
);
51 static struct pcqueue pcqueue
;
52 static struct zclient
*zclient
;
58 static int _ptm_msg_address(struct stream
*msg
, int family
, const void *addr
);
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
,
62 struct bfd_peer_cfg
*bpc
, struct ptm_client
**pc
);
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
);
75 static void bfdd_dest_register(struct stream
*msg
);
76 static void bfdd_dest_deregister(struct stream
*msg
);
77 static void bfdd_client_register(struct stream
*msg
);
78 static void bfdd_client_deregister(struct stream
*msg
);
84 static void debug_printbpc(const char *func
, unsigned int line
,
85 struct bfd_peer_cfg
*bpc
);
87 static void debug_printbpc(const char *func
, unsigned int line
,
88 struct bfd_peer_cfg
*bpc
)
93 addr
[0][0] = addr
[1][0] = addr
[2][0] = timers
[0][0] = timers
[1][0] =
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
));
101 if (bpc
->bpc_has_localif
)
102 snprintf(addr
[2], sizeof(addr
[2]), " ifname:%s",
105 if (bpc
->bpc_has_vrfname
)
106 snprintf(addr
[2], sizeof(addr
[2]), " vrf:%s", bpc
->bpc_vrfname
);
108 if (bpc
->bpc_has_recvinterval
)
109 snprintf(timers
[0], sizeof(timers
[0]), " rx:%lu",
110 bpc
->bpc_recvinterval
);
112 if (bpc
->bpc_has_txinterval
)
113 snprintf(timers
[1], sizeof(timers
[1]), " tx:%lu",
114 bpc
->bpc_recvinterval
);
116 if (bpc
->bpc_has_detectmultiplier
)
117 snprintf(timers
[2], sizeof(timers
[2]), " detect-multiplier:%d",
118 bpc
->bpc_detectmultiplier
);
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]);
125 #define DEBUG_PRINTBPC(bpc) debug_printbpc(__FILE__, __LINE__, (bpc))
127 #define DEBUG_PRINTBPC(bpc)
128 #endif /* BFD_DEBUG */
130 static int _ptm_msg_address(struct stream
*msg
, int family
, const void *addr
)
132 stream_putc(msg
, family
);
136 stream_put(msg
, addr
, sizeof(struct in_addr
));
137 stream_putc(msg
, 32);
141 stream_put(msg
, addr
, sizeof(struct in6_addr
));
142 stream_putc(msg
, 128);
153 int ptm_bfd_notify(struct bfd_session
*bs
)
157 bs
->stats
.znotification
++;
161 * - header: command, vrf
162 * - l: interface index
177 * Commands: ZEBRA_BFD_DEST_REPLAY
179 * q(64), l(32), w(16), c(8)
184 /* TODO: VRF handling */
185 zclient_create_header(msg
, ZEBRA_BFD_DEST_REPLAY
, VRF_DEFAULT
);
187 /* This header will be handled by `zebra_ptm.c`. */
188 stream_putl(msg
, ZEBRA_INTERFACE_BFD_DEST_UPDATE
);
190 /* NOTE: Interface is a shortcut to avoid comparing source address. */
192 stream_putl(msg
, bs
->ifp
->ifindex
);
194 stream_putl(msg
, IFINDEX_INTERNAL
);
196 /* BFD destination prefix information. */
197 _ptm_msg_address(msg
, bs
->key
.family
, &bs
->key
.peer
);
200 switch (bs
->ses_state
) {
202 stream_putl(msg
, BFD_STATUS_UP
);
205 case PTM_BFD_ADM_DOWN
:
208 stream_putl(msg
, BFD_STATUS_DOWN
);
212 stream_putl(msg
, BFD_STATUS_UNKNOWN
);
216 /* BFD source prefix information. */
217 _ptm_msg_address(msg
, bs
->key
.family
, &bs
->key
.local
);
219 /* Write packet size. */
220 stream_putw_at(msg
, 0, stream_get_endp(msg
));
222 return zclient_send_message(zclient
);
225 static void _ptm_msg_read_address(struct stream
*msg
, struct sockaddr_any
*sa
)
229 STREAM_GETW(msg
, family
);
233 sa
->sa_sin
.sin_family
= family
;
234 STREAM_GET(&sa
->sa_sin
.sin_addr
, msg
,
235 sizeof(sa
->sa_sin
.sin_addr
));
236 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
237 sa
->sa_sin
.sin_len
= sizeof(sa
->sa_sin
);
238 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
242 sa
->sa_sin6
.sin6_family
= family
;
243 STREAM_GET(&sa
->sa_sin6
.sin6_addr
, msg
,
244 sizeof(sa
->sa_sin6
.sin6_addr
));
245 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
246 sa
->sa_sin6
.sin6_len
= sizeof(sa
->sa_sin6
);
247 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
251 log_warning("ptm-read-address: invalid family: %d", family
);
256 memset(sa
, 0, sizeof(*sa
));
259 static int _ptm_msg_read(struct stream
*msg
, int command
,
260 struct bfd_peer_cfg
*bpc
, struct ptm_client
**pc
)
263 uint8_t ttl
__attribute__((unused
));
267 * Register/Deregister/Update Message format:
268 * - header: Command, VRF
272 * - l: destination ipv4
274 * - 16 bytes: destination IPv6
275 * - command != ZEBRA_BFD_DEST_DEREGISTER
278 * - c: detect multiplier
283 * - l: destination ipv4
285 * - 16 bytes: destination IPv6
290 * - 16 bytes: ipv6 address
292 * - X bytes: interface name
294 * q(64), l(32), w(16), c(8)
297 /* Initialize parameters return values. */
298 memset(bpc
, 0, sizeof(*bpc
));
301 /* Find or allocate process context data. */
302 STREAM_GETL(msg
, pid
);
306 log_debug("ptm-read: failed to allocate memory");
310 /* Register/update peer information. */
311 _ptm_msg_read_address(msg
, &bpc
->bpc_peer
);
313 /* Determine IP type from peer destination. */
314 bpc
->bpc_ipv4
= (bpc
->bpc_peer
.sa_sin
.sin_family
== AF_INET
);
316 /* Get peer configuration. */
317 if (command
!= ZEBRA_BFD_DEST_DEREGISTER
) {
318 STREAM_GETL(msg
, bpc
->bpc_recvinterval
);
319 bpc
->bpc_has_recvinterval
=
320 (bpc
->bpc_recvinterval
!= BPC_DEF_RECEIVEINTERVAL
);
322 STREAM_GETL(msg
, bpc
->bpc_txinterval
);
323 bpc
->bpc_has_txinterval
=
324 (bpc
->bpc_txinterval
!= BPC_DEF_TRANSMITINTERVAL
);
326 STREAM_GETC(msg
, bpc
->bpc_detectmultiplier
);
327 bpc
->bpc_has_detectmultiplier
=
328 (bpc
->bpc_detectmultiplier
!= BPC_DEF_DETECTMULTIPLIER
);
331 /* Read (single|multi)hop and its options. */
332 STREAM_GETC(msg
, bpc
->bpc_mhop
);
334 /* Read multihop source address and TTL. */
335 _ptm_msg_read_address(msg
, &bpc
->bpc_local
);
336 STREAM_GETC(msg
, ttl
);
338 /* If target is IPv6, then we must obtain local address. */
339 if (bpc
->bpc_ipv4
== false)
340 _ptm_msg_read_address(msg
, &bpc
->bpc_local
);
343 * Read interface name and make sure it fits our data
344 * structure, otherwise fail.
346 STREAM_GETC(msg
, ifnamelen
);
347 if (ifnamelen
>= sizeof(bpc
->bpc_localif
)) {
348 log_error("ptm-read: interface name is too big");
352 bpc
->bpc_has_localif
= ifnamelen
> 0;
353 if (bpc
->bpc_has_localif
) {
354 STREAM_GET(bpc
->bpc_localif
, msg
, ifnamelen
);
355 bpc
->bpc_localif
[ifnamelen
] = 0;
359 /* Sanity check: peer and local address must match IP types. */
360 if (bpc
->bpc_local
.sa_sin
.sin_family
!= 0
361 && (bpc
->bpc_local
.sa_sin
.sin_family
362 != bpc
->bpc_peer
.sa_sin
.sin_family
)) {
363 log_warning("ptm-read: peer family doesn't match local type");
373 static void bfdd_dest_register(struct stream
*msg
)
375 struct ptm_client
*pc
;
376 struct ptm_client_notification
*pcn
;
377 struct bfd_session
*bs
;
378 struct bfd_peer_cfg bpc
;
380 /* Read the client context and peer data. */
381 if (_ptm_msg_read(msg
, ZEBRA_BFD_DEST_REGISTER
, &bpc
, &pc
) == -1)
384 DEBUG_PRINTBPC(&bpc
);
386 /* Find or start new BFD session. */
387 bs
= bs_peer_find(&bpc
);
389 bs
= ptm_bfd_sess_new(&bpc
);
391 log_debug("ptm-add-dest: failed to create BFD session");
395 /* Don't try to change echo/shutdown state. */
396 bpc
.bpc_echo
= BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
398 BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
401 /* Create client peer notification register. */
402 pcn
= pcn_new(pc
, bs
);
404 log_error("ptm-add-dest: failed to registrate notifications");
411 static void bfdd_dest_deregister(struct stream
*msg
)
413 struct ptm_client
*pc
;
414 struct ptm_client_notification
*pcn
;
415 struct bfd_session
*bs
;
416 struct bfd_peer_cfg bpc
;
418 /* Read the client context and peer data. */
419 if (_ptm_msg_read(msg
, ZEBRA_BFD_DEST_DEREGISTER
, &bpc
, &pc
) == -1)
422 DEBUG_PRINTBPC(&bpc
);
424 /* Find or start new BFD session. */
425 bs
= bs_peer_find(&bpc
);
427 log_debug("ptm-del-dest: failed to find BFD session");
431 /* Unregister client peer notification. */
432 pcn
= pcn_lookup(pc
, bs
);
435 BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_CONFIG
))
437 ptm_bfd_ses_del(&bpc
);
441 * header: command, VRF
444 static void bfdd_client_register(struct stream
*msg
)
446 struct ptm_client
*pc
;
449 /* Find or allocate process context data. */
450 STREAM_GETL(msg
, pid
);
454 log_error("ptm-add-client: failed to register client: %u", pid
);
461 log_error("ptm-add-client: failed to register client");
465 * header: command, VRF
468 static void bfdd_client_deregister(struct stream
*msg
)
470 struct ptm_client
*pc
;
473 /* Find or allocate process context data. */
474 STREAM_GETL(msg
, pid
);
478 log_debug("ptm-del-client: failed to find client: %u", pid
);
487 log_error("ptm-del-client: failed to deregister client");
490 static int bfdd_replay(int cmd
, struct zclient
*zc
, uint16_t len
, vrf_id_t vid
)
492 struct stream
*msg
= zc
->ibuf
;
495 STREAM_GETL(msg
, rcmd
);
498 case ZEBRA_BFD_DEST_REGISTER
:
499 case ZEBRA_BFD_DEST_UPDATE
:
500 bfdd_dest_register(msg
);
502 case ZEBRA_BFD_DEST_DEREGISTER
:
503 bfdd_dest_deregister(msg
);
505 case ZEBRA_BFD_CLIENT_REGISTER
:
506 bfdd_client_register(msg
);
508 case ZEBRA_BFD_CLIENT_DEREGISTER
:
509 bfdd_client_deregister(msg
);
513 log_debug("ptm-replay: invalid message type %u", rcmd
);
520 log_error("ptm-replay: failed to find command");
524 static void bfdd_zebra_connected(struct zclient
*zc
)
526 struct stream
*msg
= zc
->obuf
;
528 /* Clean-up and free ptm clients data memory. */
532 * The replay is an empty message just to trigger client daemons
533 * configuration replay.
536 zclient_create_header(msg
, ZEBRA_BFD_DEST_REPLAY
, VRF_DEFAULT
);
537 stream_putl(msg
, ZEBRA_BFD_DEST_REPLAY
);
538 stream_putw_at(msg
, 0, stream_get_endp(msg
));
540 /* Ask for interfaces information. */
541 zclient_create_header(msg
, ZEBRA_INTERFACE_ADD
, VRF_DEFAULT
);
544 zclient_send_message(zclient
);
547 static void bfdd_sessions_enable_interface(struct interface
*ifp
)
549 struct bfd_session_observer
*bso
;
550 struct bfd_session
*bs
;
552 TAILQ_FOREACH(bso
, &bglobal
.bg_obslist
, bso_entry
) {
553 if (bso
->bso_isinterface
== false)
556 /* Interface name mismatch. */
558 if (strcmp(ifp
->name
, bs
->key
.ifname
))
560 /* Skip enabled sessions. */
564 /* Try to enable it. */
565 bfd_session_enable(bs
);
569 static void bfdd_sessions_disable_interface(struct interface
*ifp
)
571 struct bfd_session_observer
*bso
;
572 struct bfd_session
*bs
;
574 TAILQ_FOREACH(bso
, &bglobal
.bg_obslist
, bso_entry
) {
575 if (bso
->bso_isinterface
== false)
578 /* Interface name mismatch. */
580 if (strcmp(ifp
->name
, bs
->key
.ifname
))
582 /* Skip disabled sessions. */
586 /* Try to enable it. */
587 bfd_session_disable(bs
);
589 TAILQ_INSERT_HEAD(&bglobal
.bg_obslist
, bso
, bso_entry
);
593 static int bfdd_interface_update(int cmd
, struct zclient
*zc
,
594 uint16_t len
__attribute__((__unused__
)),
597 struct interface
*ifp
;
600 * `zebra_interface_add_read` will handle the interface creation
601 * on `lib/if.c`. We'll use that data structure instead of
604 if (cmd
== ZEBRA_INTERFACE_ADD
) {
605 ifp
= zebra_interface_add_read(zc
->ibuf
, vrfid
);
609 bfdd_sessions_enable_interface(ifp
);
613 /* Update interface information. */
614 ifp
= zebra_interface_state_read(zc
->ibuf
, vrfid
);
618 bfdd_sessions_disable_interface(ifp
);
620 if_set_index(ifp
, IFINDEX_INTERNAL
);
625 static int bfdd_interface_vrf_update(int command
__attribute__((__unused__
)),
626 struct zclient
*zclient
,
628 __attribute__((__unused__
)),
631 struct interface
*ifp
;
634 ifp
= zebra_interface_vrf_update_read(zclient
->ibuf
, vrfid
, &nvrfid
);
638 if_update_to_new_vrf(ifp
, nvrfid
);
643 static void bfdd_sessions_enable_address(struct connected
*ifc
)
645 struct bfd_session_observer
*bso
;
646 struct bfd_session
*bs
;
647 struct prefix prefix
;
649 TAILQ_FOREACH(bso
, &bglobal
.bg_obslist
, bso_entry
) {
650 if (bso
->bso_isaddress
== false)
653 /* Skip enabled sessions. */
659 prefix
= bso
->bso_addr
;
660 prefix
.prefixlen
= ifc
->address
->prefixlen
;
661 if (prefix_cmp(&prefix
, ifc
->address
))
664 /* Try to enable it. */
665 bfd_session_enable(bs
);
669 static int bfdd_interface_address_update(int cmd
, struct zclient
*zc
,
671 __attribute__((__unused__
)),
674 struct connected
*ifc
;
676 ifc
= zebra_interface_address_read(cmd
, zc
->ibuf
, vrfid
);
680 bfdd_sessions_enable_address(ifc
);
685 void bfdd_zclient_init(struct zebra_privs_t
*bfdd_priv
)
687 zclient
= zclient_new(master
, &zclient_options_default
);
688 assert(zclient
!= NULL
);
689 zclient_init(zclient
, ZEBRA_ROUTE_BFD
, 0, bfdd_priv
);
692 * We'll receive all messages through replay, however it will
693 * contain a special field with the real command inside so we
694 * avoid having to create too many handlers.
696 zclient
->bfd_dest_replay
= bfdd_replay
;
698 /* Send replay request on zebra connect. */
699 zclient
->zebra_connected
= bfdd_zebra_connected
;
701 /* Learn interfaces from zebra instead of the OS. */
702 zclient
->interface_add
= bfdd_interface_update
;
703 zclient
->interface_delete
= bfdd_interface_update
;
705 /* Learn about interface VRF. */
706 zclient
->interface_vrf_update
= bfdd_interface_vrf_update
;
708 /* Learn about new addresses being registered. */
709 zclient
->interface_address_add
= bfdd_interface_address_update
;
710 zclient
->interface_address_delete
= bfdd_interface_address_update
;
713 void bfdd_zclient_stop(void)
715 zclient_stop(zclient
);
717 /* Clean-up and free ptm clients data memory. */
725 static struct ptm_client
*pc_lookup(uint32_t pid
)
727 struct ptm_client
*pc
;
729 TAILQ_FOREACH (pc
, &pcqueue
, pc_entry
) {
730 if (pc
->pc_pid
!= pid
)
739 static struct ptm_client
*pc_new(uint32_t pid
)
741 struct ptm_client
*pc
;
743 /* Look up first, if not found create the client. */
748 /* Allocate the client data and save it. */
749 pc
= XCALLOC(MTYPE_BFDD_CONTROL
, sizeof(*pc
));
752 TAILQ_INSERT_HEAD(&pcqueue
, pc
, pc_entry
);
756 static void pc_free(struct ptm_client
*pc
)
758 struct ptm_client_notification
*pcn
;
763 TAILQ_REMOVE(&pcqueue
, pc
, pc_entry
);
765 while (!TAILQ_EMPTY(&pc
->pc_pcnqueue
)) {
766 pcn
= TAILQ_FIRST(&pc
->pc_pcnqueue
);
770 XFREE(MTYPE_BFDD_CONTROL
, pc
);
773 static void pc_free_all(void)
775 struct ptm_client
*pc
;
777 while (!TAILQ_EMPTY(&pcqueue
)) {
778 pc
= TAILQ_FIRST(&pcqueue
);
783 static struct ptm_client_notification
*pcn_new(struct ptm_client
*pc
,
784 struct bfd_session
*bs
)
786 struct ptm_client_notification
*pcn
;
788 /* Try to find an existing pcn fist. */
789 pcn
= pcn_lookup(pc
, bs
);
793 /* Save the client notification data. */
794 pcn
= XCALLOC(MTYPE_BFDD_NOTIFICATION
, sizeof(*pcn
));
796 TAILQ_INSERT_HEAD(&pc
->pc_pcnqueue
, pcn
, pcn_entry
);
804 static struct ptm_client_notification
*pcn_lookup(struct ptm_client
*pc
,
805 struct bfd_session
*bs
)
807 struct ptm_client_notification
*pcn
;
809 TAILQ_FOREACH (pcn
, &pc
->pc_pcnqueue
, pcn_entry
) {
810 if (pcn
->pcn_bs
!= bs
)
819 static void pcn_free(struct ptm_client_notification
*pcn
)
821 struct ptm_client
*pc
;
822 struct bfd_session
*bs
;
827 /* Handle session de-registration. */
832 /* Handle ptm_client deregistration. */
835 TAILQ_REMOVE(&pc
->pc_pcnqueue
, pcn
, pcn_entry
);
837 XFREE(MTYPE_BFDD_NOTIFICATION
, pcn
);