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
, struct sockaddr_any
*sa
);
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
, struct sockaddr_any
*sa
)
132 switch (sa
->sa_sin
.sin_family
) {
134 stream_putc(msg
, sa
->sa_sin
.sin_family
);
135 stream_put_in_addr(msg
, &sa
->sa_sin
.sin_addr
);
136 stream_putc(msg
, 32);
140 stream_putc(msg
, sa
->sa_sin6
.sin6_family
);
141 stream_put(msg
, &sa
->sa_sin6
.sin6_addr
,
142 sizeof(sa
->sa_sin6
.sin6_addr
));
143 stream_putc(msg
, 128);
153 int ptm_bfd_notify(struct bfd_session
*bs
)
156 struct sockaddr_any sac
;
160 * - header: command, vrf
161 * - l: interface index
176 * Commands: ZEBRA_BFD_DEST_REPLAY
178 * q(64), l(32), w(16), c(8)
183 /* TODO: VRF handling */
184 zclient_create_header(msg
, ZEBRA_BFD_DEST_REPLAY
, VRF_DEFAULT
);
186 /* This header will be handled by `zebra_ptm.c`. */
187 stream_putl(msg
, ZEBRA_INTERFACE_BFD_DEST_UPDATE
);
189 /* NOTE: Interface is a shortcut to avoid comparing source address. */
190 stream_putl(msg
, bs
->ifindex
);
192 /* BFD destination prefix information. */
193 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
))
194 _ptm_msg_address(msg
, &bs
->mhop
.peer
);
196 _ptm_msg_address(msg
, &bs
->shop
.peer
);
199 switch (bs
->ses_state
) {
201 stream_putl(msg
, BFD_STATUS_UP
);
204 case PTM_BFD_ADM_DOWN
:
207 stream_putl(msg
, BFD_STATUS_DOWN
);
211 stream_putl(msg
, BFD_STATUS_UNKNOWN
);
215 /* BFD source prefix information. */
216 if (BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_MH
)) {
217 _ptm_msg_address(msg
, &bs
->mhop
.local
);
219 if (bs
->local_address
.sa_sin
.sin_family
)
220 _ptm_msg_address(msg
, &bs
->local_address
);
221 else if (bs
->local_address
.sa_sin
.sin_family
)
222 _ptm_msg_address(msg
, &bs
->local_ip
);
225 switch (sac
.sa_sin
.sin_family
) {
227 memset(&sac
.sa_sin
.sin_addr
, 0,
228 sizeof(sac
.sa_sin
.sin_family
));
231 memset(&sac
.sa_sin6
.sin6_addr
, 0,
232 sizeof(sac
.sa_sin6
.sin6_family
));
240 /* No local address found yet, so send zeroes. */
241 _ptm_msg_address(msg
, &sac
);
245 /* Write packet size. */
246 stream_putw_at(msg
, 0, stream_get_endp(msg
));
248 return zclient_send_message(zclient
);
251 static void _ptm_msg_read_address(struct stream
*msg
, struct sockaddr_any
*sa
)
255 STREAM_GETW(msg
, family
);
259 sa
->sa_sin
.sin_family
= family
;
260 STREAM_GET(&sa
->sa_sin
.sin_addr
, msg
,
261 sizeof(sa
->sa_sin
.sin_addr
));
262 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
263 sa
->sa_sin
.sin_len
= sizeof(sa
->sa_sin
);
264 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
268 sa
->sa_sin6
.sin6_family
= family
;
269 STREAM_GET(&sa
->sa_sin6
.sin6_addr
, msg
,
270 sizeof(sa
->sa_sin6
.sin6_addr
));
271 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
272 sa
->sa_sin6
.sin6_len
= sizeof(sa
->sa_sin6
);
273 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
277 log_warning("%s: invalid family: %d", __func__
, family
);
282 memset(sa
, 0, sizeof(*sa
));
285 static int _ptm_msg_read(struct stream
*msg
, int command
,
286 struct bfd_peer_cfg
*bpc
, struct ptm_client
**pc
)
293 * Register/Deregister/Update Message format:
294 * - header: Command, VRF
298 * - l: destination ipv4
300 * - 16 bytes: destination IPv6
301 * - command != ZEBRA_BFD_DEST_DEREGISTER
304 * - c: detect multiplier
309 * - l: destination ipv4
311 * - 16 bytes: destination IPv6
316 * - 16 bytes: ipv6 address
318 * - X bytes: interface name
320 * q(64), l(32), w(16), c(8)
323 /* Initialize parameters return values. */
324 memset(bpc
, 0, sizeof(*bpc
));
327 /* Find or allocate process context data. */
328 STREAM_GETL(msg
, pid
);
332 log_debug("%s: failed to allocate memory", __func__
);
336 /* Register/update peer information. */
337 _ptm_msg_read_address(msg
, &bpc
->bpc_peer
);
339 /* Determine IP type from peer destination. */
340 bpc
->bpc_ipv4
= (bpc
->bpc_peer
.sa_sin
.sin_family
== AF_INET
);
342 /* Get peer configuration. */
343 if (command
!= ZEBRA_BFD_DEST_DEREGISTER
) {
344 STREAM_GETL(msg
, bpc
->bpc_recvinterval
);
345 bpc
->bpc_has_recvinterval
=
346 (bpc
->bpc_recvinterval
!= BPC_DEF_RECEIVEINTERVAL
);
348 STREAM_GETL(msg
, bpc
->bpc_txinterval
);
349 bpc
->bpc_has_txinterval
=
350 (bpc
->bpc_txinterval
!= BPC_DEF_TRANSMITINTERVAL
);
352 STREAM_GETC(msg
, bpc
->bpc_detectmultiplier
);
353 bpc
->bpc_has_detectmultiplier
=
354 (bpc
->bpc_detectmultiplier
!= BPC_DEF_DETECTMULTIPLIER
);
357 /* Read (single|multi)hop and its options. */
358 STREAM_GETC(msg
, bpc
->bpc_mhop
);
360 /* Read multihop source address and TTL. */
361 _ptm_msg_read_address(msg
, &bpc
->bpc_local
);
362 STREAM_GETC(msg
, ttl
);
365 * TODO: use TTL for something. The line below removes
366 * an unused variable compiler warning.
370 /* If target is IPv6, then we must obtain local address. */
371 if (bpc
->bpc_ipv4
== false)
372 _ptm_msg_read_address(msg
, &bpc
->bpc_local
);
375 * Read interface name and make sure it fits our data
376 * structure, otherwise fail.
378 STREAM_GETC(msg
, ifnamelen
);
379 if (ifnamelen
> sizeof(bpc
->bpc_localif
)) {
380 log_error("%s: interface name is too big", __func__
);
384 bpc
->bpc_has_localif
= ifnamelen
> 0;
385 if (bpc
->bpc_has_localif
) {
386 STREAM_GET(bpc
->bpc_localif
, msg
, ifnamelen
);
387 bpc
->bpc_localif
[ifnamelen
] = 0;
391 /* Sanity check: peer and local address must match IP types. */
392 if (bpc
->bpc_local
.sa_sin
.sin_family
!= 0
393 && (bpc
->bpc_local
.sa_sin
.sin_family
394 != bpc
->bpc_peer
.sa_sin
.sin_family
)) {
395 log_warning("%s: peer family doesn't match local type",
406 static void bfdd_dest_register(struct stream
*msg
)
408 struct ptm_client
*pc
;
409 struct ptm_client_notification
*pcn
;
410 struct bfd_session
*bs
;
411 struct bfd_peer_cfg bpc
;
413 /* Read the client context and peer data. */
414 if (_ptm_msg_read(msg
, ZEBRA_BFD_DEST_REGISTER
, &bpc
, &pc
) == -1)
417 DEBUG_PRINTBPC(&bpc
);
419 /* Find or start new BFD session. */
420 bs
= bs_peer_find(&bpc
);
422 bs
= ptm_bfd_sess_new(&bpc
);
424 log_debug("%s: failed to create BFD session", __func__
);
428 /* Don't try to change echo/shutdown state. */
429 bpc
.bpc_echo
= BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_ECHO
);
431 BFD_CHECK_FLAG(bs
->flags
, BFD_SESS_FLAG_SHUTDOWN
);
434 /* Create client peer notification register. */
435 pcn
= pcn_new(pc
, bs
);
437 log_error("%s: failed to registrate notifications", __func__
);
444 static void bfdd_dest_deregister(struct stream
*msg
)
446 struct ptm_client
*pc
;
447 struct ptm_client_notification
*pcn
;
448 struct bfd_session
*bs
;
449 struct bfd_peer_cfg bpc
;
451 /* Read the client context and peer data. */
452 if (_ptm_msg_read(msg
, ZEBRA_BFD_DEST_DEREGISTER
, &bpc
, &pc
) == -1)
455 DEBUG_PRINTBPC(&bpc
);
457 /* Find or start new BFD session. */
458 bs
= bs_peer_find(&bpc
);
460 log_debug("%s: failed to create BFD session", __func__
);
464 /* Unregister client peer notification. */
465 pcn
= pcn_lookup(pc
, bs
);
470 * header: command, VRF
473 static void bfdd_client_register(struct stream
*msg
)
475 struct ptm_client
*pc
;
478 /* Find or allocate process context data. */
479 STREAM_GETL(msg
, pid
);
483 log_error("%s: failed to register client: %u", __func__
, pid
);
490 log_error("%s: failed to register client", __func__
);
494 * header: command, VRF
497 static void bfdd_client_deregister(struct stream
*msg
)
499 struct ptm_client
*pc
;
502 /* Find or allocate process context data. */
503 STREAM_GETL(msg
, pid
);
507 log_debug("%s: failed to find client: %u", __func__
, pid
);
516 log_error("%s: failed to deregister client", __func__
);
519 static int bfdd_replay(int cmd
, struct zclient
*zc
, uint16_t len
, vrf_id_t vid
)
521 struct stream
*msg
= zc
->ibuf
;
524 STREAM_GETL(msg
, rcmd
);
527 case ZEBRA_BFD_DEST_REGISTER
:
528 case ZEBRA_BFD_DEST_UPDATE
:
529 bfdd_dest_register(msg
);
531 case ZEBRA_BFD_DEST_DEREGISTER
:
532 bfdd_dest_deregister(msg
);
534 case ZEBRA_BFD_CLIENT_REGISTER
:
535 bfdd_client_register(msg
);
537 case ZEBRA_BFD_CLIENT_DEREGISTER
:
538 bfdd_client_deregister(msg
);
542 log_debug("%s: invalid message type %u", __func__
, rcmd
);
549 log_error("%s: failed to find command", __func__
);
553 static void bfdd_zebra_connected(struct zclient
*zc
)
555 struct stream
*msg
= zc
->obuf
;
557 /* Clean-up and free ptm clients data memory. */
561 * The replay is an empty message just to trigger client daemons
562 * configuration replay.
565 zclient_create_header(msg
, ZEBRA_BFD_DEST_REPLAY
, VRF_DEFAULT
);
566 stream_putl(msg
, ZEBRA_BFD_DEST_REPLAY
);
567 stream_putw_at(msg
, 0, stream_get_endp(msg
));
569 zclient_send_message(zclient
);
572 void bfdd_zclient_init(struct zebra_privs_t
*bfdd_priv
)
574 zclient
= zclient_new_notify(master
, &zclient_options_default
);
575 assert(zclient
!= NULL
);
576 zclient_init(zclient
, ZEBRA_ROUTE_BFD
, 0, bfdd_priv
);
579 * We'll receive all messages through replay, however it will
580 * contain a special field with the real command inside so we
581 * avoid having to create too many handlers.
583 zclient
->bfd_dest_replay
= bfdd_replay
;
585 /* Send replay request on zebra connect. */
586 zclient
->zebra_connected
= bfdd_zebra_connected
;
589 void bfdd_zclient_stop(void)
591 zclient_stop(zclient
);
593 /* Clean-up and free ptm clients data memory. */
601 static struct ptm_client
*pc_lookup(uint32_t pid
)
603 struct ptm_client
*pc
;
605 TAILQ_FOREACH (pc
, &pcqueue
, pc_entry
) {
606 if (pc
->pc_pid
!= pid
)
615 static struct ptm_client
*pc_new(uint32_t pid
)
617 struct ptm_client
*pc
;
619 /* Look up first, if not found create the client. */
624 /* Allocate the client data and save it. */
625 pc
= XCALLOC(MTYPE_BFDD_CONTROL
, sizeof(*pc
));
630 TAILQ_INSERT_HEAD(&pcqueue
, pc
, pc_entry
);
634 static void pc_free(struct ptm_client
*pc
)
636 struct ptm_client_notification
*pcn
;
641 TAILQ_REMOVE(&pcqueue
, pc
, pc_entry
);
643 while (!TAILQ_EMPTY(&pc
->pc_pcnqueue
)) {
644 pcn
= TAILQ_FIRST(&pc
->pc_pcnqueue
);
648 XFREE(MTYPE_BFDD_CONTROL
, pc
);
651 static void pc_free_all(void)
653 struct ptm_client
*pc
;
655 while (!TAILQ_EMPTY(&pcqueue
)) {
656 pc
= TAILQ_FIRST(&pcqueue
);
661 static struct ptm_client_notification
*pcn_new(struct ptm_client
*pc
,
662 struct bfd_session
*bs
)
664 struct ptm_client_notification
*pcn
;
666 /* Try to find an existing pcn fist. */
667 pcn
= pcn_lookup(pc
, bs
);
671 /* Save the client notification data. */
672 pcn
= XCALLOC(MTYPE_BFDD_NOTIFICATION
, sizeof(*pcn
));
676 TAILQ_INSERT_HEAD(&pc
->pc_pcnqueue
, pcn
, pcn_entry
);
684 static struct ptm_client_notification
*pcn_lookup(struct ptm_client
*pc
,
685 struct bfd_session
*bs
)
687 struct ptm_client_notification
*pcn
;
689 TAILQ_FOREACH (pcn
, &pc
->pc_pcnqueue
, pcn_entry
) {
690 if (pcn
->pcn_bs
!= bs
)
699 static void pcn_free(struct ptm_client_notification
*pcn
)
701 struct ptm_client
*pc
;
702 struct bfd_session
*bs
;
707 /* Handle session de-registration. */
712 /* Handle ptm_client deregistration. */
715 TAILQ_REMOVE(&pc
->pc_pcnqueue
, pcn
, pcn_entry
);
717 XFREE(MTYPE_BFDD_NOTIFICATION
, pcn
);