Line data Source code
1 : /*
2 : * Static daemon BFD integration.
3 : *
4 : * Copyright (C) 2020-2022 Network Device Education Foundation, Inc. ("NetDEF")
5 : * Rafael Zalamena
6 : *
7 : * This program is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 2 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * This program is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with this program; if not, write to the Free Software
19 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 : * 02110-1301 USA.
21 : */
22 :
23 : #include <zebra.h>
24 :
25 : #include "lib/bfd.h"
26 : #include "lib/printfrr.h"
27 : #include "lib/srcdest_table.h"
28 :
29 : #include "staticd/static_routes.h"
30 : #include "staticd/static_zebra.h"
31 : #include "staticd/static_debug.h"
32 :
33 : #include "lib/openbsd-queue.h"
34 :
35 : /*
36 : * Next hop BFD monitoring settings.
37 : */
38 0 : static void static_next_hop_bfd_change(struct static_nexthop *sn,
39 : const struct bfd_session_status *bss)
40 : {
41 0 : switch (bss->state) {
42 : case BSS_UNKNOWN:
43 : /* FALLTHROUGH: no known state yet. */
44 : case BSS_ADMIN_DOWN:
45 : /* NOTHING: we or the remote end administratively shutdown. */
46 : break;
47 0 : case BSS_DOWN:
48 : /* Peer went down, remove this next hop. */
49 0 : DEBUGD(&static_dbg_bfd,
50 : "%s: next hop is down, remove it from RIB", __func__);
51 0 : sn->path_down = true;
52 0 : static_zebra_route_add(sn->pn, true);
53 0 : break;
54 0 : case BSS_UP:
55 : /* Peer is back up, add this next hop. */
56 0 : DEBUGD(&static_dbg_bfd, "%s: next hop is up, add it to RIB",
57 : __func__);
58 0 : sn->path_down = false;
59 0 : static_zebra_route_add(sn->pn, true);
60 0 : break;
61 : }
62 0 : }
63 :
64 0 : static void static_next_hop_bfd_updatecb(
65 : __attribute__((unused)) struct bfd_session_params *bsp,
66 : const struct bfd_session_status *bss, void *arg)
67 : {
68 0 : static_next_hop_bfd_change(arg, bss);
69 0 : }
70 :
71 : static inline int
72 0 : static_next_hop_type_to_family(const struct static_nexthop *sn)
73 : {
74 0 : switch (sn->type) {
75 0 : case STATIC_IPV4_GATEWAY_IFNAME:
76 : case STATIC_IPV6_GATEWAY_IFNAME:
77 : case STATIC_IPV4_GATEWAY:
78 : case STATIC_IPV6_GATEWAY:
79 0 : if (sn->type == STATIC_IPV4_GATEWAY ||
80 : sn->type == STATIC_IPV4_GATEWAY_IFNAME)
81 : return AF_INET;
82 : else
83 0 : return AF_INET6;
84 0 : break;
85 0 : case STATIC_IFNAME:
86 : case STATIC_BLACKHOLE:
87 : default:
88 0 : zlog_err("%s: invalid next hop type", __func__);
89 0 : break;
90 : }
91 :
92 0 : return AF_UNSPEC;
93 : }
94 :
95 0 : void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
96 : const struct lyd_node *dnode)
97 : {
98 0 : bool use_interface;
99 0 : bool use_profile;
100 0 : bool use_source;
101 0 : bool onlink;
102 0 : bool mhop;
103 0 : int family;
104 0 : struct ipaddr source;
105 :
106 0 : use_interface = false;
107 0 : use_source = yang_dnode_exists(dnode, "./source");
108 0 : use_profile = yang_dnode_exists(dnode, "./profile");
109 0 : onlink = yang_dnode_exists(dnode, "../onlink") &&
110 0 : yang_dnode_get_bool(dnode, "../onlink");
111 0 : mhop = yang_dnode_get_bool(dnode, "./multi-hop");
112 :
113 :
114 0 : family = static_next_hop_type_to_family(sn);
115 0 : if (family == AF_UNSPEC)
116 0 : return;
117 :
118 0 : if (sn->type == STATIC_IPV4_GATEWAY_IFNAME ||
119 : sn->type == STATIC_IPV6_GATEWAY_IFNAME)
120 : use_interface = true;
121 :
122 : /* Reconfigure or allocate new memory. */
123 0 : if (sn->bsp == NULL)
124 0 : sn->bsp = bfd_sess_new(static_next_hop_bfd_updatecb, sn);
125 :
126 : /* Configure the session. */
127 0 : if (use_source)
128 0 : yang_dnode_get_ip(&source, dnode, "./source");
129 :
130 0 : if (onlink || mhop == false)
131 0 : bfd_sess_set_auto_source(sn->bsp, false);
132 : else
133 0 : bfd_sess_set_auto_source(sn->bsp, !use_source);
134 :
135 : /* Configure the session.*/
136 0 : if (family == AF_INET)
137 0 : bfd_sess_set_ipv4_addrs(sn->bsp,
138 : use_source ? &source.ip._v4_addr : NULL,
139 0 : &sn->addr.ipv4);
140 0 : else if (family == AF_INET6)
141 0 : bfd_sess_set_ipv6_addrs(sn->bsp,
142 : use_source ? &source.ip._v6_addr : NULL,
143 0 : &sn->addr.ipv6);
144 :
145 0 : bfd_sess_set_interface(sn->bsp, use_interface ? sn->ifname : NULL);
146 :
147 0 : bfd_sess_set_profile(sn->bsp, use_profile ? yang_dnode_get_string(
148 : dnode, "./profile")
149 : : NULL);
150 :
151 0 : bfd_sess_set_hop_count(sn->bsp, (onlink || mhop == false) ? 1 : 254);
152 :
153 : /* Install or update the session. */
154 0 : bfd_sess_install(sn->bsp);
155 :
156 : /* Update current path status. */
157 0 : sn->path_down = (bfd_sess_status(sn->bsp) != BSS_UP);
158 : }
159 :
160 0 : void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn)
161 : {
162 0 : bfd_sess_free(&sn->bsp);
163 :
164 : /* Reset path status. */
165 0 : sn->path_down = false;
166 0 : }
167 :
168 0 : void static_next_hop_bfd_source(struct static_nexthop *sn,
169 : const struct ipaddr *source)
170 : {
171 0 : int family;
172 :
173 0 : if (sn->bsp == NULL)
174 : return;
175 :
176 0 : family = static_next_hop_type_to_family(sn);
177 0 : if (family == AF_UNSPEC)
178 : return;
179 :
180 0 : bfd_sess_set_auto_source(sn->bsp, false);
181 0 : if (family == AF_INET)
182 0 : bfd_sess_set_ipv4_addrs(sn->bsp, &source->ip._v4_addr,
183 0 : &sn->addr.ipv4);
184 0 : else if (family == AF_INET6)
185 0 : bfd_sess_set_ipv6_addrs(sn->bsp, &source->ip._v6_addr,
186 0 : &sn->addr.ipv6);
187 :
188 0 : bfd_sess_install(sn->bsp);
189 : }
190 :
191 0 : void static_next_hop_bfd_auto_source(struct static_nexthop *sn)
192 : {
193 0 : if (sn->bsp == NULL)
194 : return;
195 :
196 0 : bfd_sess_set_auto_source(sn->bsp, true);
197 0 : bfd_sess_install(sn->bsp);
198 : }
199 :
200 0 : void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop)
201 : {
202 0 : if (sn->bsp == NULL)
203 : return;
204 :
205 0 : bfd_sess_set_hop_count(sn->bsp, mhop ? 254 : 1);
206 0 : bfd_sess_install(sn->bsp);
207 : }
208 :
209 0 : void static_next_hop_bfd_profile(struct static_nexthop *sn, const char *name)
210 : {
211 0 : if (sn->bsp == NULL)
212 : return;
213 :
214 0 : bfd_sess_set_profile(sn->bsp, name);
215 0 : bfd_sess_install(sn->bsp);
216 : }
217 :
218 1 : void static_bfd_initialize(struct zclient *zc, struct thread_master *tm)
219 : {
220 : /* Initialize BFD integration library. */
221 1 : bfd_protocol_integration_init(zc, tm);
222 1 : }
223 :
224 : /*
225 : * Display functions
226 : */
227 0 : static void static_bfd_show_nexthop_json(struct vty *vty,
228 : struct json_object *jo,
229 : const struct static_nexthop *sn)
230 : {
231 0 : const struct prefix *dst_p, *src_p;
232 0 : struct json_object *jo_nh;
233 :
234 0 : jo_nh = json_object_new_object();
235 :
236 0 : srcdest_rnode_prefixes(sn->rn, &dst_p, &src_p);
237 0 : if (src_p)
238 0 : json_object_string_addf(jo_nh, "from", "%pFX", src_p);
239 :
240 0 : json_object_string_addf(jo_nh, "prefix", "%pFX", dst_p);
241 0 : json_object_string_add(jo_nh, "vrf", sn->nh_vrfname);
242 :
243 0 : json_object_boolean_add(jo_nh, "installed", !sn->path_down);
244 :
245 0 : json_object_array_add(jo, jo_nh);
246 0 : }
247 :
248 0 : static void static_bfd_show_path_json(struct vty *vty, struct json_object *jo,
249 : struct route_table *rt)
250 : {
251 0 : struct route_node *rn;
252 :
253 0 : for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
254 0 : struct static_route_info *si = static_route_info_from_rnode(rn);
255 0 : struct static_path *sp;
256 :
257 0 : if (si == NULL)
258 0 : continue;
259 :
260 0 : frr_each (static_path_list, &si->path_list, sp) {
261 0 : struct static_nexthop *sn;
262 :
263 0 : frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
264 : /* Skip non configured BFD sessions. */
265 0 : if (sn->bsp == NULL)
266 0 : continue;
267 :
268 0 : static_bfd_show_nexthop_json(vty, jo, sn);
269 : }
270 : }
271 : }
272 0 : }
273 :
274 0 : static void static_bfd_show_json(struct vty *vty)
275 : {
276 0 : struct json_object *jo, *jo_path, *jo_afi_safi;
277 0 : struct vrf *vrf;
278 :
279 0 : jo = json_object_new_object();
280 0 : jo_path = json_object_new_object();
281 :
282 0 : json_object_object_add(jo, "path-list", jo_path);
283 0 : RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
284 0 : const struct static_vrf *svrf = vrf->info;
285 0 : struct route_table *rt;
286 :
287 0 : jo_afi_safi = json_object_new_array();
288 0 : json_object_object_add(jo_path, "ipv4-unicast", jo_afi_safi);
289 0 : rt = svrf->stable[AFI_IP][SAFI_UNICAST];
290 0 : if (rt)
291 0 : static_bfd_show_path_json(vty, jo_afi_safi, rt);
292 :
293 0 : jo_afi_safi = json_object_new_array();
294 0 : json_object_object_add(jo_path, "ipv4-multicast", jo_afi_safi);
295 0 : rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
296 0 : if (rt)
297 0 : static_bfd_show_path_json(vty, jo_afi_safi, rt);
298 :
299 0 : jo_afi_safi = json_object_new_array();
300 0 : json_object_object_add(jo_path, "ipv6-unicast", jo_afi_safi);
301 0 : rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
302 0 : if (rt)
303 0 : static_bfd_show_path_json(vty, jo_afi_safi, rt);
304 : }
305 :
306 0 : vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
307 0 : json_object_free(jo);
308 0 : }
309 :
310 0 : static void static_bfd_show_nexthop(struct vty *vty,
311 : const struct static_nexthop *sn)
312 : {
313 0 : vty_out(vty, " %pRN", sn->rn);
314 :
315 0 : if (sn->bsp == NULL) {
316 0 : vty_out(vty, "\n");
317 0 : return;
318 : }
319 :
320 0 : if (sn->type == STATIC_IPV4_GATEWAY ||
321 : sn->type == STATIC_IPV4_GATEWAY_IFNAME)
322 0 : vty_out(vty, " peer %pI4", &sn->addr.ipv4);
323 0 : else if (sn->type == STATIC_IPV6_GATEWAY ||
324 : sn->type == STATIC_IPV6_GATEWAY_IFNAME)
325 0 : vty_out(vty, " peer %pI6", &sn->addr.ipv6);
326 : else
327 0 : vty_out(vty, " peer unknown");
328 :
329 0 : vty_out(vty, " (status: %s)\n",
330 0 : sn->path_down ? "uninstalled" : "installed");
331 : }
332 :
333 0 : static void static_bfd_show_path(struct vty *vty, struct route_table *rt)
334 : {
335 0 : struct route_node *rn;
336 :
337 0 : for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
338 0 : struct static_route_info *si = static_route_info_from_rnode(rn);
339 0 : struct static_path *sp;
340 :
341 0 : if (si == NULL)
342 0 : continue;
343 :
344 0 : frr_each (static_path_list, &si->path_list, sp) {
345 0 : struct static_nexthop *sn;
346 :
347 0 : frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
348 : /* Skip non configured BFD sessions. */
349 0 : if (sn->bsp == NULL)
350 0 : continue;
351 :
352 0 : static_bfd_show_nexthop(vty, sn);
353 : }
354 : }
355 : }
356 0 : }
357 :
358 0 : void static_bfd_show(struct vty *vty, bool json)
359 : {
360 0 : struct vrf *vrf;
361 :
362 0 : if (json) {
363 0 : static_bfd_show_json(vty);
364 0 : return;
365 : }
366 :
367 0 : vty_out(vty, "Showing BFD monitored static routes:\n");
368 0 : vty_out(vty, "\n Next hops:\n");
369 0 : RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
370 0 : const struct static_vrf *svrf = vrf->info;
371 0 : struct route_table *rt;
372 :
373 0 : vty_out(vty, " VRF %s IPv4 Unicast:\n", vrf->name);
374 0 : rt = svrf->stable[AFI_IP][SAFI_UNICAST];
375 0 : if (rt)
376 0 : static_bfd_show_path(vty, rt);
377 :
378 0 : vty_out(vty, "\n VRF %s IPv4 Multicast:\n", vrf->name);
379 0 : rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
380 0 : if (rt)
381 0 : static_bfd_show_path(vty, rt);
382 :
383 0 : vty_out(vty, "\n VRF %s IPv6 Unicast:\n", vrf->name);
384 0 : rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
385 0 : if (rt)
386 0 : static_bfd_show_path(vty, rt);
387 : }
388 :
389 0 : vty_out(vty, "\n");
390 : }
|