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