]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_bfd.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / isisd / isis_bfd.c
1 /*
2 * IS-IS Rout(e)ing protocol - BFD support
3 * Copyright (C) 2018 Christian Franke
4 *
5 * This program 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 Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 #include <zebra.h>
20
21 #include "zclient.h"
22 #include "nexthop.h"
23 #include "bfd.h"
24 #include "lib_errors.h"
25
26 #include "isisd/isis_bfd.h"
27 #include "isisd/isis_zebra.h"
28 #include "isisd/isis_common.h"
29 #include "isisd/isis_constants.h"
30 #include "isisd/isis_adjacency.h"
31 #include "isisd/isis_circuit.h"
32 #include "isisd/isisd.h"
33 #include "isisd/fabricd.h"
34
35 DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session");
36
37 static void adj_bfd_cb(struct bfd_session_params *bsp,
38 const struct bfd_session_status *bss, void *arg)
39 {
40 struct isis_adjacency *adj = arg;
41
42 if (IS_DEBUG_BFD)
43 zlog_debug(
44 "ISIS-BFD: BFD changed status for adjacency %s old %s new %s",
45 isis_adj_name(adj),
46 bfd_get_status_str(bss->previous_state),
47 bfd_get_status_str(bss->state));
48
49 if (bss->state == BFD_STATUS_DOWN
50 && bss->previous_state == BFD_STATUS_UP) {
51 adj->circuit->area->bfd_signalled_down = true;
52 isis_adj_state_change(&adj, ISIS_ADJ_DOWN,
53 "bfd session went down");
54 }
55 }
56
57 static void bfd_handle_adj_down(struct isis_adjacency *adj)
58 {
59 bfd_sess_free(&adj->bfd_session);
60 }
61
62 static void bfd_handle_adj_up(struct isis_adjacency *adj)
63 {
64 struct isis_circuit *circuit = adj->circuit;
65 int family;
66 union g_addr dst_ip;
67 union g_addr src_ip;
68 struct list *local_ips;
69 struct prefix *local_ip;
70
71 if (!circuit->bfd_config.enabled) {
72 if (IS_DEBUG_BFD)
73 zlog_debug(
74 "ISIS-BFD: skipping BFD initialization on adjacency with %s because BFD is not enabled for the circuit",
75 isis_adj_name(adj));
76 goto out;
77 }
78
79 /* If IS-IS IPv6 is configured wait for IPv6 address to be programmed
80 * before starting up BFD
81 */
82 if (circuit->ipv6_router
83 && (listcount(circuit->ipv6_link) == 0
84 || adj->ll_ipv6_count == 0)) {
85 if (IS_DEBUG_BFD)
86 zlog_debug(
87 "ISIS-BFD: skipping BFD initialization on adjacency with %s because IPv6 is enabled but not ready",
88 isis_adj_name(adj));
89 return;
90 }
91
92 /*
93 * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
94 * creating a BFD session over IPv6.
95 */
96 if (circuit->ipv6_router && adj->ll_ipv6_count) {
97 family = AF_INET6;
98 dst_ip.ipv6 = adj->ll_ipv6_addrs[0];
99 local_ips = circuit->ipv6_link;
100 if (list_isempty(local_ips)) {
101 if (IS_DEBUG_BFD)
102 zlog_debug(
103 "ISIS-BFD: skipping BFD initialization: IPv6 enabled and no local IPv6 addresses");
104 goto out;
105 }
106 local_ip = listgetdata(listhead(local_ips));
107 src_ip.ipv6 = local_ip->u.prefix6;
108 } else if (circuit->ip_router && adj->ipv4_address_count) {
109 family = AF_INET;
110 dst_ip.ipv4 = adj->ipv4_addresses[0];
111 local_ips = fabricd_ip_addrs(adj->circuit);
112 if (!local_ips || list_isempty(local_ips)) {
113 if (IS_DEBUG_BFD)
114 zlog_debug(
115 "ISIS-BFD: skipping BFD initialization: IPv4 enabled and no local IPv4 addresses");
116 goto out;
117 }
118 local_ip = listgetdata(listhead(local_ips));
119 src_ip.ipv4 = local_ip->u.prefix4;
120 } else
121 goto out;
122
123 if (adj->bfd_session == NULL)
124 adj->bfd_session = bfd_sess_new(adj_bfd_cb, adj);
125
126 bfd_sess_set_timers(adj->bfd_session, BFD_DEF_DETECT_MULT,
127 BFD_DEF_MIN_RX, BFD_DEF_MIN_TX);
128 if (family == AF_INET)
129 bfd_sess_set_ipv4_addrs(adj->bfd_session, &src_ip.ipv4,
130 &dst_ip.ipv4);
131 else
132 bfd_sess_set_ipv6_addrs(adj->bfd_session, &src_ip.ipv6,
133 &dst_ip.ipv6);
134 bfd_sess_set_interface(adj->bfd_session, adj->circuit->interface->name);
135 bfd_sess_set_vrf(adj->bfd_session,
136 adj->circuit->interface->vrf->vrf_id);
137 bfd_sess_set_profile(adj->bfd_session, circuit->bfd_config.profile);
138 bfd_sess_install(adj->bfd_session);
139 return;
140 out:
141 bfd_handle_adj_down(adj);
142 }
143
144 static int bfd_handle_adj_state_change(struct isis_adjacency *adj)
145 {
146 if (adj->adj_state == ISIS_ADJ_UP)
147 bfd_handle_adj_up(adj);
148 else
149 bfd_handle_adj_down(adj);
150 return 0;
151 }
152
153 static void bfd_adj_cmd(struct isis_adjacency *adj)
154 {
155 if (adj->adj_state == ISIS_ADJ_UP && adj->circuit->bfd_config.enabled)
156 bfd_handle_adj_up(adj);
157 else
158 bfd_handle_adj_down(adj);
159 }
160
161 void isis_bfd_circuit_cmd(struct isis_circuit *circuit)
162 {
163 switch (circuit->circ_type) {
164 case CIRCUIT_T_BROADCAST:
165 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
166 struct list *adjdb = circuit->u.bc.adjdb[level - 1];
167
168 struct listnode *node;
169 struct isis_adjacency *adj;
170
171 if (!adjdb)
172 continue;
173 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
174 bfd_adj_cmd(adj);
175 }
176 break;
177 case CIRCUIT_T_P2P:
178 if (circuit->u.p2p.neighbor)
179 bfd_adj_cmd(circuit->u.p2p.neighbor);
180 break;
181 default:
182 break;
183 }
184 }
185
186 static int bfd_handle_adj_ip_enabled(struct isis_adjacency *adj, int family,
187 bool global)
188 {
189
190 if (family != AF_INET6 || global)
191 return 0;
192
193 if (adj->bfd_session)
194 return 0;
195
196 if (adj->adj_state != ISIS_ADJ_UP)
197 return 0;
198
199 bfd_handle_adj_up(adj);
200
201 return 0;
202 }
203
204 static int bfd_handle_circuit_add_addr(struct isis_circuit *circuit)
205 {
206 struct isis_adjacency *adj;
207 struct listnode *node;
208
209 if (circuit->area == NULL)
210 return 0;
211
212 for (ALL_LIST_ELEMENTS_RO(circuit->area->adjacency_list, node, adj)) {
213 if (adj->bfd_session)
214 continue;
215
216 if (adj->adj_state != ISIS_ADJ_UP)
217 continue;
218
219 bfd_handle_adj_up(adj);
220 }
221
222 return 0;
223 }
224
225 void isis_bfd_init(struct thread_master *tm)
226 {
227 bfd_protocol_integration_init(zclient, tm);
228
229 hook_register(isis_adj_state_change_hook, bfd_handle_adj_state_change);
230 hook_register(isis_adj_ip_enabled_hook, bfd_handle_adj_ip_enabled);
231 hook_register(isis_circuit_add_addr_hook, bfd_handle_circuit_add_addr);
232 }