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