Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /** |
3 | | * bgp_bfd.c: BGP BFD handling routines |
4 | | * |
5 | | * @copyright Copyright (C) 2015 Cumulus Networks, Inc. |
6 | | */ |
7 | | |
8 | | #include <zebra.h> |
9 | | |
10 | | #include "command.h" |
11 | | #include "linklist.h" |
12 | | #include "memory.h" |
13 | | #include "prefix.h" |
14 | | #include "frrevent.h" |
15 | | #include "buffer.h" |
16 | | #include "stream.h" |
17 | | #include "vrf.h" |
18 | | #include "zclient.h" |
19 | | #include "bfd.h" |
20 | | #include "lib/json.h" |
21 | | #include "filter.h" |
22 | | |
23 | | #include "bgpd/bgpd.h" |
24 | | #include "bgp_fsm.h" |
25 | | #include "bgpd/bgp_bfd.h" |
26 | | #include "bgpd/bgp_debug.h" |
27 | | #include "bgpd/bgp_vty.h" |
28 | | #include "bgpd/bgp_packet.h" |
29 | | |
30 | 2 | DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data"); |
31 | 2 | |
32 | 2 | extern struct zclient *zclient; |
33 | 2 | |
34 | 2 | static void bfd_session_status_update(struct bfd_session_params *bsp, |
35 | 2 | const struct bfd_session_status *bss, |
36 | 2 | void *arg) |
37 | 2 | { |
38 | 0 | struct peer *peer = arg; |
39 | |
|
40 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
41 | 0 | zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s", |
42 | 0 | __func__, peer->conf_if ? peer->conf_if : peer->host, |
43 | 0 | bfd_sess_vrf(bsp), bfd_sess_vrf_id(bsp), |
44 | 0 | bfd_get_status_str(bss->previous_state), |
45 | 0 | bfd_get_status_str(bss->state)); |
46 | |
|
47 | 0 | if (bss->state == BSS_DOWN && bss->previous_state == BSS_UP) { |
48 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) |
49 | 0 | && bfd_sess_cbit(bsp) && !bss->remote_cbit) { |
50 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
51 | 0 | zlog_debug( |
52 | 0 | "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared", |
53 | 0 | peer->host); |
54 | 0 | return; |
55 | 0 | } |
56 | 0 | peer->last_reset = PEER_DOWN_BFD_DOWN; |
57 | | |
58 | | /* rfc9384 */ |
59 | 0 | if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) |
60 | 0 | bgp_notify_send(peer, BGP_NOTIFY_CEASE, |
61 | 0 | BGP_NOTIFY_CEASE_BFD_DOWN); |
62 | |
|
63 | 0 | BGP_EVENT_ADD(peer, BGP_Stop); |
64 | 0 | } |
65 | | |
66 | 0 | if (bss->state == BSS_UP && bss->previous_state != BSS_UP |
67 | 0 | && !peer_established(peer)) { |
68 | 0 | if (!BGP_PEER_START_SUPPRESSED(peer)) { |
69 | 0 | bgp_fsm_nht_update(peer, true); |
70 | 0 | BGP_EVENT_ADD(peer, BGP_Start); |
71 | 0 | } |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | void bgp_peer_config_apply(struct peer *p, struct peer_group *pg) |
76 | 0 | { |
77 | 0 | struct listnode *n; |
78 | 0 | struct peer *pn; |
79 | 0 | struct peer *gconfig; |
80 | | |
81 | | /* When called on a group, apply to all peers. */ |
82 | 0 | if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) { |
83 | 0 | for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) |
84 | 0 | bgp_peer_config_apply(pn, pg); |
85 | 0 | return; |
86 | 0 | } |
87 | | |
88 | | /* No group, just use current configuration. */ |
89 | 0 | if (pg == NULL || pg->conf->bfd_config == NULL) { |
90 | 0 | bfd_sess_set_timers(p->bfd_config->session, |
91 | 0 | p->bfd_config->detection_multiplier, |
92 | 0 | p->bfd_config->min_rx, |
93 | 0 | p->bfd_config->min_tx); |
94 | 0 | bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit); |
95 | 0 | bfd_sess_set_profile(p->bfd_config->session, |
96 | 0 | p->bfd_config->profile); |
97 | 0 | bfd_sess_install(p->bfd_config->session); |
98 | 0 | return; |
99 | 0 | } |
100 | | |
101 | | /* |
102 | | * Check if the group configuration was overwritten or apply group |
103 | | * configuration. |
104 | | */ |
105 | 0 | gconfig = pg->conf; |
106 | | |
107 | | /* |
108 | | * If using default control plane independent configuration, |
109 | | * then prefer group's (e.g. it means it wasn't manually configured). |
110 | | */ |
111 | 0 | if (!p->bfd_config->cbit) |
112 | 0 | bfd_sess_set_cbit(p->bfd_config->session, |
113 | 0 | gconfig->bfd_config->cbit); |
114 | 0 | else |
115 | 0 | bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit); |
116 | | |
117 | | /* If no profile was specified in peer, then use the group profile. */ |
118 | 0 | if (p->bfd_config->profile[0] == 0) |
119 | 0 | bfd_sess_set_profile(p->bfd_config->session, |
120 | 0 | gconfig->bfd_config->profile); |
121 | 0 | else |
122 | 0 | bfd_sess_set_profile(p->bfd_config->session, |
123 | 0 | p->bfd_config->profile); |
124 | | |
125 | | /* If no specific timers were configured, then use the group timers. */ |
126 | 0 | if (p->bfd_config->detection_multiplier == BFD_DEF_DETECT_MULT |
127 | 0 | || p->bfd_config->min_rx == BFD_DEF_MIN_RX |
128 | 0 | || p->bfd_config->min_tx == BFD_DEF_MIN_TX) |
129 | 0 | bfd_sess_set_timers(p->bfd_config->session, |
130 | 0 | gconfig->bfd_config->detection_multiplier, |
131 | 0 | gconfig->bfd_config->min_rx, |
132 | 0 | gconfig->bfd_config->min_tx); |
133 | 0 | else |
134 | 0 | bfd_sess_set_timers(p->bfd_config->session, |
135 | 0 | p->bfd_config->detection_multiplier, |
136 | 0 | p->bfd_config->min_rx, |
137 | 0 | p->bfd_config->min_tx); |
138 | |
|
139 | 0 | bfd_sess_install(p->bfd_config->session); |
140 | 0 | } |
141 | | |
142 | | void bgp_peer_bfd_update_source(struct peer *p) |
143 | 0 | { |
144 | 0 | struct bfd_session_params *session = p->bfd_config->session; |
145 | 0 | const union sockunion *source; |
146 | 0 | bool changed = false; |
147 | 0 | int family; |
148 | 0 | union { |
149 | 0 | struct in_addr v4; |
150 | 0 | struct in6_addr v6; |
151 | 0 | } src, dst; |
152 | | |
153 | | /* Nothing to do for groups. */ |
154 | 0 | if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) |
155 | 0 | return; |
156 | | |
157 | | /* Figure out the correct source to use. */ |
158 | 0 | if (CHECK_FLAG(p->flags, PEER_FLAG_UPDATE_SOURCE) && p->update_source) |
159 | 0 | source = p->update_source; |
160 | 0 | else |
161 | 0 | source = p->su_local; |
162 | | |
163 | | /* Update peer's source/destination addresses. */ |
164 | 0 | bfd_sess_addresses(session, &family, &src.v6, &dst.v6); |
165 | 0 | if (family == AF_INET) { |
166 | 0 | if ((source && source->sin.sin_addr.s_addr != src.v4.s_addr) |
167 | 0 | || p->su.sin.sin_addr.s_addr != dst.v4.s_addr) { |
168 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
169 | 0 | zlog_debug( |
170 | 0 | "%s: address [%pI4->%pI4] to [%pI4->%pI4]", |
171 | 0 | __func__, &src.v4, &dst.v4, |
172 | 0 | source ? &source->sin.sin_addr |
173 | 0 | : &src.v4, |
174 | 0 | &p->su.sin.sin_addr); |
175 | |
|
176 | 0 | bfd_sess_set_ipv4_addrs( |
177 | 0 | session, source ? &source->sin.sin_addr : NULL, |
178 | 0 | &p->su.sin.sin_addr); |
179 | 0 | changed = true; |
180 | 0 | } |
181 | 0 | } else { |
182 | 0 | if ((source && memcmp(&source->sin6, &src.v6, sizeof(src.v6))) |
183 | 0 | || memcmp(&p->su.sin6, &dst.v6, sizeof(dst.v6))) { |
184 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
185 | 0 | zlog_debug( |
186 | 0 | "%s: address [%pI6->%pI6] to [%pI6->%pI6]", |
187 | 0 | __func__, &src.v6, &dst.v6, |
188 | 0 | source ? &source->sin6.sin6_addr |
189 | 0 | : &src.v6, |
190 | 0 | &p->su.sin6.sin6_addr); |
191 | |
|
192 | 0 | bfd_sess_set_ipv6_addrs(session, |
193 | 0 | source ? &source->sin6.sin6_addr |
194 | 0 | : NULL, |
195 | 0 | &p->su.sin6.sin6_addr); |
196 | 0 | changed = true; |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | /* Update interface. */ |
201 | 0 | if (p->nexthop.ifp && bfd_sess_interface(session) == NULL) { |
202 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
203 | 0 | zlog_debug("%s: interface none to %s", __func__, |
204 | 0 | p->nexthop.ifp->name); |
205 | |
|
206 | 0 | bfd_sess_set_interface(session, p->nexthop.ifp->name); |
207 | 0 | changed = true; |
208 | 0 | } |
209 | | |
210 | | /* |
211 | | * Update TTL. |
212 | | * |
213 | | * Two cases: |
214 | | * - We detected that the peer is a hop away from us (remove multi hop). |
215 | | * (this happens when `p->shared_network` is set to `true`) |
216 | | * - eBGP multi hop / TTL security changed. |
217 | | */ |
218 | 0 | if (!PEER_IS_MULTIHOP(p) && bfd_sess_hop_count(session) > 1) { |
219 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
220 | 0 | zlog_debug("%s: TTL %d to 1", __func__, |
221 | 0 | bfd_sess_hop_count(session)); |
222 | |
|
223 | 0 | bfd_sess_set_hop_count(session, 1); |
224 | 0 | changed = true; |
225 | 0 | } |
226 | 0 | if (PEER_IS_MULTIHOP(p) && p->ttl != bfd_sess_hop_count(session)) { |
227 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
228 | 0 | zlog_debug("%s: TTL %d to %d", __func__, |
229 | 0 | bfd_sess_hop_count(session), p->ttl); |
230 | |
|
231 | 0 | bfd_sess_set_hop_count(session, p->ttl); |
232 | 0 | changed = true; |
233 | 0 | } |
234 | | |
235 | | /* Update VRF. */ |
236 | 0 | if (bfd_sess_vrf_id(session) != p->bgp->vrf_id) { |
237 | 0 | if (BGP_DEBUG(bfd, BFD_LIB)) |
238 | 0 | zlog_debug( |
239 | 0 | "%s: VRF %s(%d) to %s(%d)", __func__, |
240 | 0 | bfd_sess_vrf(session), bfd_sess_vrf_id(session), |
241 | 0 | vrf_id_to_name(p->bgp->vrf_id), p->bgp->vrf_id); |
242 | |
|
243 | 0 | bfd_sess_set_vrf(session, p->bgp->vrf_id); |
244 | 0 | changed = true; |
245 | 0 | } |
246 | |
|
247 | 0 | if (changed) |
248 | 0 | bfd_sess_install(session); |
249 | 0 | } |
250 | | |
251 | | /** |
252 | | * Reset BFD configuration data structure to its defaults settings. |
253 | | */ |
254 | | static void bgp_peer_bfd_reset(struct peer *p) |
255 | 0 | { |
256 | | /* Set defaults. */ |
257 | 0 | p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT; |
258 | 0 | p->bfd_config->min_rx = BFD_DEF_MIN_RX; |
259 | 0 | p->bfd_config->min_tx = BFD_DEF_MIN_TX; |
260 | 0 | p->bfd_config->cbit = false; |
261 | 0 | p->bfd_config->profile[0] = 0; |
262 | 0 | } |
263 | | |
264 | | void bgp_peer_configure_bfd(struct peer *p, bool manual) |
265 | 0 | { |
266 | | /* Groups should not call this. */ |
267 | 0 | assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); |
268 | | |
269 | | /* Already configured, skip it. */ |
270 | 0 | if (p->bfd_config) { |
271 | | /* If manually active update flag. */ |
272 | 0 | if (!p->bfd_config->manual) |
273 | 0 | p->bfd_config->manual = manual; |
274 | |
|
275 | 0 | return; |
276 | 0 | } |
277 | | |
278 | | /* Allocate memory for configuration overrides. */ |
279 | 0 | p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config)); |
280 | 0 | p->bfd_config->manual = manual; |
281 | | |
282 | | /* Create new session and assign callback. */ |
283 | 0 | p->bfd_config->session = bfd_sess_new(bfd_session_status_update, p); |
284 | 0 | bgp_peer_bfd_reset(p); |
285 | | |
286 | | /* Configure session with basic BGP peer data. */ |
287 | 0 | if (p->su.sa.sa_family == AF_INET) |
288 | 0 | bfd_sess_set_ipv4_addrs(p->bfd_config->session, |
289 | 0 | p->su_local ? &p->su_local->sin.sin_addr |
290 | 0 | : NULL, |
291 | 0 | &p->su.sin.sin_addr); |
292 | 0 | else |
293 | 0 | bfd_sess_set_ipv6_addrs( |
294 | 0 | p->bfd_config->session, |
295 | 0 | p->su_local ? &p->su_local->sin6.sin6_addr : NULL, |
296 | 0 | &p->su.sin6.sin6_addr); |
297 | |
|
298 | 0 | bfd_sess_set_vrf(p->bfd_config->session, p->bgp->vrf_id); |
299 | 0 | bfd_sess_set_hop_count(p->bfd_config->session, |
300 | 0 | PEER_IS_MULTIHOP(p) ? p->ttl : 1); |
301 | |
|
302 | 0 | if (p->nexthop.ifp) |
303 | 0 | bfd_sess_set_interface(p->bfd_config->session, |
304 | 0 | p->nexthop.ifp->name); |
305 | 0 | } |
306 | | |
307 | | static void bgp_peer_remove_bfd(struct peer *p) |
308 | 0 | { |
309 | | /* Groups should not call this. */ |
310 | 0 | assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); |
311 | | |
312 | | /* |
313 | | * Peer configuration was removed, however we must check if there |
314 | | * is still a group configuration to keep this running. |
315 | | */ |
316 | 0 | if (p->group && p->group->conf->bfd_config) { |
317 | 0 | p->bfd_config->manual = false; |
318 | 0 | bgp_peer_bfd_reset(p); |
319 | 0 | bgp_peer_config_apply(p, p->group); |
320 | 0 | return; |
321 | 0 | } |
322 | | |
323 | 0 | if (p->bfd_config) |
324 | 0 | bfd_sess_free(&p->bfd_config->session); |
325 | |
|
326 | 0 | XFREE(MTYPE_BFD_CONFIG, p->bfd_config); |
327 | 0 | } |
328 | | |
329 | | static void bgp_group_configure_bfd(struct peer *p) |
330 | 0 | { |
331 | 0 | struct listnode *n; |
332 | 0 | struct peer *pn; |
333 | | |
334 | | /* Peers should not call this. */ |
335 | 0 | assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); |
336 | | |
337 | | /* Already allocated: do nothing. */ |
338 | 0 | if (p->bfd_config) |
339 | 0 | return; |
340 | | |
341 | 0 | p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config)); |
342 | | |
343 | | /* Set defaults. */ |
344 | 0 | p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT; |
345 | 0 | p->bfd_config->min_rx = BFD_DEF_MIN_RX; |
346 | 0 | p->bfd_config->min_tx = BFD_DEF_MIN_TX; |
347 | |
|
348 | 0 | for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) |
349 | 0 | bgp_peer_configure_bfd(pn, false); |
350 | 0 | } |
351 | | |
352 | | static void bgp_group_remove_bfd(struct peer *p) |
353 | 0 | { |
354 | 0 | struct listnode *n; |
355 | 0 | struct peer *pn; |
356 | | |
357 | | /* Peers should not call this. */ |
358 | 0 | assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); |
359 | | |
360 | | /* Already freed: do nothing. */ |
361 | 0 | if (p->bfd_config == NULL) |
362 | 0 | return; |
363 | | |
364 | | /* Free configuration and point to `NULL`. */ |
365 | 0 | XFREE(MTYPE_BFD_CONFIG, p->bfd_config); |
366 | | |
367 | | /* Now that it is `NULL` recalculate configuration for all peers. */ |
368 | 0 | for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) { |
369 | 0 | if (pn->bfd_config->manual) |
370 | 0 | bgp_peer_config_apply(pn, NULL); |
371 | 0 | else |
372 | 0 | bgp_peer_remove_bfd(pn); |
373 | 0 | } |
374 | 0 | } |
375 | | |
376 | | void bgp_peer_remove_bfd_config(struct peer *p) |
377 | 0 | { |
378 | 0 | if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) |
379 | 0 | bgp_group_remove_bfd(p); |
380 | 0 | else |
381 | 0 | bgp_peer_remove_bfd(p); |
382 | 0 | } |
383 | | |
384 | | /* |
385 | | * bgp_bfd_peer_config_write - Write the peer BFD configuration. |
386 | | */ |
387 | | void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer, |
388 | | const char *addr) |
389 | 0 | { |
390 | | /* |
391 | | * Always show group BFD configuration, but peer only when explicitly |
392 | | * configured. |
393 | | */ |
394 | 0 | if ((!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) |
395 | 0 | && peer->bfd_config->manual) |
396 | 0 | || CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { |
397 | 0 | #if HAVE_BFDD > 0 |
398 | 0 | vty_out(vty, " neighbor %s bfd\n", addr); |
399 | | #else |
400 | | vty_out(vty, " neighbor %s bfd %d %d %d\n", addr, |
401 | | peer->bfd_config->detection_multiplier, |
402 | | peer->bfd_config->min_rx, peer->bfd_config->min_tx); |
403 | | #endif /* HAVE_BFDD */ |
404 | 0 | } |
405 | |
|
406 | 0 | if (peer->bfd_config->profile[0]) |
407 | 0 | vty_out(vty, " neighbor %s bfd profile %s\n", addr, |
408 | 0 | peer->bfd_config->profile); |
409 | |
|
410 | 0 | if (peer->bfd_config->cbit) |
411 | 0 | vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", |
412 | 0 | addr); |
413 | 0 | } |
414 | | |
415 | | /* |
416 | | * bgp_bfd_show_info - Show the peer BFD information. |
417 | | */ |
418 | | void bgp_bfd_show_info(struct vty *vty, const struct peer *peer, |
419 | | json_object *json_neigh) |
420 | 0 | { |
421 | 0 | bfd_sess_show(vty, json_neigh, peer->bfd_config->session); |
422 | 0 | } |
423 | | |
424 | | DEFUN (neighbor_bfd, |
425 | | neighbor_bfd_cmd, |
426 | | "neighbor <A.B.C.D|X:X::X:X|WORD> bfd", |
427 | | NEIGHBOR_STR |
428 | | NEIGHBOR_ADDR_STR2 |
429 | | "Enables BFD support\n") |
430 | 0 | { |
431 | 0 | int idx_peer = 1; |
432 | 0 | struct peer *peer; |
433 | |
|
434 | 0 | peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); |
435 | 0 | if (!peer) |
436 | 0 | return CMD_WARNING_CONFIG_FAILED; |
437 | | |
438 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) |
439 | 0 | bgp_group_configure_bfd(peer); |
440 | 0 | else |
441 | 0 | bgp_peer_configure_bfd(peer, true); |
442 | |
|
443 | 0 | bgp_peer_config_apply(peer, peer->group); |
444 | |
|
445 | 0 | return CMD_SUCCESS; |
446 | 0 | } |
447 | | |
448 | | #if HAVE_BFDD > 0 |
449 | | DEFUN_HIDDEN( |
450 | | #else |
451 | | DEFUN( |
452 | | #endif /* HAVE_BFDD */ |
453 | | neighbor_bfd_param, |
454 | | neighbor_bfd_param_cmd, |
455 | | "neighbor <A.B.C.D|X:X::X:X|WORD> bfd (2-255) (50-60000) (50-60000)", |
456 | | NEIGHBOR_STR |
457 | | NEIGHBOR_ADDR_STR2 |
458 | | "Enables BFD support\n" |
459 | | "Detect Multiplier\n" |
460 | | "Required min receive interval\n" |
461 | | "Desired min transmit interval\n") |
462 | 0 | { |
463 | 0 | int idx_peer = 1; |
464 | 0 | int idx_number_1 = 3; |
465 | 0 | int idx_number_2 = 4; |
466 | 0 | int idx_number_3 = 5; |
467 | 0 | long detection_multiplier, min_rx, min_tx; |
468 | 0 | struct peer *peer; |
469 | |
|
470 | 0 | peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); |
471 | 0 | if (!peer) |
472 | 0 | return CMD_WARNING_CONFIG_FAILED; |
473 | | |
474 | 0 | detection_multiplier = strtol(argv[idx_number_1]->arg, NULL, 10); |
475 | 0 | min_rx = strtol(argv[idx_number_2]->arg, NULL, 10); |
476 | 0 | min_tx = strtol(argv[idx_number_3]->arg, NULL, 10); |
477 | |
|
478 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) |
479 | 0 | bgp_group_configure_bfd(peer); |
480 | 0 | else |
481 | 0 | bgp_peer_configure_bfd(peer, true); |
482 | |
|
483 | 0 | peer->bfd_config->detection_multiplier = detection_multiplier; |
484 | 0 | peer->bfd_config->min_rx = min_rx; |
485 | 0 | peer->bfd_config->min_tx = min_tx; |
486 | 0 | bgp_peer_config_apply(peer, peer->group); |
487 | |
|
488 | 0 | return CMD_SUCCESS; |
489 | 0 | } |
490 | | |
491 | | DEFUN (neighbor_bfd_check_controlplane_failure, |
492 | | neighbor_bfd_check_controlplane_failure_cmd, |
493 | | "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure", |
494 | | NO_STR |
495 | | NEIGHBOR_STR |
496 | | NEIGHBOR_ADDR_STR2 |
497 | | "BFD support\n" |
498 | | "Link dataplane status with BGP controlplane\n") |
499 | 0 | { |
500 | 0 | const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL; |
501 | 0 | int idx_peer = 0; |
502 | 0 | struct peer *peer; |
503 | |
|
504 | 0 | if (no) |
505 | 0 | idx_peer = 2; |
506 | 0 | else |
507 | 0 | idx_peer = 1; |
508 | 0 | peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); |
509 | 0 | if (!peer) |
510 | 0 | return CMD_WARNING_CONFIG_FAILED; |
511 | | |
512 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) |
513 | 0 | bgp_group_configure_bfd(peer); |
514 | 0 | else |
515 | 0 | bgp_peer_configure_bfd(peer, true); |
516 | |
|
517 | 0 | peer->bfd_config->cbit = no == NULL; |
518 | 0 | bgp_peer_config_apply(peer, peer->group); |
519 | |
|
520 | 0 | return CMD_SUCCESS; |
521 | 0 | } |
522 | | |
523 | | DEFUN (no_neighbor_bfd, |
524 | | no_neighbor_bfd_cmd, |
525 | | #if HAVE_BFDD > 0 |
526 | | "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd", |
527 | | #else |
528 | | "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd [(2-255) (50-60000) (50-60000)]", |
529 | | #endif /* HAVE_BFDD */ |
530 | | NO_STR |
531 | | NEIGHBOR_STR |
532 | | NEIGHBOR_ADDR_STR2 |
533 | | "Disables BFD support\n" |
534 | | #if HAVE_BFDD == 0 |
535 | | "Detect Multiplier\n" |
536 | | "Required min receive interval\n" |
537 | | "Desired min transmit interval\n" |
538 | | #endif /* !HAVE_BFDD */ |
539 | | ) |
540 | 0 | { |
541 | 0 | int idx_peer = 2; |
542 | 0 | struct peer *peer; |
543 | |
|
544 | 0 | peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); |
545 | 0 | if (!peer) |
546 | 0 | return CMD_WARNING_CONFIG_FAILED; |
547 | | |
548 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) |
549 | 0 | bgp_group_remove_bfd(peer); |
550 | 0 | else |
551 | 0 | bgp_peer_remove_bfd(peer); |
552 | |
|
553 | 0 | return CMD_SUCCESS; |
554 | 0 | } |
555 | | |
556 | | #if HAVE_BFDD > 0 |
557 | | DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd, |
558 | | "neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile BFDPROF", |
559 | | NEIGHBOR_STR |
560 | | NEIGHBOR_ADDR_STR2 |
561 | | "BFD integration\n" |
562 | | BFD_PROFILE_STR |
563 | | BFD_PROFILE_NAME_STR) |
564 | 0 | { |
565 | 0 | int idx_peer = 1, idx_prof = 4; |
566 | 0 | struct peer *peer; |
567 | |
|
568 | 0 | peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); |
569 | 0 | if (!peer) |
570 | 0 | return CMD_WARNING_CONFIG_FAILED; |
571 | | |
572 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) |
573 | 0 | bgp_group_configure_bfd(peer); |
574 | 0 | else |
575 | 0 | bgp_peer_configure_bfd(peer, true); |
576 | |
|
577 | 0 | strlcpy(peer->bfd_config->profile, argv[idx_prof]->arg, |
578 | 0 | sizeof(peer->bfd_config->profile)); |
579 | 0 | bgp_peer_config_apply(peer, peer->group); |
580 | |
|
581 | 0 | return CMD_SUCCESS; |
582 | 0 | } |
583 | | |
584 | | DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd, |
585 | | "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile [BFDPROF]", |
586 | | NO_STR |
587 | | NEIGHBOR_STR |
588 | | NEIGHBOR_ADDR_STR2 |
589 | | "BFD integration\n" |
590 | | BFD_PROFILE_STR |
591 | | BFD_PROFILE_NAME_STR) |
592 | 0 | { |
593 | 0 | int idx_peer = 2; |
594 | 0 | struct peer *peer; |
595 | |
|
596 | 0 | peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); |
597 | 0 | if (!peer) |
598 | 0 | return CMD_WARNING_CONFIG_FAILED; |
599 | | |
600 | 0 | if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) |
601 | 0 | bgp_group_configure_bfd(peer); |
602 | 0 | else |
603 | 0 | bgp_peer_configure_bfd(peer, true); |
604 | |
|
605 | 0 | peer->bfd_config->profile[0] = 0; |
606 | 0 | bgp_peer_config_apply(peer, peer->group); |
607 | |
|
608 | 0 | return CMD_SUCCESS; |
609 | 0 | } |
610 | | #endif /* HAVE_BFDD */ |
611 | | |
612 | | void bgp_bfd_init(struct event_loop *tm) |
613 | 0 | { |
614 | | /* Initialize BFD client functions */ |
615 | 0 | bfd_protocol_integration_init(zclient, tm); |
616 | | |
617 | | /* "neighbor bfd" commands. */ |
618 | 0 | install_element(BGP_NODE, &neighbor_bfd_cmd); |
619 | 0 | install_element(BGP_NODE, &neighbor_bfd_param_cmd); |
620 | 0 | install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd); |
621 | 0 | install_element(BGP_NODE, &no_neighbor_bfd_cmd); |
622 | |
|
623 | 0 | #if HAVE_BFDD > 0 |
624 | 0 | install_element(BGP_NODE, &neighbor_bfd_profile_cmd); |
625 | 0 | install_element(BGP_NODE, &no_neighbor_bfd_profile_cmd); |
626 | 0 | #endif /* HAVE_BFDD */ |
627 | 0 | } |