]> git.proxmox.com Git - mirror_frr.git/blame - bfdd/config.c
Merge pull request #5721 from mjstapp/vty_copy_to_runn
[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)) {
b7b3d466
DL
218 log_debug("\tlocal-interface: %s (truncated)",
219 sval);
e9e2c950
RZ
220 error++;
221 } else {
222 log_debug("\tlocal-interface: %s", sval);
223 }
e9e2c950
RZ
224 } else if (strcmp(key, "vrf-name") == 0) {
225 bpc->bpc_has_vrfname = true;
226 sval = json_object_get_string(jo_val);
227 if (strlcpy(bpc->bpc_vrfname, sval,
228 sizeof(bpc->bpc_vrfname))
229 > sizeof(bpc->bpc_vrfname)) {
230 log_debug("\tvrf-name: %s (truncated)", sval);
231 error++;
232 } else {
233 log_debug("\tvrf-name: %s", sval);
234 }
235 } else if (strcmp(key, "detect-multiplier") == 0) {
236 bpc->bpc_detectmultiplier =
237 json_object_get_int64(jo_val);
238 bpc->bpc_has_detectmultiplier = true;
b7b3d466 239 log_debug("\tdetect-multiplier: %u",
e9e2c950
RZ
240 bpc->bpc_detectmultiplier);
241 } else if (strcmp(key, "receive-interval") == 0) {
242 bpc->bpc_recvinterval = json_object_get_int64(jo_val);
243 bpc->bpc_has_recvinterval = true;
244 log_debug("\treceive-interval: %llu",
245 bpc->bpc_recvinterval);
246 } else if (strcmp(key, "transmit-interval") == 0) {
247 bpc->bpc_txinterval = json_object_get_int64(jo_val);
248 bpc->bpc_has_txinterval = true;
249 log_debug("\ttransmit-interval: %llu",
250 bpc->bpc_txinterval);
251 } else if (strcmp(key, "echo-interval") == 0) {
252 bpc->bpc_echointerval = json_object_get_int64(jo_val);
253 bpc->bpc_has_echointerval = true;
254 log_debug("\techo-interval: %llu",
255 bpc->bpc_echointerval);
256 } else if (strcmp(key, "create-only") == 0) {
257 bpc->bpc_createonly = json_object_get_boolean(jo_val);
258 log_debug("\tcreate-only: %s",
259 bpc->bpc_createonly ? "true" : "false");
260 } else if (strcmp(key, "shutdown") == 0) {
261 bpc->bpc_shutdown = json_object_get_boolean(jo_val);
262 log_debug("\tshutdown: %s",
263 bpc->bpc_shutdown ? "true" : "false");
264 } else if (strcmp(key, "echo-mode") == 0) {
265 bpc->bpc_echo = json_object_get_boolean(jo_val);
266 log_debug("\techo-mode: %s",
267 bpc->bpc_echo ? "true" : "false");
268 } else if (strcmp(key, "label") == 0) {
269 bpc->bpc_has_label = true;
270 sval = json_object_get_string(jo_val);
271 if (strlcpy(bpc->bpc_label, sval,
272 sizeof(bpc->bpc_label))
273 > sizeof(bpc->bpc_label)) {
274 log_debug("\tlabel: %s (truncated)", sval);
275 error++;
276 } else {
277 log_debug("\tlabel: %s", sval);
278 }
279 } else {
280 sval = json_object_get_string(jo_val);
281 log_warning("%s:%d invalid configuration: '%s: %s'",
282 __func__, __LINE__, key, sval);
283 error++;
284 }
285 }
286
287 if (bpc->bpc_peer.sa_sin.sin_family == 0) {
288 log_debug("%s:%d no peer address provided", __func__, __LINE__);
289 error++;
290 }
291
292 return error;
293}
294
295static int parse_peer_label_config(struct json_object *jo,
296 struct bfd_peer_cfg *bpc)
297{
298 struct peer_label *pl;
299 struct json_object *label;
300 const char *sval;
301
302 /* Get label and translate it to BFD daemon key. */
303 if (!json_object_object_get_ex(jo, "label", &label))
304 return 1;
305
306 sval = json_object_get_string(label);
307
308 pl = pl_find(sval);
309 if (pl == NULL)
310 return 1;
311
312 log_debug("\tpeer-label: %s", sval);
313
314 /* Translate the label into BFD address keys. */
79b4a6fc 315 bs_to_bpc(pl->pl_bs, bpc);
e9e2c950
RZ
316
317 return 0;
318}
319
320
321/*
322 * Control socket JSON parsing.
323 */
324int config_request_add(const char *jsonstr)
325{
326 struct json_object *jo;
327
328 jo = json_tokener_parse(jsonstr);
329 if (jo == NULL)
330 return -1;
331
332 return parse_config_json(jo, config_add, NULL);
333}
334
335int config_request_del(const char *jsonstr)
336{
337 struct json_object *jo;
338
339 jo = json_tokener_parse(jsonstr);
340 if (jo == NULL)
341 return -1;
342
343 return parse_config_json(jo, config_del, NULL);
344}
345
346char *config_response(const char *status, const char *error)
347{
348 struct json_object *resp, *jo;
349 char *jsonstr;
350
351 resp = json_object_new_object();
352 if (resp == NULL)
353 return NULL;
354
355 /* Add 'status' response key. */
356 jo = json_object_new_string(status);
357 if (jo == NULL) {
358 json_object_put(resp);
359 return NULL;
360 }
361
362 json_object_object_add(resp, "status", jo);
363
364 /* Add 'error' response key. */
365 if (error != NULL) {
366 jo = json_object_new_string(error);
367 if (jo == NULL) {
368 json_object_put(resp);
369 return NULL;
370 }
371
372 json_object_object_add(resp, "error", jo);
373 }
374
375 /* Generate JSON response. */
376 jsonstr = XSTRDUP(
377 MTYPE_BFDD_NOTIFICATION,
378 json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
379 json_object_put(resp);
380
381 return jsonstr;
382}
383
384char *config_notify(struct bfd_session *bs)
385{
386 struct json_object *resp;
387 char *jsonstr;
388 time_t now;
389
390 resp = json_object_new_object();
391 if (resp == NULL)
392 return NULL;
393
394 json_object_string_add(resp, "op", BCM_NOTIFY_PEER_STATUS);
395
396 json_object_add_peer(resp, bs);
397
398 /* Add status information */
399 json_object_int_add(resp, "id", bs->discrs.my_discr);
400 json_object_int_add(resp, "remote-id", bs->discrs.my_discr);
401
402 switch (bs->ses_state) {
403 case PTM_BFD_UP:
404 json_object_string_add(resp, "state", "up");
405
406 now = monotime(NULL);
407 json_object_int_add(resp, "uptime", now - bs->uptime.tv_sec);
408 break;
409 case PTM_BFD_ADM_DOWN:
410 json_object_string_add(resp, "state", "adm-down");
411 break;
412 case PTM_BFD_DOWN:
413 json_object_string_add(resp, "state", "down");
414
415 now = monotime(NULL);
416 json_object_int_add(resp, "downtime",
417 now - bs->downtime.tv_sec);
418 break;
419 case PTM_BFD_INIT:
420 json_object_string_add(resp, "state", "init");
421 break;
422
423 default:
424 json_object_string_add(resp, "state", "unknown");
425 break;
426 }
427
428 json_object_int_add(resp, "diagnostics", bs->local_diag);
429 json_object_int_add(resp, "remote-diagnostics", bs->remote_diag);
430
431 /* Generate JSON response. */
432 jsonstr = XSTRDUP(
433 MTYPE_BFDD_NOTIFICATION,
434 json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
435 json_object_put(resp);
436
437 return jsonstr;
438}
439
440char *config_notify_config(const char *op, struct bfd_session *bs)
441{
442 struct json_object *resp;
443 char *jsonstr;
444
445 resp = json_object_new_object();
446 if (resp == NULL)
447 return NULL;
448
449 json_object_string_add(resp, "op", op);
450
451 json_object_add_peer(resp, bs);
452
453 /* On peer deletion we don't need to add any additional information. */
454 if (strcmp(op, BCM_NOTIFY_CONFIG_DELETE) == 0)
455 goto skip_config;
456
457 json_object_int_add(resp, "detect-multiplier", bs->detect_mult);
458 json_object_int_add(resp, "receive-interval",
459 bs->timers.required_min_rx / 1000);
f43b9368
RZ
460 json_object_int_add(resp, "transmit-interval",
461 bs->timers.desired_min_tx / 1000);
e9e2c950
RZ
462 json_object_int_add(resp, "echo-interval",
463 bs->timers.required_min_echo / 1000);
464
465 json_object_int_add(resp, "remote-detect-multiplier",
466 bs->remote_detect_mult);
467 json_object_int_add(resp, "remote-receive-interval",
468 bs->remote_timers.required_min_rx / 1000);
469 json_object_int_add(resp, "remote-transmit-interval",
470 bs->remote_timers.desired_min_tx / 1000);
471 json_object_int_add(resp, "remote-echo-interval",
472 bs->remote_timers.required_min_echo / 1000);
473
474 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
475 json_object_boolean_true_add(resp, "echo-mode");
476 else
477 json_object_boolean_false_add(resp, "echo-mode");
478
479 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN))
480 json_object_boolean_true_add(resp, "shutdown");
481 else
482 json_object_boolean_false_add(resp, "shutdown");
483
484skip_config:
485 /* Generate JSON response. */
486 jsonstr = XSTRDUP(
487 MTYPE_BFDD_NOTIFICATION,
488 json_object_to_json_string_ext(resp, BFDD_JSON_CONV_OPTIONS));
489 json_object_put(resp);
490
491 return jsonstr;
492}
493
494int config_notify_request(struct bfd_control_socket *bcs, const char *jsonstr,
495 bpc_handle bh)
496{
497 struct json_object *jo;
498
499 jo = json_tokener_parse(jsonstr);
500 if (jo == NULL)
501 return -1;
502
503 return parse_config_json(jo, bh, bcs);
504}
505
506static int json_object_add_peer(struct json_object *jo, struct bfd_session *bs)
507{
79b4a6fc
RZ
508 char addr_buf[INET6_ADDRSTRLEN];
509
e9e2c950
RZ
510 /* Add peer 'key' information. */
511 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6))
512 json_object_boolean_true_add(jo, "ipv6");
513 else
514 json_object_boolean_false_add(jo, "ipv6");
515
516 if (BFD_CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
517 json_object_boolean_true_add(jo, "multihop");
518 json_object_string_add(jo, "peer-address",
79b4a6fc
RZ
519 inet_ntop(bs->key.family, &bs->key.peer,
520 addr_buf, sizeof(addr_buf)));
e9e2c950 521 json_object_string_add(jo, "local-address",
79b4a6fc
RZ
522 inet_ntop(bs->key.family, &bs->key.local,
523 addr_buf, sizeof(addr_buf)));
524 if (bs->key.vrfname[0])
525 json_object_string_add(jo, "vrf-name", bs->key.vrfname);
e9e2c950
RZ
526 } else {
527 json_object_boolean_false_add(jo, "multihop");
528 json_object_string_add(jo, "peer-address",
79b4a6fc
RZ
529 inet_ntop(bs->key.family, &bs->key.peer,
530 addr_buf, sizeof(addr_buf)));
531 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local)))
532 json_object_string_add(
533 jo, "local-address",
534 inet_ntop(bs->key.family, &bs->key.local,
535 addr_buf, sizeof(addr_buf)));
536 if (bs->key.ifname[0])
e9e2c950 537 json_object_string_add(jo, "local-interface",
79b4a6fc 538 bs->key.ifname);
e9e2c950
RZ
539 }
540
541 if (bs->pl)
542 json_object_string_add(jo, "label", bs->pl->pl_label);
543
544 return 0;
545}
546
547
548/*
549 * Label handling
550 */
551struct peer_label *pl_find(const char *label)
552{
553 struct peer_label *pl;
554
555 TAILQ_FOREACH (pl, &bglobal.bg_pllist, pl_entry) {
556 if (strcmp(pl->pl_label, label) != 0)
557 continue;
558
559 return pl;
560 }
561
562 return NULL;
563}
564
565struct peer_label *pl_new(const char *label, struct bfd_session *bs)
566{
567 struct peer_label *pl;
568
569 pl = XCALLOC(MTYPE_BFDD_LABEL, sizeof(*pl));
e9e2c950
RZ
570
571 if (strlcpy(pl->pl_label, label, sizeof(pl->pl_label))
572 > sizeof(pl->pl_label))
573 log_warning("%s:%d: label was truncated", __func__, __LINE__);
574
575 pl->pl_bs = bs;
576 bs->pl = pl;
577
578 TAILQ_INSERT_HEAD(&bglobal.bg_pllist, pl, pl_entry);
579
580 return pl;
581}
582
583void pl_free(struct peer_label *pl)
584{
585 if (pl == NULL)
586 return;
587
588 /* Remove the pointer back. */
589 pl->pl_bs->pl = NULL;
590
591 TAILQ_REMOVE(&bglobal.bg_pllist, pl, pl_entry);
592 XFREE(MTYPE_BFDD_LABEL, pl);
593}