]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/ptm_adapter.c
*: use frr_elevate_privs() (2/2: manual)
[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"
27
28#include "lib/bfd.h"
29
30#include "bfd.h"
31
32/*
33 * Data structures
34 */
35struct 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};
41TAILQ_HEAD(pcnqueue, ptm_client_notification);
42
43struct ptm_client {
44 uint32_t pc_pid;
45 struct pcnqueue pc_pcnqueue;
46
47 TAILQ_ENTRY(ptm_client) pc_entry;
48};
49TAILQ_HEAD(pcqueue, ptm_client);
50
51static struct pcqueue pcqueue;
52static struct zclient *zclient;
53
54
55/*
56 * Prototypes
57 */
58static int _ptm_msg_address(struct stream *msg, struct sockaddr_any *sa);
59
60static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa);
61static int _ptm_msg_read(struct stream *msg, int command,
62 struct bfd_peer_cfg *bpc, struct ptm_client **pc);
63
64static struct ptm_client *pc_lookup(uint32_t pid);
65static struct ptm_client *pc_new(uint32_t pid);
a375de5c 66static void pc_free(struct ptm_client *pc);
788378fe 67static void pc_free_all(void);
d3af6147
RZ
68static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
69 struct bfd_session *bs);
70static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
71 struct bfd_session *bs);
72static void pcn_free(struct ptm_client_notification *pcn);
73
74
75static void bfdd_dest_register(struct stream *msg);
76static void bfdd_dest_deregister(struct stream *msg);
77static void bfdd_client_register(struct stream *msg);
a375de5c 78static void bfdd_client_deregister(struct stream *msg);
d3af6147
RZ
79
80/*
81 * Functions
82 */
83#ifdef BFD_DEBUG
84static void debug_printbpc(const char *func, unsigned int line,
85 struct bfd_peer_cfg *bpc);
86
87static 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
130static 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
153int ptm_bfd_notify(struct bfd_session *bs)
154{
155 struct stream *msg;
156 struct sockaddr_any sac;
157
158 /*
159 * Message format:
160 * - header: command, vrf
161 * - l: interface index
162 * - c: family
163 * - AF_INET:
164 * - 4 bytes: ipv4
165 * - AF_INET6:
166 * - 16 bytes: ipv6
167 * - c: prefix length
168 * - l: bfd status
169 * - c: family
170 * - AF_INET:
171 * - 4 bytes: ipv4
172 * - AF_INET6:
173 * - 16 bytes: ipv6
174 * - c: prefix length
175 *
176 * Commands: ZEBRA_BFD_DEST_REPLAY
177 *
178 * q(64), l(32), w(16), c(8)
179 */
180 msg = zclient->obuf;
181 stream_reset(msg);
182
183 /* TODO: VRF handling */
184 zclient_create_header(msg, ZEBRA_BFD_DEST_REPLAY, VRF_DEFAULT);
185
971532e2
RZ
186 /* This header will be handled by `zebra_ptm.c`. */
187 stream_putl(msg, ZEBRA_INTERFACE_BFD_DEST_UPDATE);
188
d3af6147
RZ
189 /* NOTE: Interface is a shortcut to avoid comparing source address. */
190 stream_putl(msg, bs->ifindex);
191
192 /* BFD destination prefix information. */
193 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH))
194 _ptm_msg_address(msg, &bs->mhop.peer);
195 else
196 _ptm_msg_address(msg, &bs->shop.peer);
197
198 /* BFD status */
199 switch (bs->ses_state) {
200 case PTM_BFD_UP:
201 stream_putl(msg, BFD_STATUS_UP);
202 break;
203
204 case PTM_BFD_ADM_DOWN:
205 case PTM_BFD_DOWN:
206 case PTM_BFD_INIT:
207 stream_putl(msg, BFD_STATUS_DOWN);
208 break;
209
210 default:
211 stream_putl(msg, BFD_STATUS_UNKNOWN);
212 break;
213 }
214
215 /* BFD source prefix information. */
216 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
217 _ptm_msg_address(msg, &bs->mhop.local);
218 } else {
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);
223 else {
224 sac = bs->shop.peer;
225 switch (sac.sa_sin.sin_family) {
226 case AF_INET:
227 memset(&sac.sa_sin.sin_addr, 0,
228 sizeof(sac.sa_sin.sin_family));
229 break;
230 case AF_INET6:
231 memset(&sac.sa_sin6.sin6_addr, 0,
232 sizeof(sac.sa_sin6.sin6_family));
233 break;
234
235 default:
236 assert(false);
237 break;
238 }
239
240 /* No local address found yet, so send zeroes. */
241 _ptm_msg_address(msg, &sac);
242 }
243 }
244
245 /* Write packet size. */
246 stream_putw_at(msg, 0, stream_get_endp(msg));
247
248 return zclient_send_message(zclient);
249}
250
251static void _ptm_msg_read_address(struct stream *msg, struct sockaddr_any *sa)
252{
253 uint16_t family;
254
255 STREAM_GETW(msg, family);
256
257 switch (family) {
258 case AF_INET:
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 */
265 return;
266
267 case AF_INET6:
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 */
274 return;
275
276 default:
277 log_warning("%s: invalid family: %d", __func__, family);
278 break;
279 }
280
281stream_failure:
282 memset(sa, 0, sizeof(*sa));
283}
284
285static int _ptm_msg_read(struct stream *msg, int command,
286 struct bfd_peer_cfg *bpc, struct ptm_client **pc)
287{
288 uint32_t pid;
289 uint8_t ttl;
290 uint8_t ifnamelen;
291
292 /*
293 * Register/Deregister/Update Message format:
294 * - header: Command, VRF
295 * - l: pid
296 * - w: family
297 * - AF_INET:
298 * - l: destination ipv4
299 * - AF_INET6:
300 * - 16 bytes: destination IPv6
301 * - command != ZEBRA_BFD_DEST_DEREGISTER
302 * - l: min_rx
303 * - l: min_tx
304 * - c: detect multiplier
305 * - c: is_multihop?
306 * - multihop:
307 * - w: family
308 * - AF_INET:
309 * - l: destination ipv4
310 * - AF_INET6:
311 * - 16 bytes: destination IPv6
312 * - c: ttl
313 * - no multihop
314 * - AF_INET6:
315 * - w: family
316 * - 16 bytes: ipv6 address
317 * - c: ifname length
318 * - X bytes: interface name
319 *
320 * q(64), l(32), w(16), c(8)
321 */
322
323 /* Initialize parameters return values. */
324 memset(bpc, 0, sizeof(*bpc));
325 *pc = NULL;
326
327 /* Find or allocate process context data. */
328 STREAM_GETL(msg, pid);
329
330 *pc = pc_new(pid);
331 if (*pc == NULL) {
332 log_debug("%s: failed to allocate memory", __func__);
333 return -1;
334 }
335
336 /* Register/update peer information. */
337 _ptm_msg_read_address(msg, &bpc->bpc_peer);
338
339 /* Determine IP type from peer destination. */
340 bpc->bpc_ipv4 = (bpc->bpc_peer.sa_sin.sin_family == AF_INET);
341
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);
347
348 STREAM_GETL(msg, bpc->bpc_txinterval);
349 bpc->bpc_has_txinterval =
350 (bpc->bpc_txinterval != BPC_DEF_TRANSMITINTERVAL);
351
352 STREAM_GETC(msg, bpc->bpc_detectmultiplier);
353 bpc->bpc_has_detectmultiplier =
354 (bpc->bpc_detectmultiplier != BPC_DEF_DETECTMULTIPLIER);
355 }
356
357 /* Read (single|multi)hop and its options. */
358 STREAM_GETC(msg, bpc->bpc_mhop);
359 if (bpc->bpc_mhop) {
360 /* Read multihop source address and TTL. */
361 _ptm_msg_read_address(msg, &bpc->bpc_local);
362 STREAM_GETC(msg, ttl);
363
364 /*
365 * TODO: use TTL for something. The line below removes
366 * an unused variable compiler warning.
367 */
368 ttl = ttl;
369 } else {
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);
373
374 /*
375 * Read interface name and make sure it fits our data
376 * structure, otherwise fail.
377 */
378 STREAM_GETC(msg, ifnamelen);
379 if (ifnamelen > sizeof(bpc->bpc_localif)) {
380 log_error("%s: interface name is too big", __func__);
381 return -1;
382 }
383
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;
388 }
389 }
390
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",
396 __func__);
397 return -1;
398 }
399
400 return 0;
401
402stream_failure:
403 return -1;
404}
405
406static void bfdd_dest_register(struct stream *msg)
407{
408 struct ptm_client *pc;
409 struct ptm_client_notification *pcn;
410 struct bfd_session *bs;
411 struct bfd_peer_cfg bpc;
412
413 /* Read the client context and peer data. */
414 if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_REGISTER, &bpc, &pc) == -1)
415 return;
416
417 DEBUG_PRINTBPC(&bpc);
418
419 /* Find or start new BFD session. */
420 bs = bs_peer_find(&bpc);
421 if (bs == NULL) {
422 bs = ptm_bfd_sess_new(&bpc);
423 if (bs == NULL) {
424 log_debug("%s: failed to create BFD session", __func__);
425 return;
426 }
427 } else {
428 /* Don't try to change echo/shutdown state. */
429 bpc.bpc_echo = BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
430 bpc.bpc_shutdown =
431 BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
432 }
433
434 /* Create client peer notification register. */
435 pcn = pcn_new(pc, bs);
436 if (pcn == NULL) {
437 log_error("%s: failed to registrate notifications", __func__);
438 return;
439 }
440
441 ptm_bfd_notify(bs);
442}
443
444static void bfdd_dest_deregister(struct stream *msg)
445{
446 struct ptm_client *pc;
447 struct ptm_client_notification *pcn;
448 struct bfd_session *bs;
449 struct bfd_peer_cfg bpc;
450
451 /* Read the client context and peer data. */
452 if (_ptm_msg_read(msg, ZEBRA_BFD_DEST_DEREGISTER, &bpc, &pc) == -1)
453 return;
454
455 DEBUG_PRINTBPC(&bpc);
456
457 /* Find or start new BFD session. */
458 bs = bs_peer_find(&bpc);
459 if (bs == NULL) {
460 log_debug("%s: failed to create BFD session", __func__);
461 return;
462 }
463
464 /* Unregister client peer notification. */
465 pcn = pcn_lookup(pc, bs);
466 pcn_free(pcn);
467}
468
469/*
470 * header: command, VRF
471 * l: pid
472 */
473static void bfdd_client_register(struct stream *msg)
474{
475 struct ptm_client *pc;
476 uint32_t pid;
477
478 /* Find or allocate process context data. */
479 STREAM_GETL(msg, pid);
480
481 pc = pc_new(pid);
482 if (pc == NULL) {
483 log_error("%s: failed to register client: %u", __func__, pid);
484 return;
485 }
486
487 return;
488
489stream_failure:
490 log_error("%s: failed to register client", __func__);
491}
492
a375de5c
RZ
493/*
494 * header: command, VRF
495 * l: pid
496 */
497static void bfdd_client_deregister(struct stream *msg)
498{
499 struct ptm_client *pc;
500 uint32_t pid;
501
502 /* Find or allocate process context data. */
503 STREAM_GETL(msg, pid);
504
505 pc = pc_lookup(pid);
506 if (pc == NULL) {
507 log_debug("%s: failed to find client: %u", __func__, pid);
508 return;
509 }
510
511 pc_free(pc);
512
513 return;
514
515stream_failure:
516 log_error("%s: failed to deregister client", __func__);
517}
518
d3af6147
RZ
519static int bfdd_replay(int cmd, struct zclient *zc, uint16_t len, vrf_id_t vid)
520{
521 struct stream *msg = zc->ibuf;
522 uint32_t rcmd;
523
524 STREAM_GETL(msg, rcmd);
525
526 switch (rcmd) {
527 case ZEBRA_BFD_DEST_REGISTER:
528 case ZEBRA_BFD_DEST_UPDATE:
529 bfdd_dest_register(msg);
530 break;
531 case ZEBRA_BFD_DEST_DEREGISTER:
532 bfdd_dest_deregister(msg);
533 break;
534 case ZEBRA_BFD_CLIENT_REGISTER:
535 bfdd_client_register(msg);
536 break;
a375de5c
RZ
537 case ZEBRA_BFD_CLIENT_DEREGISTER:
538 bfdd_client_deregister(msg);
539 break;
d3af6147
RZ
540
541 default:
542 log_debug("%s: invalid message type %u", __func__, rcmd);
543 return -1;
544 }
545
546 return 0;
547
548stream_failure:
549 log_error("%s: failed to find command", __func__);
550 return -1;
551}
552
971532e2
RZ
553static void bfdd_zebra_connected(struct zclient *zc)
554{
555 struct stream *msg = zc->obuf;
556
788378fe
RZ
557 /* Clean-up and free ptm clients data memory. */
558 pc_free_all();
559
971532e2
RZ
560 /*
561 * The replay is an empty message just to trigger client daemons
562 * configuration replay.
563 */
564 stream_reset(msg);
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));
568
569 zclient_send_message(zclient);
570}
571
d3af6147
RZ
572void bfdd_zclient_init(struct zebra_privs_t *bfdd_priv)
573{
574 zclient = zclient_new_notify(master, &zclient_options_default);
575 assert(zclient != NULL);
576 zclient_init(zclient, ZEBRA_ROUTE_BFD, 0, bfdd_priv);
577
578 /*
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.
582 */
583 zclient->bfd_dest_replay = bfdd_replay;
971532e2
RZ
584
585 /* Send replay request on zebra connect. */
586 zclient->zebra_connected = bfdd_zebra_connected;
d3af6147
RZ
587}
588
589void bfdd_zclient_stop(void)
590{
591 zclient_stop(zclient);
788378fe
RZ
592
593 /* Clean-up and free ptm clients data memory. */
594 pc_free_all();
d3af6147
RZ
595}
596
597
598/*
599 * Client handling.
600 */
601static struct ptm_client *pc_lookup(uint32_t pid)
602{
603 struct ptm_client *pc;
604
605 TAILQ_FOREACH (pc, &pcqueue, pc_entry) {
606 if (pc->pc_pid != pid)
607 continue;
608
609 break;
610 }
611
612 return pc;
613}
614
615static struct ptm_client *pc_new(uint32_t pid)
616{
617 struct ptm_client *pc;
618
619 /* Look up first, if not found create the client. */
620 pc = pc_lookup(pid);
621 if (pc != NULL)
622 return pc;
623
624 /* Allocate the client data and save it. */
625 pc = XCALLOC(MTYPE_BFDD_CONTROL, sizeof(*pc));
626 if (pc == NULL)
627 return NULL;
628
629 pc->pc_pid = pid;
630 TAILQ_INSERT_HEAD(&pcqueue, pc, pc_entry);
631 return pc;
632}
633
a375de5c
RZ
634static void pc_free(struct ptm_client *pc)
635{
636 struct ptm_client_notification *pcn;
637
638 if (pc == NULL)
639 return;
640
641 TAILQ_REMOVE(&pcqueue, pc, pc_entry);
642
643 while (!TAILQ_EMPTY(&pc->pc_pcnqueue)) {
644 pcn = TAILQ_FIRST(&pc->pc_pcnqueue);
645 pcn_free(pcn);
646 }
647
648 XFREE(MTYPE_BFDD_CONTROL, pc);
649}
650
788378fe
RZ
651static void pc_free_all(void)
652{
653 struct ptm_client *pc;
654
655 while (!TAILQ_EMPTY(&pcqueue)) {
656 pc = TAILQ_FIRST(&pcqueue);
657 pc_free(pc);
658 }
659}
660
d3af6147
RZ
661static struct ptm_client_notification *pcn_new(struct ptm_client *pc,
662 struct bfd_session *bs)
663{
664 struct ptm_client_notification *pcn;
665
666 /* Try to find an existing pcn fist. */
667 pcn = pcn_lookup(pc, bs);
668 if (pcn != NULL)
669 return pcn;
670
671 /* Save the client notification data. */
672 pcn = XCALLOC(MTYPE_BFDD_NOTIFICATION, sizeof(*pcn));
673 if (pcn == NULL)
674 return NULL;
675
676 TAILQ_INSERT_HEAD(&pc->pc_pcnqueue, pcn, pcn_entry);
677 pcn->pcn_pc = pc;
678 pcn->pcn_bs = bs;
679 bs->refcount++;
680
681 return pcn;
682}
683
684static struct ptm_client_notification *pcn_lookup(struct ptm_client *pc,
685 struct bfd_session *bs)
686{
687 struct ptm_client_notification *pcn;
688
689 TAILQ_FOREACH (pcn, &pc->pc_pcnqueue, pcn_entry) {
690 if (pcn->pcn_bs != bs)
691 continue;
692
693 break;
694 }
695
696 return pcn;
697}
698
699static void pcn_free(struct ptm_client_notification *pcn)
700{
701 struct ptm_client *pc;
702 struct bfd_session *bs;
703
704 if (pcn == NULL)
705 return;
706
707 /* Handle session de-registration. */
708 bs = pcn->pcn_bs;
709 pcn->pcn_bs = NULL;
710 bs->refcount--;
711
712 /* Handle ptm_client deregistration. */
713 pc = pcn->pcn_pc;
714 pcn->pcn_pc = NULL;
715 TAILQ_REMOVE(&pc->pc_pcnqueue, pcn, pcn_entry);
716
717 XFREE(MTYPE_BFDD_NOTIFICATION, pcn);
718}