]>
Commit | Line | Data |
---|---|---|
6c574029 RW |
1 | /* |
2 | * BFD daemon northbound implementation. | |
3 | * | |
4 | * Copyright (C) 2019 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/log.h" | |
26 | #include "lib/northbound.h" | |
27 | ||
28 | #include "bfd.h" | |
29 | #include "bfdd_nb.h" | |
30 | ||
31 | /* | |
32 | * Helpers. | |
33 | */ | |
34 | static void bfd_session_get_key(bool mhop, const struct lyd_node *dnode, | |
35 | struct bfd_key *bk) | |
36 | { | |
37 | const char *ifname = NULL, *vrfname = NULL; | |
38 | struct sockaddr_any psa, lsa; | |
39 | ||
40 | /* Required destination parameter. */ | |
41 | strtosa(yang_dnode_get_string(dnode, "./dest-addr"), &psa); | |
42 | ||
43 | /* Get optional source address. */ | |
44 | memset(&lsa, 0, sizeof(lsa)); | |
45 | if (yang_dnode_exists(dnode, "./source-addr")) | |
46 | strtosa(yang_dnode_get_string(dnode, "./source-addr"), &lsa); | |
47 | ||
48 | /* Get optional interface and vrf names. */ | |
49 | if (yang_dnode_exists(dnode, "./interface")) | |
50 | ifname = yang_dnode_get_string(dnode, "./interface"); | |
51 | if (yang_dnode_exists(dnode, "./vrf")) | |
52 | vrfname = yang_dnode_get_string(dnode, "./vrf"); | |
53 | ||
54 | /* Generate the corresponding key. */ | |
55 | gen_bfd_key(bk, &psa, &lsa, mhop, ifname, vrfname); | |
56 | } | |
57 | ||
58 | static int bfd_session_create(enum nb_event event, const struct lyd_node *dnode, | |
59 | union nb_resource *resource, bool mhop) | |
60 | { | |
61 | struct bfd_session *bs; | |
62 | const char *ifname; | |
63 | struct bfd_key bk; | |
64 | struct prefix p; | |
65 | ||
66 | switch (event) { | |
67 | case NB_EV_VALIDATE: | |
68 | /* | |
69 | * When `dest-addr` is IPv6 and link-local we must | |
70 | * require interface name, otherwise we can't figure | |
71 | * which interface to use to send the packets. | |
72 | */ | |
73 | yang_dnode_get_prefix(&p, dnode, "./dest-addr"); | |
74 | ||
75 | /* | |
76 | * To support old FRR versions we must allow empty | |
77 | * interface to be specified, however that should | |
78 | * change in the future. | |
79 | */ | |
80 | if (yang_dnode_exists(dnode, "./interface")) | |
81 | ifname = yang_dnode_get_string(dnode, "./interface"); | |
82 | else | |
83 | ifname = ""; | |
84 | ||
85 | if (p.family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6) | |
86 | && strlen(ifname) == 0) { | |
87 | zlog_warn( | |
88 | "%s: when using link-local you must specify " | |
89 | "an interface.", | |
90 | __func__); | |
91 | return NB_ERR_VALIDATION; | |
92 | } | |
93 | break; | |
94 | ||
95 | case NB_EV_PREPARE: | |
96 | bfd_session_get_key(mhop, dnode, &bk); | |
97 | bs = bfd_key_lookup(bk); | |
98 | ||
99 | /* This session was already configured by another daemon. */ | |
100 | if (bs != NULL) { | |
101 | /* Now it is configured also by CLI. */ | |
102 | BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); | |
103 | bs->refcount++; | |
104 | ||
105 | resource->ptr = bs; | |
106 | break; | |
107 | } | |
108 | ||
109 | bs = bfd_session_new(); | |
110 | if (bs == NULL) | |
111 | return NB_ERR_RESOURCE; | |
112 | ||
113 | /* Fill the session key. */ | |
114 | bfd_session_get_key(mhop, dnode, &bs->key); | |
115 | ||
116 | /* Set configuration flags. */ | |
117 | bs->refcount = 1; | |
118 | BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); | |
119 | if (mhop) | |
120 | BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_MH); | |
121 | if (bs->key.family == AF_INET6) | |
122 | BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_IPV6); | |
123 | ||
124 | resource->ptr = bs; | |
125 | break; | |
126 | ||
127 | case NB_EV_APPLY: | |
128 | bs = resource->ptr; | |
129 | ||
130 | /* Only attempt to registrate if freshly allocated. */ | |
131 | if (bs->discrs.my_discr == 0 && bs_registrate(bs) == NULL) | |
132 | return NB_ERR_RESOURCE; | |
133 | ||
134 | nb_running_set_entry(dnode, bs); | |
135 | break; | |
136 | ||
137 | case NB_EV_ABORT: | |
138 | bs = resource->ptr; | |
139 | if (bs->refcount <= 1) | |
140 | bfd_session_free(resource->ptr); | |
141 | break; | |
142 | } | |
143 | ||
144 | return NB_OK; | |
145 | } | |
146 | ||
147 | static int bfd_session_destroy(enum nb_event event, | |
148 | const struct lyd_node *dnode, bool mhop) | |
149 | { | |
150 | struct bfd_session *bs; | |
151 | struct bfd_key bk; | |
152 | ||
153 | switch (event) { | |
154 | case NB_EV_VALIDATE: | |
155 | bfd_session_get_key(mhop, dnode, &bk); | |
156 | if (bfd_key_lookup(bk) == NULL) | |
157 | return NB_ERR_INCONSISTENCY; | |
158 | break; | |
159 | ||
160 | case NB_EV_PREPARE: | |
161 | /* NOTHING */ | |
162 | break; | |
163 | ||
164 | case NB_EV_APPLY: | |
165 | bs = nb_running_unset_entry(dnode); | |
166 | /* CLI is not using this session anymore. */ | |
167 | if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0) | |
168 | break; | |
169 | ||
170 | BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG); | |
171 | bs->refcount--; | |
172 | /* There are still daemons using it. */ | |
173 | if (bs->refcount > 0) | |
174 | break; | |
175 | ||
176 | bfd_session_free(bs); | |
177 | break; | |
178 | ||
179 | case NB_EV_ABORT: | |
180 | /* NOTHING */ | |
181 | break; | |
182 | } | |
183 | ||
184 | return NB_OK; | |
185 | } | |
186 | ||
187 | /* | |
188 | * XPath: /frr-bfdd:bfdd/bfd | |
189 | */ | |
190 | int bfdd_bfd_create(enum nb_event event, | |
191 | const struct lyd_node *dnode __attribute__((__unused__)), | |
192 | union nb_resource *resource __attribute__((__unused__))) | |
193 | { | |
194 | /* NOTHING */ | |
195 | return NB_OK; | |
196 | } | |
197 | ||
198 | int bfdd_bfd_destroy(enum nb_event event, const struct lyd_node *dnode) | |
199 | { | |
200 | switch (event) { | |
201 | case NB_EV_VALIDATE: | |
202 | /* NOTHING */ | |
203 | return NB_OK; | |
204 | ||
205 | case NB_EV_PREPARE: | |
206 | /* NOTHING */ | |
207 | return NB_OK; | |
208 | ||
209 | case NB_EV_APPLY: | |
210 | bfd_sessions_remove_manual(); | |
211 | break; | |
212 | ||
213 | case NB_EV_ABORT: | |
214 | /* NOTHING */ | |
215 | return NB_OK; | |
216 | } | |
217 | ||
218 | return NB_OK; | |
219 | } | |
220 | ||
221 | /* | |
222 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop | |
223 | */ | |
224 | int bfdd_bfd_sessions_single_hop_create(enum nb_event event, | |
225 | const struct lyd_node *dnode, | |
226 | union nb_resource *resource) | |
227 | { | |
228 | return bfd_session_create(event, dnode, resource, false); | |
229 | } | |
230 | ||
231 | int bfdd_bfd_sessions_single_hop_destroy(enum nb_event event, | |
232 | const struct lyd_node *dnode) | |
233 | { | |
234 | return bfd_session_destroy(event, dnode, false); | |
235 | } | |
236 | ||
237 | /* | |
238 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/source-addr | |
239 | */ | |
240 | int bfdd_bfd_sessions_single_hop_source_addr_modify(enum nb_event event | |
241 | __attribute__((__unused__)), | |
242 | const struct lyd_node *dnode | |
243 | __attribute__((__unused__)), | |
244 | union nb_resource *resource | |
245 | __attribute__((__unused__))) | |
246 | { | |
247 | return NB_OK; | |
248 | } | |
249 | ||
250 | int bfdd_bfd_sessions_single_hop_source_addr_destroy( | |
251 | enum nb_event event __attribute__((__unused__)), | |
252 | const struct lyd_node *dnode __attribute__((__unused__))) | |
253 | { | |
254 | return NB_OK; | |
255 | } | |
256 | ||
257 | /* | |
258 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/detection-multiplier | |
259 | */ | |
260 | int bfdd_bfd_sessions_single_hop_detection_multiplier_modify( | |
261 | enum nb_event event, const struct lyd_node *dnode, | |
262 | union nb_resource *resource __attribute__((__unused__))) | |
263 | { | |
264 | uint8_t detection_multiplier = yang_dnode_get_uint8(dnode, NULL); | |
265 | struct bfd_session *bs; | |
266 | ||
267 | switch (event) { | |
268 | case NB_EV_VALIDATE: | |
269 | break; | |
270 | ||
271 | case NB_EV_PREPARE: | |
272 | /* NOTHING */ | |
273 | break; | |
274 | ||
275 | case NB_EV_APPLY: | |
276 | bs = nb_running_get_entry(dnode, NULL, true); | |
277 | bs->detect_mult = detection_multiplier; | |
278 | break; | |
279 | ||
280 | case NB_EV_ABORT: | |
281 | /* NOTHING */ | |
282 | break; | |
283 | } | |
284 | ||
285 | return NB_OK; | |
286 | } | |
287 | ||
288 | /* | |
289 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-transmission-interval | |
290 | */ | |
291 | int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify( | |
292 | enum nb_event event, const struct lyd_node *dnode, | |
293 | union nb_resource *resource __attribute__((__unused__))) | |
294 | { | |
295 | uint32_t tx_interval = yang_dnode_get_uint32(dnode, NULL); | |
296 | struct bfd_session *bs; | |
297 | ||
298 | switch (event) { | |
299 | case NB_EV_VALIDATE: | |
300 | if (tx_interval < 10000 || tx_interval > 60000000) | |
301 | return NB_ERR_VALIDATION; | |
302 | break; | |
303 | ||
304 | case NB_EV_PREPARE: | |
305 | /* NOTHING */ | |
306 | break; | |
307 | ||
308 | case NB_EV_APPLY: | |
309 | bs = nb_running_get_entry(dnode, NULL, true); | |
310 | if (tx_interval == bs->timers.desired_min_tx) | |
311 | return NB_OK; | |
312 | ||
313 | bs->timers.desired_min_tx = tx_interval; | |
314 | bfd_set_polling(bs); | |
315 | break; | |
316 | ||
317 | case NB_EV_ABORT: | |
318 | /* NOTHING */ | |
319 | break; | |
320 | } | |
321 | ||
322 | return NB_OK; | |
323 | } | |
324 | ||
325 | /* | |
326 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/required-receive-interval | |
327 | */ | |
328 | int bfdd_bfd_sessions_single_hop_required_receive_interval_modify( | |
329 | enum nb_event event, const struct lyd_node *dnode, | |
330 | union nb_resource *resource __attribute__((__unused__))) | |
331 | { | |
332 | uint32_t rx_interval = yang_dnode_get_uint32(dnode, NULL); | |
333 | struct bfd_session *bs; | |
334 | ||
335 | switch (event) { | |
336 | case NB_EV_VALIDATE: | |
337 | if (rx_interval < 10000 || rx_interval > 60000000) | |
338 | return NB_ERR_VALIDATION; | |
339 | break; | |
340 | ||
341 | case NB_EV_PREPARE: | |
342 | /* NOTHING */ | |
343 | break; | |
344 | ||
345 | case NB_EV_APPLY: | |
346 | bs = nb_running_get_entry(dnode, NULL, true); | |
347 | if (rx_interval == bs->timers.required_min_rx) | |
348 | return NB_OK; | |
349 | ||
350 | bs->timers.required_min_rx = rx_interval; | |
351 | bfd_set_polling(bs); | |
352 | break; | |
353 | ||
354 | case NB_EV_ABORT: | |
355 | /* NOTHING */ | |
356 | break; | |
357 | } | |
358 | ||
359 | return NB_OK; | |
360 | } | |
361 | ||
362 | /* | |
363 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/administrative-down | |
364 | */ | |
365 | int bfdd_bfd_sessions_single_hop_administrative_down_modify( | |
366 | enum nb_event event, const struct lyd_node *dnode, | |
367 | union nb_resource *resource __attribute__((__unused__))) | |
368 | { | |
369 | bool shutdown = yang_dnode_get_bool(dnode, NULL); | |
370 | struct bfd_session *bs; | |
371 | ||
372 | switch (event) { | |
373 | case NB_EV_VALIDATE: | |
374 | case NB_EV_PREPARE: | |
375 | return NB_OK; | |
376 | ||
377 | case NB_EV_APPLY: | |
378 | break; | |
379 | ||
380 | case NB_EV_ABORT: | |
381 | return NB_OK; | |
382 | } | |
383 | ||
384 | bs = nb_running_get_entry(dnode, NULL, true); | |
385 | ||
386 | if (shutdown == false) { | |
387 | if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) | |
388 | return NB_OK; | |
389 | ||
390 | BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); | |
391 | ||
392 | /* Change and notify state change. */ | |
393 | bs->ses_state = PTM_BFD_DOWN; | |
394 | control_notify(bs, bs->ses_state); | |
395 | ||
396 | /* Enable all timers. */ | |
397 | bfd_recvtimer_update(bs); | |
398 | bfd_xmttimer_update(bs, bs->xmt_TO); | |
399 | if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) { | |
400 | bfd_echo_recvtimer_update(bs); | |
401 | bfd_echo_xmttimer_update(bs, bs->echo_xmt_TO); | |
402 | } | |
403 | } else { | |
404 | if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) | |
405 | return NB_OK; | |
406 | ||
407 | BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN); | |
408 | ||
409 | /* Disable all events. */ | |
410 | bfd_recvtimer_delete(bs); | |
411 | bfd_echo_recvtimer_delete(bs); | |
412 | bfd_xmttimer_delete(bs); | |
413 | bfd_echo_xmttimer_delete(bs); | |
414 | ||
415 | /* Change and notify state change. */ | |
416 | bs->ses_state = PTM_BFD_ADM_DOWN; | |
417 | control_notify(bs, bs->ses_state); | |
418 | ||
419 | ptm_bfd_snd(bs, 0); | |
420 | } | |
421 | ||
422 | return NB_OK; | |
423 | } | |
424 | ||
425 | /* | |
426 | * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode | |
427 | */ | |
428 | int bfdd_bfd_sessions_single_hop_echo_mode_modify(enum nb_event event, | |
429 | const struct lyd_node *dnode, | |
430 | union nb_resource *resource | |
431 | __attribute__((__unused__))) | |
432 | { | |
433 | bool echo = yang_dnode_get_bool(dnode, NULL); | |
434 | struct bfd_session *bs; | |
435 | ||
436 | switch (event) { | |
437 | case NB_EV_VALIDATE: | |
438 | case NB_EV_PREPARE: | |
439 | return NB_OK; | |
440 | ||
441 | case NB_EV_APPLY: | |
442 | break; | |
443 | ||
444 | case NB_EV_ABORT: | |
445 | return NB_OK; | |
446 | } | |
447 | ||
448 | bs = nb_running_get_entry(dnode, NULL, true); | |
449 | ||
450 | if (echo == false) { | |
451 | if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) | |
452 | return NB_OK; | |
453 | ||
454 | BFD_UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); | |
455 | ptm_bfd_echo_stop(bs); | |
456 | } else { | |
457 | if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) | |
458 | return NB_OK; | |
459 | ||
460 | BFD_SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO); | |
461 | /* Apply setting immediately. */ | |
462 | if (!BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN)) | |
463 | bs_echo_timer_handler(bs); | |
464 | } | |
465 | ||
466 | return NB_OK; | |
467 | } | |
468 | ||
469 | /* | |
470 | * XPath: | |
471 | * /frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval | |
472 | */ | |
473 | int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( | |
474 | enum nb_event event, const struct lyd_node *dnode, | |
475 | union nb_resource *resource __attribute__((__unused__))) | |
476 | { | |
477 | uint32_t echo_interval = yang_dnode_get_uint32(dnode, NULL); | |
478 | struct bfd_session *bs; | |
479 | ||
480 | switch (event) { | |
481 | case NB_EV_VALIDATE: | |
482 | if (echo_interval < 10000 || echo_interval > 60000000) | |
483 | return NB_ERR_VALIDATION; | |
484 | break; | |
485 | ||
486 | case NB_EV_PREPARE: | |
487 | /* NOTHING */ | |
488 | break; | |
489 | ||
490 | case NB_EV_APPLY: | |
491 | bs = nb_running_get_entry(dnode, NULL, true); | |
492 | if (echo_interval == bs->timers.required_min_echo) | |
493 | return NB_OK; | |
494 | ||
495 | bs->timers.required_min_echo = echo_interval; | |
496 | break; | |
497 | ||
498 | case NB_EV_ABORT: | |
499 | /* NOTHING */ | |
500 | break; | |
501 | } | |
502 | ||
503 | return NB_OK; | |
504 | } | |
505 | ||
506 | /* | |
507 | * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop | |
508 | */ | |
509 | int bfdd_bfd_sessions_multi_hop_create(enum nb_event event, | |
510 | const struct lyd_node *dnode, | |
511 | union nb_resource *resource) | |
512 | { | |
513 | return bfd_session_create(event, dnode, resource, true); | |
514 | } | |
515 | ||
516 | int bfdd_bfd_sessions_multi_hop_destroy(enum nb_event event, | |
517 | const struct lyd_node *dnode) | |
518 | { | |
519 | return bfd_session_destroy(event, dnode, true); | |
520 | } |