]> 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 /*
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, struct sockaddr_any *sa);
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,
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);
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);
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, struct sockaddr_any *sa)
131 {
132 switch (sa->sa_sin.sin_family) {
133 case AF_INET:
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);
137 break;
138
139 case AF_INET6:
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);
144 break;
145
146 default:
147 return -1;
148 }
149
150 return 0;
151 }
152
153 int ptm_bfd_notify(struct bfd_session *bs)
154 {
155 struct stream *msg;
156 struct sockaddr_any sac;
157
158 bs->stats.znotification++;
159
160 /*
161 * Message format:
162 * - header: command, vrf
163 * - l: interface index
164 * - c: family
165 * - AF_INET:
166 * - 4 bytes: ipv4
167 * - AF_INET6:
168 * - 16 bytes: ipv6
169 * - c: prefix length
170 * - l: bfd status
171 * - c: family
172 * - AF_INET:
173 * - 4 bytes: ipv4
174 * - AF_INET6:
175 * - 16 bytes: ipv6
176 * - c: prefix length
177 *
178 * Commands: ZEBRA_BFD_DEST_REPLAY
179 *
180 * q(64), l(32), w(16), c(8)
181 */
182 msg = zclient->obuf;
183 stream_reset(msg);
184
185 /* TODO: VRF handling */
186 zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
187
188 /* This header will be handled by `zebra_ptm.c`. */
189 stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE);
190
191 /* NOTE: Interface is a shortcut to avoid comparing source address. */
192 stream_putl(msg, bs->ifindex);
193
194 /* BFD destination prefix information. */
195 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
196 _ptm_msg_address(msg, &bs->mhop.peer);
197 else
198 _ptm_msg_address(msg, &bs->shop.peer);
199
200 /* BFD status */
201 switch (bs->ses_state) {
202 case PTM_BFD_UP:
203 stream_putl(msg, BFD_STATUS_UP);
204 break;
205
206 case PTM_BFD_ADM_DOWN:
207 case PTM_BFD_DOWN:
208 case PTM_BFD_INIT:
209 stream_putl(msg, BFD_STATUS_DOWN);
210 break;
211
212 default:
213 stream_putl(msg, BFD_STATUS_UNKNOWN);
214 break;
215 }
216
217 /* BFD source prefix information. */
218 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
219 _ptm_msg_address(msg, &bs->mhop.local);
220 } else {
221 if (bs->local_address.sa_sin.sin_family)
222 _ptm_msg_address(msg, &bs->local_address);
223 else if (bs->local_address.sa_sin.sin_family)
224 _ptm_msg_address(msg, &bs->local_ip);
225 else {
226 sac = bs->shop.peer;
227 switch (sac.sa_sin.sin_family) {
228 case AF_INET:
229 memset(&sac.sa_sin.sin_addr, 0,
230 sizeof(sac.sa_sin.sin_family));
231 break;
232 case AF_INET6:
233 memset(&sac.sa_sin6.sin6_addr, 0,
234 sizeof(sac.sa_sin6.sin6_family));
235 break;
236
237 default:
238 assert(false);
239 break;
240 }
241
242 /* No local address found yet, so send zeroes. */
243 _ptm_msg_address(msg, &sac);
244 }
245 }
246
247 /* Write packet size. */
248 stream_putw_at(msg, 0, stream_get_endp(msg));
249
250 return zclient_send_message(zclient);
251 }
252
253 static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa)
254 {
255 uint16_t family;
256
257 STREAM_GETW(msg, family);
258
259 switch (family) {
260 case AF_INET:
261 sa->sa_sin.sin_family = family;
262 STREAM_GET(&sa->sa_sin.sin_addr, msg,
263 sizeof(sa->sa_sin.sin_addr));
264 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
265 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
266 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
267 return;
268
269 case AF_INET6:
270 sa->sa_sin6.sin6_family = family;
271 STREAM_GET(&sa->sa_sin6.sin6_addr, msg,
272 sizeof(sa->sa_sin6.sin6_addr));
273 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
274 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
275 #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
276 return;
277
278 default:
279 log_warning("ptm-read-address: invalid family: %d", family);
280 break;
281 }
282
283 stream_failure:
284 memset(sa, 0, sizeof(*sa));
285 }
286
287 static int _ptm_msg_read(struct stream *msg, int command,
288 struct bfd_peer_cfg *bpc, struct ptm_client **pc)
289 {
290 uint32_t pid;
291 uint8_t ttl __attribute__((unused));
292 size_t ifnamelen;
293
294 /*
295 * Register/Deregister/Update Message format:
296 * - header: Command, VRF
297 * - l: pid
298 * - w: family
299 * - AF_INET:
300 * - l: destination ipv4
301 * - AF_INET6:
302 * - 16 bytes: destination IPv6
303 * - command != ZEBRA_BFD_DEST_DEREGISTER
304 * - l: min_rx
305 * - l: min_tx
306 * - c: detect multiplier
307 * - c: is_multihop?
308 * - multihop:
309 * - w: family
310 * - AF_INET:
311 * - l: destination ipv4
312 * - AF_INET6:
313 * - 16 bytes: destination IPv6
314 * - c: ttl
315 * - no multihop
316 * - AF_INET6:
317 * - w: family
318 * - 16 bytes: ipv6 address
319 * - c: ifname length
320 * - X bytes: interface name
321 *
322 * q(64), l(32), w(16), c(8)
323 */
324
325 /* Initialize parameters return values. */
326 memset(bpc, 0, sizeof(*bpc));
327 *pc = NULL;
328
329 /* Find or allocate process context data. */
330 STREAM_GETL(msg, pid);
331
332 *pc = pc_new(pid);
333 if (*pc == NULL) {
334 log_debug("ptm-read: failed to allocate memory");
335 return -1;
336 }
337
338 /* Register/update peer information. */
339 _ptm_msg_read_address(msg, &bpc->bpc_peer);
340
341 /* Determine IP type from peer destination. */
342 bpc->bpc_ipv4 = (bpc->bpc_peer.sa_sin.sin_family == AF_INET);
343
344 /* Get peer configuration. */
345 if (command != ZEBRA_BFD_DEST_DEREGISTER) {
346 STREAM_GETL(msg, bpc->bpc_recvinterval);
347 bpc->bpc_has_recvinterval =
348 (bpc->bpc_recvinterval != BPC_DEF_RECEIVEINTERVAL);
349
350 STREAM_GETL(msg, bpc->bpc_txinterval);
351 bpc->bpc_has_txinterval =
352 (bpc->bpc_txinterval != BPC_DEF_TRANSMITINTERVAL);
353
354 STREAM_GETC(msg, bpc->bpc_detectmultiplier);
355 bpc->bpc_has_detectmultiplier =
356 (bpc->bpc_detectmultiplier != BPC_DEF_DETECTMULTIPLIER);
357 }
358
359 /* Read (single|multi)hop and its options. */
360 STREAM_GETC(msg, bpc->bpc_mhop);
361 if (bpc->bpc_mhop) {
362 /* Read multihop source address and TTL. */
363 _ptm_msg_read_address(msg, &bpc->bpc_local);
364 STREAM_GETC(msg, ttl);
365 } else {
366 /* If target is IPv6, then we must obtain local address. */
367 if (bpc->bpc_ipv4 == false)
368 _ptm_msg_read_address(msg, &bpc->bpc_local);
369
370 /*
371 * Read interface name and make sure it fits our data
372 * structure, otherwise fail.
373 */
374 STREAM_GETC(msg, ifnamelen);
375 if (ifnamelen >= sizeof(bpc->bpc_localif)) {
376 log_error("ptm-read: interface name is too big");
377 return -1;
378 }
379
380 bpc->bpc_has_localif = ifnamelen > 0;
381 if (bpc->bpc_has_localif) {
382 STREAM_GET(bpc->bpc_localif, msg, ifnamelen);
383 bpc->bpc_localif[ifnamelen] = 0;
384
385 /*
386 * IPv6 link-local addresses must use scope id,
387 * otherwise the session lookup will always fail
388 * and we'll have multiple sessions showing up.
389 *
390 * This problem only happens with single hop
391 * since it is not possible to have link-local
392 * address for multi hop sessions.
393 */
394 if (bpc->bpc_ipv4 == false
395 && IN6_IS_ADDR_LINKLOCAL(
396 &bpc->bpc_peer.sa_sin6.sin6_addr))
397 bpc->bpc_peer.sa_sin6.sin6_scope_id =
398 ptm_bfd_fetch_ifindex(bpc->bpc_localif);
399 }
400 }
401
402 /* Sanity check: peer and local address must match IP types. */
403 if (bpc->bpc_local.sa_sin.sin_family != 0
404 && (bpc->bpc_local.sa_sin.sin_family
405 != bpc->bpc_peer.sa_sin.sin_family)) {
406 log_warning("ptm-read: peer family doesn't match local type");
407 return -1;
408 }
409
410 return 0;
411
412 stream_failure:
413 return -1;
414 }
415
416 static void bfdd_dest_register(struct stream *msg)
417 {
418 struct ptm_client *pc;
419 struct ptm_client_notification *pcn;
420 struct bfd_session *bs;
421 struct bfd_peer_cfg bpc;
422
423 /* Read the client context and peer data. */
424 if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, &bpc, &pc) == -1)
425 return;
426
427 DEBUG_PRINTBPC(&bpc);
428
429 /* Find or start new BFD session. */
430 bs = bs_peer_find(&bpc);
431 if (bs == NULL) {
432 bs = ptm_bfd_sess_new(&bpc);
433 if (bs == NULL) {
434 log_debug("ptm-add-dest: failed to create BFD session");
435 return;
436 }
437 } else {
438 /* Don't try to change echo/shutdown state. */
439 bpc.bpc_echo = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
440 bpc.bpc_shutdown =
441 BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
442 }
443
444 /* Create client peer notification register. */
445 pcn = pcn_new(pc, bs);
446 if (pcn == NULL) {
447 log_error("ptm-add-dest: failed to registrate notifications");
448 return;
449 }
450
451 ptm_bfd_notify(bs);
452 }
453
454 static void bfdd_dest_deregister(struct stream *msg)
455 {
456 struct ptm_client *pc;
457 struct ptm_client_notification *pcn;
458 struct bfd_session *bs;
459 struct bfd_peer_cfg bpc;
460
461 /* Read the client context and peer data. */
462 if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, &bpc, &pc) == -1)
463 return;
464
465 DEBUG_PRINTBPC(&bpc);
466
467 /* Find or start new BFD session. */
468 bs = bs_peer_find(&bpc);
469 if (bs == NULL) {
470 log_debug("ptm-del-dest: failed to find BFD session");
471 return;
472 }
473
474 /* Unregister client peer notification. */
475 pcn = pcn_lookup(pc, bs);
476 pcn_free(pcn);
477 }
478
479 /*
480 * header: command, VRF
481 * l: pid
482 */
483 static void bfdd_client_register(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_new(pid);
492 if (pc == NULL) {
493 log_error("ptm-add-client: failed to register client: %u", pid);
494 return;
495 }
496
497 return;
498
499 stream_failure:
500 log_error("ptm-add-client: failed to register client");
501 }
502
503 /*
504 * header: command, VRF
505 * l: pid
506 */
507 static void bfdd_client_deregister(struct stream *msg)
508 {
509 struct ptm_client *pc;
510 uint32_t pid;
511
512 /* Find or allocate process context data. */
513 STREAM_GETL(msg, pid);
514
515 pc = pc_lookup(pid);
516 if (pc == NULL) {
517 log_debug("ptm-del-client: failed to find client: %u", pid);
518 return;
519 }
520
521 pc_free(pc);
522
523 return;
524
525 stream_failure:
526 log_error("ptm-del-client: failed to deregister client");
527 }
528
529 static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid)
530 {
531 struct stream *msg = zc->ibuf;
532 uint32_t rcmd;
533
534 STREAM_GETL(msg, rcmd);
535
536 switch (rcmd) {
537 case ZEBRA_BFD_DEST_REGISTER:
538 case ZEBRA_BFD_DEST_UPDATE:
539 bfdd_dest_register(msg);
540 break;
541 case ZEBRA_BFD_DEST_DEREGISTER:
542 bfdd_dest_deregister(msg);
543 break;
544 case ZEBRA_BFD_CLIENT_REGISTER:
545 bfdd_client_register(msg);
546 break;
547 case ZEBRA_BFD_CLIENT_DEREGISTER:
548 bfdd_client_deregister(msg);
549 break;
550
551 default:
552 log_debug("ptm-replay: invalid message type %u", rcmd);
553 return -1;
554 }
555
556 return 0;
557
558 stream_failure:
559 log_error("ptm-replay: failed to find command");
560 return -1;
561 }
562
563 static void bfdd_zebra_connected(struct zclient *zc)
564 {
565 struct stream *msg = zc->obuf;
566
567 /* Clean-up and free ptm clients data memory. */
568 pc_free_all();
569
570 /*
571 * The replay is an empty message just to trigger client daemons
572 * configuration replay.
573 */
574 stream_reset(msg);
575 zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
576 stream_putl(msg, ZEBRA_BFD_DEST_REPLAY);
577 stream_putw_at(msg, 0, stream_get_endp(msg));
578
579 zclient_send_message(zclient);
580 }
581
582 void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
583 {
584 zclient = zclient_new(master, &zclient_options_default);
585 assert(zclient != NULL);
586 zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
587
588 /*
589 * We'll receive all messages through replay, however it will
590 * contain a special field with the real command inside so we
591 * avoid having to create too many handlers.
592 */
593 zclient->bfd_dest_replay = bfdd_replay;
594
595 /* Send replay request on zebra connect. */
596 zclient->zebra_connected = bfdd_zebra_connected;
597 }
598
599 void bfdd_zclient_stop(void)
600 {
601 zclient_stop(zclient);
602
603 /* Clean-up and free ptm clients data memory. */
604 pc_free_all();
605 }
606
607
608 /*
609 * Client handling.
610 */
611 static struct ptm_client *pc_lookup(uint32_t pid)
612 {
613 struct ptm_client *pc;
614
615 TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
616 if (pc->pc_pid != pid)
617 continue;
618
619 break;
620 }
621
622 return pc;
623 }
624
625 static struct ptm_client *pc_new(uint32_t pid)
626 {
627 struct ptm_client *pc;
628
629 /* Look up first, if not found create the client. */
630 pc = pc_lookup(pid);
631 if (pc != NULL)
632 return pc;
633
634 /* Allocate the client data and save it. */
635 pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
636 if (pc == NULL)
637 return NULL;
638
639 pc->pc_pid = pid;
640 TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
641 return pc;
642 }
643
644 static void pc_free(struct ptm_client *pc)
645 {
646 struct ptm_client_notification *pcn;
647
648 if (pc == NULL)
649 return;
650
651 TAILQ_REMOVE(&pcqueue, pc, pc_entry);
652
653 while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) {
654 pcn = TAILQ_FIRST(&pc->pc_pcnqueue);
655 pcn_free(pcn);
656 }
657
658 XFREE(MTYPE_BFDD_CONTROL, pc);
659 }
660
661 static void pc_free_all(void)
662 {
663 struct ptm_client *pc;
664
665 while (!TAILQ_EMPTY(&pcqueue)) {
666 pc = TAILQ_FIRST(&pcqueue);
667 pc_free(pc);
668 }
669 }
670
671 static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
672 struct bfd_session *bs)
673 {
674 struct ptm_client_notification *pcn;
675
676 /* Try to find an existing pcn fist. */
677 pcn = pcn_lookup(pc, bs);
678 if (pcn != NULL)
679 return pcn;
680
681 /* Save the client notification data. */
682 pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
683 if (pcn == NULL)
684 return NULL;
685
686 TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
687 pcn->pcn_pc = pc;
688 pcn->pcn_bs = bs;
689 bs->refcount++;
690
691 return pcn;
692 }
693
694 static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
695 struct bfd_session *bs)
696 {
697 struct ptm_client_notification *pcn;
698
699 TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
700 if (pcn->pcn_bs != bs)
701 continue;
702
703 break;
704 }
705
706 return pcn;
707 }
708
709 static void pcn_free(struct ptm_client_notification *pcn)
710 {
711 struct ptm_client *pc;
712 struct bfd_session *bs;
713
714 if (pcn == NULL)
715 return;
716
717 /* Handle session de-registration. */
718 bs = pcn->pcn_bs;
719 pcn->pcn_bs = NULL;
720 bs->refcount--;
721
722 /* Handle ptm_client deregistration. */
723 pc = pcn->pcn_pc;
724 pcn->pcn_pc = NULL;
725 TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
726
727 XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
728 }