]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/config.c
Merge pull request #5450 from donaldsharp/rpki_node_issues
[mirror_frr.git] / bfdd / config.c
CommitLineData
e9e2c950
RZ
1/*********************************************************************
2 * Copyright 2017-2018 Network Device Education Foundation, Inc. ("NetDEF")
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; see the file COPYING; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * config.c: implements the BFD daemon configuration handling.
19 *
20 * Authors
21 * -------
22 * Rafael Zalamena <rzalamena@opensourcerouting.org>
23 */
24
25#include <zebra.h>
26
27#include <string.h>
28
29#include "lib/json.h"
30
31#include "bfd.h"
32
1b88c3cb
DL
33DEFINE_MTYPE_STATIC(BFDD, BFDD_LABEL, "long-lived label memory")
34
e9e2c950
RZ
35/*
36 * Definitions
37 */
38enum peer_list_type {
39 PLT_IPV4,
40 PLT_IPV6,
41 PLT_LABEL,
42};
43
44
45/*
46 * Prototypes
47 */
48static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg);
49static int parse_list(struct json_object *jo, enum peer_list_type plt,
50 bpc_handle h, void *arg);
51static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc);
52static int parse_peer_label_config(struct json_object *jo,
53 struct bfd_peer_cfg *bpc);
54
55static int config_add(struct bfd_peer_cfg *bpc, void *arg);
56static int config_del(struct bfd_peer_cfg *bpc, void *arg);
57
58static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs);
59
60
61/*
62 * Implementation
63 */
64static int config_add(struct bfd_peer_cfg *bpc,
65 void *arg __attribute__((unused)))
66{
67 return ptm_bfd_sess_new(bpc) == NULL;
68}
69
70static int config_del(struct bfd_peer_cfg *bpc,
71 void *arg __attribute__((unused)))
72{
bc50bcc8 73 return ptm_bfd_sess_del(bpc) != 0;
e9e2c950
RZ
74}
75
76static int parse_config_json(struct json_object *jo, bpc_handle h, void *arg)
77{
78 const char *key, *sval;
79 struct json_object *jo_val;
80 struct json_object_iterator joi, join;
81 int error = 0;
82
83 JSON_FOREACH (jo, joi, join) {
84 key = json_object_iter_peek_name(&joi);
85 jo_val = json_object_iter_peek_value(&joi);
86
87 if (strcmp(key, "ipv4") == 0) {
88 error += parse_list(jo_val, PLT_IPV4, h, arg);
89 } else if (strcmp(key, "ipv6") == 0) {
90 error += parse_list(jo_val, PLT_IPV6, h, arg);
91 } else if (strcmp(key, "label") == 0) {
92 error += parse_list(jo_val, PLT_LABEL, h, arg);
93 } else {
94 sval = json_object_get_string(jo_val);
95 log_warning("%s:%d invalid configuration: %s", __func__,
96 __LINE__, sval);
97 error++;
98 }
99 }
100
101 /*
102 * Our callers never call free() on json_object and only expect
103 * the return value, so lets free() it here.
104 */
105 json_object_put(jo);
106
107 return error;
108}
109
110int parse_config(const char *fname)
111{
112 struct json_object *jo;
113
114 jo = json_object_from_file(fname);
115 if (jo == NULL)
116 return -1;
117
118 return parse_config_json(jo, config_add, NULL);
119}
120
121static int parse_list(struct json_object *jo, enum peer_list_type plt,
122 bpc_handle h, void *arg)
123{
124 struct json_object *jo_val;
125 struct bfd_peer_cfg bpc;
126 int allen, idx;
127 int error = 0, result;
128
129 allen = json_object_array_length(jo);
130 for (idx = 0; idx < allen; idx++) {
131 jo_val = json_object_array_get_idx(jo, idx);
132
133 /* Set defaults. */
134 memset(&bpc, 0, sizeof(bpc));
135 bpc.bpc_detectmultiplier = BFD_DEFDETECTMULT;
136 bpc.bpc_recvinterval = BFD_DEFREQUIREDMINRX;
137 bpc.bpc_txinterval = BFD_DEFDESIREDMINTX;
138 bpc.bpc_echointerval = BFD_DEF_REQ_MIN_ECHO;
139
140 switch (plt) {
141 case PLT_IPV4:
142 log_debug("ipv4 peers %d:", allen);
143 bpc.bpc_ipv4 = true;
144 break;
145 case PLT_IPV6:
146 log_debug("ipv6 peers %d:", allen);
147 bpc.bpc_ipv4 = false;
148 break;
149 case PLT_LABEL:
150 log_debug("label peers %d:", allen);
151 if (parse_peer_label_config(jo_val, &bpc) != 0) {
152 error++;
153 continue;
154 }
155 break;
156
157 default:
158 error++;
159 log_error("%s:%d: unsupported peer type", __func__,
160 __LINE__);
161 break;
162 }
163
164 result = parse_peer_config(jo_val, &bpc);
165 error += result;
166 if (result == 0)
167 error += (h(&bpc, arg) != 0);
168 }
169
170 return error;
171}
172
173static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc)
174{
175 const char *key, *sval;
176 struct json_object *jo_val;
177 struct json_object_iterator joi, join;
178 int family_type = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
179 int error = 0;
180
181 log_debug("\tpeer: %s", bpc->bpc_ipv4 ? "ipv4" : "ipv6");
182
183 JSON_FOREACH (jo, joi, join) {
184 key = json_object_iter_peek_name(&joi);
185 jo_val = json_object_iter_peek_value(&joi);
186
187 if (strcmp(key, "multihop") == 0) {
188 bpc->bpc_mhop = json_object_get_boolean(jo_val);
189 log_debug("\tmultihop: %s",
190 bpc->bpc_mhop ? "true" : "false");
191 } else if (strcmp(key, "peer-address") == 0) {
192 sval = json_object_get_string(jo_val);
193 if (strtosa(sval, &bpc->bpc_peer) != 0
194 || bpc->bpc_peer.sa_sin.sin_family != family_type) {
195 log_info(
196 "%s:%d failed to parse peer-address '%s'",
197 __func__, __LINE__, sval);
198 error++;
199 }
200 log_debug("\tpeer-address: %s", sval);
201 } else if (strcmp(key, "local-address") == 0) {
202 sval = json_object_get_string(jo_val);
203 if (strtosa(sval, &bpc->bpc_local) != 0
204 || bpc->bpc_local.sa_sin.sin_family
205 != family_type) {
206 log_info(
207 "%s:%d failed to parse local-address '%s'",
208 __func__, __LINE__, sval);
209 error++;
210 }
211 log_debug("\tlocal-address: %s", sval);
212 } else if (strcmp(key, "local-interface") == 0) {
213 bpc->bpc_has_localif = true;
214 sval = json_object_get_string(jo_val);
215 if (strlcpy(bpc->bpc_localif, sval,
216 sizeof(bpc->bpc_localif))
217 > sizeof(bpc->bpc_localif)) {
218 log_debug("\tlocal-interface: %s (truncated)");
219 error++;
220 } else {
221 log_debug("\tlocal-interface: %s", sval);
222 }
e9e2c950
RZ
223 } else if (strcmp(key, "vrf-name") == 0) {
224 bpc->bpc_has_vrfname = true;
225 sval = json_object_get_string(jo_val);
226 if (strlcpy(bpc->bpc_vrfname, sval,
227 sizeof(bpc->bpc_vrfname))
228 > sizeof(bpc->bpc_vrfname)) {
229 log_debug("\tvrf-name: %s (truncated)", sval);
230 error++;
231 } else {
232 log_debug("\tvrf-name: %s", sval);
233 }
234 } else if (strcmp(key, "detect-multiplier") == 0) {
235 bpc->bpc_detectmultiplier =
236 json_object_get_int64(jo_val);
237 bpc->bpc_has_detectmultiplier = true;
238 log_debug("\tdetect-multiplier: %llu",
239 bpc->bpc_detectmultiplier);
240 } else if (strcmp(key, "receive-interval") == 0) {
241 bpc->bpc_recvinterval = json_object_get_int64(jo_val);
242 bpc->bpc_has_recvinterval = true;
243 log_debug("\treceive-interval: %llu",
244 bpc->bpc_recvinterval);
245 } else if (strcmp(key, "transmit-interval") == 0) {
246 bpc->bpc_txinterval = json_object_get_int64(jo_val);
247 bpc->bpc_has_txinterval = true;
248 log_debug("\ttransmit-interval: %llu",
249 bpc->bpc_txinterval);
250 } else if (strcmp(key, "echo-interval") == 0) {
251 bpc->bpc_echointerval = json_object_get_int64(jo_val);
252 bpc->bpc_has_echointerval = true;
253 log_debug("\techo-interval: %llu",
254 bpc->bpc_echointerval);
255 } else if (strcmp(key, "create-only") == 0) {
256 bpc->bpc_createonly = json_object_get_boolean(jo_val);
257 log_debug("\tcreate-only: %s",
258 bpc->bpc_createonly ? "true" : "false");
259 } else if (strcmp(key, "shutdown") == 0) {
260 bpc->bpc_shutdown = json_object_get_boolean(jo_val);
261 log_debug("\tshutdown: %s",
262 bpc->bpc_shutdown ? "true" : "false");
263 } else if (strcmp(key, "echo-mode") == 0) {
264 bpc->bpc_echo = json_object_get_boolean(jo_val);
265 log_debug("\techo-mode: %s",
266 bpc->bpc_echo ? "true" : "false");
267 } else if (strcmp(key, "label") == 0) {
268 bpc->bpc_has_label = true;
269 sval = json_object_get_string(jo_val);
270 if (strlcpy(bpc->bpc_label, sval,
271 sizeof(bpc->bpc_label))
272 > sizeof(bpc->bpc_label)) {
273 log_debug("\tlabel: %s (truncated)", sval);
274 error++;
275 } else {
276 log_debug("\tlabel: %s", sval);
277 }
278 } else {
279 sval = json_object_get_string(jo_val);
280 log_warning("%s:%d invalid configuration: '%s: %s'",
281 __func__, __LINE__, key, sval);
282 error++;
283 }
284 }
285
286 if (bpc->bpc_peer.sa_sin.sin_family == 0) {
287 log_debug("%s:%d no peer address provided", __func__, __LINE__);
288 error++;
289 }
290
291 return error;
292}
293
294static int parse_peer_label_config(struct json_object *jo,
295 struct bfd_peer_cfg *bpc)
296{
297 struct peer_label *pl;
298 struct json_object *label;
299 const char *sval;
300
301 /* Get label and translate it to BFD daemon key. */
302 if (!json_object_object_get_ex(jo, "label", &label))
303 return 1;
304
305 sval = json_object_get_string(label);
306
307 pl = pl_find(sval);
308 if (pl == NULL)
309 return 1;
310
311 log_debug("\tpeer-label: %s", sval);
312
313 /* Translate the label into BFD address keys. */
79b4a6fc 314 bs_to_bpc(pl->pl_bs, bpc);
e9e2c950
RZ
315
316 return 0;
317}
318
319
320/*
321 * Control socket JSON parsing.
322 */
323int config_request_add(const char *jsonstr)
324{
325 struct json_object *jo;
326
327 jo = json_tokener_parse(jsonstr);
328 if (jo == NULL)
329 return -1;
330
331 return parse_config_json(jo, config_add, NULL);
332}
333
334int config_request_del(const char *jsonstr)
335{
336 struct json_object *jo;
337
338 jo = json_tokener_parse(jsonstr);
339 if (jo == NULL)
340 return -1;
341
342 return parse_config_json(jo, config_del, NULL);
343}
344
345char *config_response(const char *status, const char *error)
346{
347 struct json_object *resp, *jo;
348 char *jsonstr;
349
350 resp = json_object_new_object();
351 if (resp == NULL)
352 return NULL;
353
354 /* Add 'status' response key. */
355 jo = json_object_new_string(status);
356 if (jo == NULL) {
357 json_object_put(resp);
358 return NULL;
359 }
360
361 json_object_object_add(resp, "status", jo);
362
363 /* Add 'error' response key. */
364 if (error != NULL) {
365 jo = json_object_new_string(error);
366 if (jo == NULL) {
367 json_object_put(resp);
368 return NULL;
369 }
370
371 json_object_object_add(resp, "error", jo);
372 }
373
374 /* Generate JSON response. */
375 jsonstr = XSTRDUP(
376 MTYPE_BFDD_NOTIFICATION,
377 json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
378 json_object_put(resp);
379
380 return jsonstr;
381}
382
383char *config_notify(struct bfd_session *bs)
384{
385 struct json_object *resp;
386 char *jsonstr;
387 time_t now;
388
389 resp = json_object_new_object();
390 if (resp == NULL)
391 return NULL;
392
393 json_object_string_add(resp, "op", BCM_NOTIFY_PEER_STATUS);
394
395 json_object_add_peer(resp, bs);
396
397 /* Add status information */
398 json_object_int_add(resp, "id", bs->discrs.my_discr);
399 json_object_int_add(resp, "remote-id", bs->discrs.my_discr);
400
401 switch (bs->ses_state) {
402 case PTM_BFD_UP:
403 json_object_string_add(resp, "state", "up");
404
405 now = monotime(NULL);
406 json_object_int_add(resp, "uptime", now - bs->uptime.tv_sec);
407 break;
408 case PTM_BFD_ADM_DOWN:
409 json_object_string_add(resp, "state", "adm-down");
410 break;
411 case PTM_BFD_DOWN:
412 json_object_string_add(resp, "state", "down");
413
414 now = monotime(NULL);
415 json_object_int_add(resp, "downtime",
416 now - bs->downtime.tv_sec);
417 break;
418 case PTM_BFD_INIT:
419 json_object_string_add(resp, "state", "init");
420 break;
421
422 default:
423 json_object_string_add(resp, "state", "unknown");
424 break;
425 }
426
427 json_object_int_add(resp, "diagnostics", bs->local_diag);
428 json_object_int_add(resp, "remote-diagnostics", bs->remote_diag);
429
430 /* Generate JSON response. */
431 jsonstr = XSTRDUP(
432 MTYPE_BFDD_NOTIFICATION,
433 json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
434 json_object_put(resp);
435
436 return jsonstr;
437}
438
439char *config_notify_config(const char *op, struct bfd_session *bs)
440{
441 struct json_object *resp;
442 char *jsonstr;
443
444 resp = json_object_new_object();
445 if (resp == NULL)
446 return NULL;
447
448 json_object_string_add(resp, "op", op);
449
450 json_object_add_peer(resp, bs);
451
452 /* On peer deletion we don't need to add any additional information. */
453 if (strcmp(op, BCM_NOTIFY_CONFIG_DELETE) == 0)
454 goto skip_config;
455
456 json_object_int_add(resp, "detect-multiplier", bs->detect_mult);
457 json_object_int_add(resp, "receive-interval",
458 bs->timers.required_min_rx / 1000);
f43b9368
RZ
459 json_object_int_add(resp, "transmit-interval",
460 bs->timers.desired_min_tx / 1000);
e9e2c950
RZ
461 json_object_int_add(resp, "echo-interval",
462 bs->timers.required_min_echo / 1000);
463
464 json_object_int_add(resp, "remote-detect-multiplier",
465 bs->remote_detect_mult);
466 json_object_int_add(resp, "remote-receive-interval",
467 bs->remote_timers.required_min_rx / 1000);
468 json_object_int_add(resp, "remote-transmit-interval",
469 bs->remote_timers.desired_min_tx / 1000);
470 json_object_int_add(resp, "remote-echo-interval",
471 bs->remote_timers.required_min_echo / 1000);
472
473 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
474 json_object_boolean_true_add(resp, "echo-mode");
475 else
476 json_object_boolean_false_add(resp, "echo-mode");
477
478 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
479 json_object_boolean_true_add(resp, "shutdown");
480 else
481 json_object_boolean_false_add(resp, "shutdown");
482
483skip_config:
484 /* Generate JSON response. */
485 jsonstr = XSTRDUP(
486 MTYPE_BFDD_NOTIFICATION,
487 json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
488 json_object_put(resp);
489
490 return jsonstr;
491}
492
493int config_notify_request(struct bfd_control_socket *bcs, const char *jsonstr,
494 bpc_handle bh)
495{
496 struct json_object *jo;
497
498 jo = json_tokener_parse(jsonstr);
499 if (jo == NULL)
500 return -1;
501
502 return parse_config_json(jo, bh, bcs);
503}
504
505static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
506{
79b4a6fc
RZ
507 char addr_buf[INET6_ADDRSTRLEN];
508
e9e2c950
RZ
509 /* Add peer 'key' information. */
510 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6))
511 json_object_boolean_true_add(jo, "ipv6");
512 else
513 json_object_boolean_false_add(jo, "ipv6");
514
515 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
516 json_object_boolean_true_add(jo, "multihop");
517 json_object_string_add(jo, "peer-address",
79b4a6fc
RZ
518 inet_ntop(bs->key.family, &bs->key.peer,
519 addr_buf, sizeof(addr_buf)));
e9e2c950 520 json_object_string_add(jo, "local-address",
79b4a6fc
RZ
521 inet_ntop(bs->key.family, &bs->key.local,
522 addr_buf, sizeof(addr_buf)));
523 if (bs->key.vrfname[0])
524 json_object_string_add(jo, "vrf-name", bs->key.vrfname);
e9e2c950
RZ
525 } else {
526 json_object_boolean_false_add(jo, "multihop");
527 json_object_string_add(jo, "peer-address",
79b4a6fc
RZ
528 inet_ntop(bs->key.family, &bs->key.peer,
529 addr_buf, sizeof(addr_buf)));
530 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
531 json_object_string_add(
532 jo, "local-address",
533 inet_ntop(bs->key.family, &bs->key.local,
534 addr_buf, sizeof(addr_buf)));
535 if (bs->key.ifname[0])
e9e2c950 536 json_object_string_add(jo, "local-interface",
79b4a6fc 537 bs->key.ifname);
e9e2c950
RZ
538 }
539
540 if (bs->pl)
541 json_object_string_add(jo, "label", bs->pl->pl_label);
542
543 return 0;
544}
545
546
547/*
548 * Label handling
549 */
550struct peer_label *pl_find(const char *label)
551{
552 struct peer_label *pl;
553
554 TAILQ_FOREACH (pl, &bglobal.bg_pllist, pl_entry) {
555 if (strcmp(pl->pl_label, label) != 0)
556 continue;
557
558 return pl;
559 }
560
561 return NULL;
562}
563
564struct peer_label *pl_new(const char *label, struct bfd_session *bs)
565{
566 struct peer_label *pl;
567
568 pl = XCALLOC(MTYPE_BFDD_LABEL, sizeof(*pl));
e9e2c950
RZ
569
570 if (strlcpy(pl->pl_label, label, sizeof(pl->pl_label))
571 > sizeof(pl->pl_label))
572 log_warning("%s:%d: label was truncated", __func__, __LINE__);
573
574 pl->pl_bs = bs;
575 bs->pl = pl;
576
577 TAILQ_INSERT_HEAD(&bglobal.bg_pllist, pl, pl_entry);
578
579 return pl;
580}
581
582void pl_free(struct peer_label *pl)
583{
584 if (pl == NULL)
585 return;
586
587 /* Remove the pointer back. */
588 pl->pl_bs->pl = NULL;
589
590 TAILQ_REMOVE(&bglobal.bg_pllist, pl, pl_entry);
591 XFREE(MTYPE_BFDD_LABEL, pl);
592}