]> git.proxmox.com Git - mirror_frr.git/blob - lib/northbound_confd.c
Merge pull request #10816 from anlancs/fix-bgdp-local-es-rt
[mirror_frr.git] / lib / northbound_confd.c
1 /*
2 * Copyright (C) 2018 NetDEF, Inc.
3 * Renato Westphal
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
20 #include <zebra.h>
21
22 #include "log.h"
23 #include "lib_errors.h"
24 #include "command.h"
25 #include "debug.h"
26 #include "libfrr.h"
27 #include "lib/version.h"
28 #include "northbound.h"
29
30 #include <confd_lib.h>
31 #include <confd_cdb.h>
32 #include <confd_dp.h>
33 #include <confd_maapi.h>
34
35 DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module");
36
37 static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"};
38
39 static struct thread_master *master;
40 static struct sockaddr confd_addr;
41 static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock;
42 static struct thread *t_cdb_sub, *t_dp_ctl, *t_dp_worker;
43 static struct confd_daemon_ctx *dctx;
44 static struct confd_notification_ctx *live_ctx;
45 static bool confd_connected;
46 static struct list *confd_spoints;
47 static struct nb_transaction *transaction;
48
49 static void frr_confd_finish_cdb(void);
50 static void frr_confd_finish_dp(void);
51 static int frr_confd_finish(void);
52
53 #define flog_err_confd(funcname) \
54 flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \
55 (funcname), confd_strerror(confd_errno), confd_errno, \
56 confd_lasterr())
57
58
59 /* ------------ Utils ------------ */
60
61 /* Get XPath string from ConfD hashed keypath. */
62 static void frr_confd_get_xpath(const confd_hkeypath_t *kp, char *xpath,
63 size_t len)
64 {
65 char *p;
66
67 confd_xpath_pp_kpath(xpath, len, 0, kp);
68
69 /*
70 * Replace double quotes by single quotes (the format accepted by the
71 * northbound API).
72 */
73 p = xpath;
74 while ((p = strchr(p, '"')) != NULL)
75 *p++ = '\'';
76 }
77
78 /* Convert ConfD binary value to a string. */
79 static int frr_confd_val2str(const char *xpath, const confd_value_t *value,
80 char *string, size_t string_size)
81 {
82 struct confd_cs_node *csp;
83
84 csp = confd_cs_node_cd(NULL, xpath);
85 if (!csp) {
86 flog_err_confd("confd_cs_node_cd");
87 return -1;
88 }
89 if (confd_val2str(csp->info.type, value, string, string_size)
90 == CONFD_ERR) {
91 flog_err_confd("confd_val2str");
92 return -1;
93 }
94
95 return 0;
96 }
97
98 /* Obtain list entry from ConfD hashed keypath. */
99 static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t *kp,
100 struct nb_node *nb_node,
101 const void **list_entry)
102 {
103 struct nb_node *nb_node_list;
104 int parent_lists = 0;
105 int curr_list = 0;
106
107 *list_entry = NULL;
108
109 /*
110 * Count the number of YANG lists in the path, disconsidering the
111 * last element.
112 */
113 nb_node_list = nb_node;
114 while (nb_node_list->parent_list) {
115 nb_node_list = nb_node_list->parent_list;
116 parent_lists++;
117 }
118 if (nb_node->snode->nodetype != LYS_LIST && parent_lists == 0)
119 return 0;
120
121 /* Start from the beginning and move down the tree. */
122 for (int i = kp->len; i >= 0; i--) {
123 struct yang_list_keys keys;
124
125 /* Not a YANG list. */
126 if (kp->v[i][0].type != C_BUF)
127 continue;
128
129 /* Obtain list keys. */
130 memset(&keys, 0, sizeof(keys));
131 for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) {
132 strlcpy(keys.key[keys.num],
133 (char *)kp->v[i][j].val.buf.ptr,
134 sizeof(keys.key[keys.num]));
135 keys.num++;
136 }
137
138 /* Obtain northbound node associated to the YANG list. */
139 nb_node_list = nb_node;
140 for (int j = curr_list; j < parent_lists; j++)
141 nb_node_list = nb_node_list->parent_list;
142
143 /* Obtain list entry. */
144 if (!CHECK_FLAG(nb_node_list->flags, F_NB_NODE_KEYLESS_LIST)) {
145 *list_entry = nb_callback_lookup_entry(
146 nb_node, *list_entry, &keys);
147 if (*list_entry == NULL)
148 return -1;
149 } else {
150 unsigned long ptr_ulong;
151
152 /* Retrieve list entry from pseudo-key (string). */
153 if (sscanf(keys.key[0], "%lu", &ptr_ulong) != 1)
154 return -1;
155 *list_entry = (const void *)ptr_ulong;
156 }
157
158 curr_list++;
159 }
160
161 return 0;
162 }
163
164 /* Fill the current date and time into a confd_datetime structure. */
165 static void getdatetime(struct confd_datetime *datetime)
166 {
167 struct tm tm;
168 struct timeval tv;
169
170 gettimeofday(&tv, NULL);
171 gmtime_r(&tv.tv_sec, &tm);
172
173 memset(datetime, 0, sizeof(*datetime));
174 datetime->year = 1900 + tm.tm_year;
175 datetime->month = tm.tm_mon + 1;
176 datetime->day = tm.tm_mday;
177 datetime->sec = tm.tm_sec;
178 datetime->micro = tv.tv_usec;
179 datetime->timezone = 0;
180 datetime->timezone_minutes = 0;
181 datetime->hour = tm.tm_hour;
182 datetime->min = tm.tm_min;
183 }
184
185 /* ------------ CDB code ------------ */
186
187 struct cdb_iter_args {
188 struct nb_config *candidate;
189 bool error;
190 };
191
192 static enum cdb_iter_ret
193 frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op,
194 confd_value_t *oldv, confd_value_t *newv, void *args)
195 {
196 char xpath[XPATH_MAXLEN];
197 struct nb_node *nb_node;
198 enum nb_operation nb_op;
199 struct cdb_iter_args *iter_args = args;
200 char value_str[YANG_VALUE_MAXLEN];
201 struct yang_data *data;
202 char *sb1, *sb2;
203 int ret;
204
205 frr_confd_get_xpath(kp, xpath, sizeof(xpath));
206
207 /*
208 * HACK: obtain value of leaf-list elements from the XPath due to
209 * a bug in the ConfD API.
210 */
211 value_str[0] = '\0';
212 sb1 = strrchr(xpath, '[');
213 sb2 = strrchr(xpath, ']');
214 if (sb1 && sb2 && !strchr(sb1, '=')) {
215 *sb2 = '\0';
216 strlcpy(value_str, sb1 + 1, sizeof(value_str));
217 *sb1 = '\0';
218 }
219
220 nb_node = nb_node_find(xpath);
221 if (!nb_node) {
222 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
223 "%s: unknown data path: %s", __func__, xpath);
224 iter_args->error = true;
225 return ITER_STOP;
226 }
227
228 /* Map operation values. */
229 switch (cdb_op) {
230 case MOP_CREATED:
231 nb_op = NB_OP_CREATE;
232 break;
233 case MOP_DELETED:
234 nb_op = NB_OP_DESTROY;
235 break;
236 case MOP_VALUE_SET:
237 if (nb_operation_is_valid(NB_OP_MODIFY, nb_node->snode))
238 nb_op = NB_OP_MODIFY;
239 else
240 /* Ignore list keys modifications. */
241 return ITER_RECURSE;
242 break;
243 case MOP_MOVED_AFTER:
244 nb_op = NB_OP_MOVE;
245 break;
246 case MOP_MODIFIED:
247 /* We're not interested on this. */
248 return ITER_RECURSE;
249 default:
250 flog_err(EC_LIB_DEVELOPMENT,
251 "%s: unexpected operation %u [xpath %s]", __func__,
252 cdb_op, xpath);
253 iter_args->error = true;
254 return ITER_STOP;
255 }
256
257 /* Convert ConfD value to a string. */
258 if (nb_node->snode->nodetype != LYS_LEAFLIST && newv
259 && frr_confd_val2str(nb_node->xpath, newv, value_str,
260 sizeof(value_str))
261 != 0) {
262 flog_err(EC_LIB_CONFD_DATA_CONVERT,
263 "%s: failed to convert ConfD value to a string",
264 __func__);
265 iter_args->error = true;
266 return ITER_STOP;
267 }
268
269 /* Edit the candidate configuration. */
270 data = yang_data_new(xpath, value_str);
271 ret = nb_candidate_edit(iter_args->candidate, nb_node, nb_op, xpath,
272 NULL, data);
273 yang_data_free(data);
274 if (ret != NB_OK) {
275 flog_warn(
276 EC_LIB_NB_CANDIDATE_EDIT_ERROR,
277 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
278 __func__, nb_operation_name(nb_op), xpath);
279 iter_args->error = true;
280 return ITER_STOP;
281 }
282
283 return ITER_RECURSE;
284 }
285
286 static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
287 {
288 struct nb_context context = {};
289 struct nb_config *candidate;
290 struct cdb_iter_args iter_args;
291 char errmsg[BUFSIZ] = {0};
292 int ret;
293
294 candidate = nb_config_dup(running_config);
295
296 /* Iterate over all configuration changes. */
297 iter_args.candidate = candidate;
298 iter_args.error = false;
299 for (int i = 0; i < reslen; i++) {
300 if (cdb_diff_iterate(fd, subp[i], frr_confd_cdb_diff_iter,
301 ITER_WANT_PREV, &iter_args)
302 != CONFD_OK) {
303 flog_err_confd("cdb_diff_iterate");
304 }
305 }
306 free(subp);
307
308 if (iter_args.error) {
309 nb_config_free(candidate);
310
311 if (cdb_sub_abort_trans(
312 cdb_sub_sock, CONFD_ERRCODE_APPLICATION_INTERNAL, 0,
313 0, "Couldn't apply configuration changes")
314 != CONFD_OK) {
315 flog_err_confd("cdb_sub_abort_trans");
316 return;
317 }
318 return;
319 }
320
321 /*
322 * Validate the configuration changes and allocate all resources
323 * required to apply them.
324 */
325 transaction = NULL;
326 context.client = NB_CLIENT_CONFD;
327 ret = nb_candidate_commit_prepare(&context, candidate, NULL,
328 &transaction, errmsg, sizeof(errmsg));
329 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
330 enum confd_errcode errcode;
331
332 switch (ret) {
333 case NB_ERR_LOCKED:
334 errcode = CONFD_ERRCODE_IN_USE;
335 break;
336 case NB_ERR_RESOURCE:
337 errcode = CONFD_ERRCODE_RESOURCE_DENIED;
338 break;
339 default:
340 errcode = CONFD_ERRCODE_APPLICATION;
341 break;
342 }
343
344 /* Reject the configuration changes. */
345 if (cdb_sub_abort_trans(cdb_sub_sock, errcode, 0, 0, "%s",
346 errmsg)
347 != CONFD_OK) {
348 flog_err_confd("cdb_sub_abort_trans");
349 return;
350 }
351 } else {
352 /* Acknowledge the notification. */
353 if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY)
354 != CONFD_OK) {
355 flog_err_confd("cdb_sync_subscription_socket");
356 return;
357 }
358
359 /* No configuration changes. */
360 if (!transaction)
361 nb_config_free(candidate);
362 }
363 }
364
365 static void frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen)
366 {
367 /*
368 * No need to process the configuration changes again as we're already
369 * keeping track of them in the "transaction" variable.
370 */
371 free(subp);
372
373 /* Apply the transaction. */
374 if (transaction) {
375 struct nb_config *candidate = transaction->config;
376 char errmsg[BUFSIZ] = {0};
377
378 nb_candidate_commit_apply(transaction, true, NULL, errmsg,
379 sizeof(errmsg));
380 nb_config_free(candidate);
381 }
382
383 /* Acknowledge the notification. */
384 if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) {
385 flog_err_confd("cdb_sync_subscription_socket");
386 return;
387 }
388 }
389
390 static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen)
391 {
392 /*
393 * No need to process the configuration changes again as we're already
394 * keeping track of them in the "transaction" variable.
395 */
396 free(subp);
397
398 /* Abort the transaction. */
399 if (transaction) {
400 struct nb_config *candidate = transaction->config;
401 char errmsg[BUFSIZ] = {0};
402
403 nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg));
404 nb_config_free(candidate);
405 }
406
407 /* Acknowledge the notification. */
408 if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) {
409 flog_err_confd("cdb_sync_subscription_socket");
410 return -1;
411 }
412
413 return 0;
414 }
415
416 static void frr_confd_cdb_read_cb(struct thread *thread)
417 {
418 int fd = THREAD_FD(thread);
419 enum cdb_sub_notification cdb_ev;
420 int flags;
421 int *subp = NULL;
422 int reslen = 0;
423
424 thread_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub);
425
426 if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen)
427 != CONFD_OK) {
428 flog_err_confd("cdb_read_subscription_socket2");
429 return;
430 }
431
432 switch (cdb_ev) {
433 case CDB_SUB_PREPARE:
434 frr_confd_cdb_read_cb_prepare(fd, subp, reslen);
435 break;
436 case CDB_SUB_COMMIT:
437 frr_confd_cdb_read_cb_commit(fd, subp, reslen);
438 break;
439 case CDB_SUB_ABORT:
440 frr_confd_cdb_read_cb_abort(fd, subp, reslen);
441 break;
442 default:
443 flog_err_confd("unknown CDB event");
444 break;
445 }
446 }
447
448 /* Trigger CDB subscriptions to read the startup configuration. */
449 static void *thread_cdb_trigger_subscriptions(void *data)
450 {
451 int sock;
452 int *sub_points = NULL, len = 0;
453 struct listnode *node;
454 int *spoint;
455 int i = 0;
456
457 /* Create CDB data socket. */
458 sock = socket(PF_INET, SOCK_STREAM, 0);
459 if (sock < 0) {
460 flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
461 __func__, safe_strerror(errno));
462 return NULL;
463 }
464
465 if (cdb_connect(sock, CDB_DATA_SOCKET, &confd_addr,
466 sizeof(struct sockaddr_in))
467 != CONFD_OK) {
468 flog_err_confd("cdb_connect");
469 return NULL;
470 }
471
472 /*
473 * Fill array containing the subscription point of all loaded YANG
474 * modules.
475 */
476 len = listcount(confd_spoints);
477 sub_points = XCALLOC(MTYPE_CONFD, len * sizeof(int));
478 for (ALL_LIST_ELEMENTS_RO(confd_spoints, node, spoint))
479 sub_points[i++] = *spoint;
480
481 if (cdb_trigger_subscriptions(sock, sub_points, len) != CONFD_OK) {
482 flog_err_confd("cdb_trigger_subscriptions");
483 return NULL;
484 }
485
486 /* Cleanup and exit thread. */
487 XFREE(MTYPE_CONFD, sub_points);
488 cdb_close(sock);
489
490 return NULL;
491 }
492
493 static int frr_confd_subscribe(const struct lysc_node *snode, void *arg)
494 {
495 struct yang_module *module = arg;
496 struct nb_node *nb_node;
497 int *spoint;
498 int ret;
499
500 switch (snode->nodetype) {
501 case LYS_CONTAINER:
502 case LYS_LEAF:
503 case LYS_LEAFLIST:
504 case LYS_LIST:
505 break;
506 default:
507 return YANG_ITER_CONTINUE;
508 }
509
510 if (CHECK_FLAG(snode->flags, LYS_CONFIG_R))
511 return YANG_ITER_CONTINUE;
512
513 nb_node = snode->priv;
514 if (!nb_node)
515 return YANG_ITER_CONTINUE;
516
517 DEBUGD(&nb_dbg_client_confd, "%s: subscribing to '%s'", __func__,
518 nb_node->xpath);
519
520 spoint = XMALLOC(MTYPE_CONFD, sizeof(*spoint));
521 ret = cdb_subscribe2(cdb_sub_sock, CDB_SUB_RUNNING_TWOPHASE,
522 CDB_SUB_WANT_ABORT_ON_ABORT, 3, spoint,
523 module->confd_hash, nb_node->xpath);
524 if (ret != CONFD_OK) {
525 flog_err_confd("cdb_subscribe2");
526 XFREE(MTYPE_CONFD, spoint);
527 return YANG_ITER_CONTINUE;
528 }
529
530 listnode_add(confd_spoints, spoint);
531 return YANG_ITER_CONTINUE;
532 }
533
534 static int frr_confd_init_cdb(void)
535 {
536 struct yang_module *module;
537 pthread_t cdb_trigger_thread;
538
539 /* Create CDB subscription socket. */
540 cdb_sub_sock = socket(PF_INET, SOCK_STREAM, 0);
541 if (cdb_sub_sock < 0) {
542 flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
543 __func__, safe_strerror(errno));
544 return -1;
545 }
546
547 if (cdb_connect(cdb_sub_sock, CDB_SUBSCRIPTION_SOCKET, &confd_addr,
548 sizeof(struct sockaddr_in))
549 != CONFD_OK) {
550 flog_err_confd("cdb_connect");
551 goto error;
552 }
553
554 /* Subscribe to all loaded YANG data modules. */
555 confd_spoints = list_new();
556 RB_FOREACH (module, yang_modules, &yang_modules) {
557 module->confd_hash = confd_str2hash(module->info->ns);
558 if (module->confd_hash == 0) {
559 flog_err(
560 EC_LIB_LIBCONFD,
561 "%s: failed to find hash value for namespace %s",
562 __func__, module->info->ns);
563 goto error;
564 }
565
566 /*
567 * The CDB API doesn't provide a mechanism to subscribe to an
568 * entire YANG module. So we have to find the top level
569 * nodes ourselves and subscribe to their paths.
570 */
571 yang_snodes_iterate(module->info, frr_confd_subscribe, 0,
572 module);
573 }
574
575 if (cdb_subscribe_done(cdb_sub_sock) != CONFD_OK) {
576 flog_err_confd("cdb_subscribe_done");
577 goto error;
578 }
579
580 /* Create short lived pthread to trigger the CDB subscriptions. */
581 if (pthread_create(&cdb_trigger_thread, NULL,
582 thread_cdb_trigger_subscriptions, NULL)) {
583 flog_err(EC_LIB_SYSTEM_CALL, "%s: error creating pthread: %s",
584 __func__, safe_strerror(errno));
585 goto error;
586 }
587 pthread_detach(cdb_trigger_thread);
588
589 thread_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock,
590 &t_cdb_sub);
591
592 return 0;
593
594 error:
595 frr_confd_finish_cdb();
596
597 return -1;
598 }
599
600 static void frr_confd_finish_cdb(void)
601 {
602 if (cdb_sub_sock > 0) {
603 THREAD_OFF(t_cdb_sub);
604 cdb_close(cdb_sub_sock);
605 }
606 }
607
608 /* ------------ DP code ------------ */
609
610 static int frr_confd_transaction_init(struct confd_trans_ctx *tctx)
611 {
612 confd_trans_set_fd(tctx, dp_worker_sock);
613
614 return CONFD_OK;
615 }
616
617 #define CONFD_MAX_CHILD_NODES 32
618
619 static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx,
620 confd_hkeypath_t *kp)
621 {
622 struct nb_node *nb_node;
623 char xpath[XPATH_MAXLEN];
624 struct yang_data *data;
625 confd_value_t v;
626 const void *list_entry = NULL;
627
628 frr_confd_get_xpath(kp, xpath, sizeof(xpath));
629
630 nb_node = nb_node_find(xpath);
631 if (!nb_node) {
632 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
633 "%s: unknown data path: %s", __func__, xpath);
634 confd_data_reply_not_found(tctx);
635 return CONFD_OK;
636 }
637
638 if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) {
639 confd_data_reply_not_found(tctx);
640 return CONFD_OK;
641 }
642
643 data = nb_callback_get_elem(nb_node, xpath, list_entry);
644 if (data) {
645 if (data->value) {
646 CONFD_SET_STR(&v, data->value);
647 confd_data_reply_value(tctx, &v);
648 } else
649 confd_data_reply_found(tctx);
650 yang_data_free(data);
651 } else
652 confd_data_reply_not_found(tctx);
653
654 return CONFD_OK;
655 }
656
657 static int frr_confd_data_get_next(struct confd_trans_ctx *tctx,
658 confd_hkeypath_t *kp, long next)
659 {
660 struct nb_node *nb_node;
661 char xpath[XPATH_MAXLEN];
662 struct yang_data *data;
663 const void *parent_list_entry, *nb_next;
664 confd_value_t v[LIST_MAXKEYS];
665
666 frr_confd_get_xpath(kp, xpath, sizeof(xpath));
667
668 nb_node = nb_node_find(xpath);
669 if (!nb_node) {
670 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
671 "%s: unknown data path: %s", __func__, xpath);
672 confd_data_reply_next_key(tctx, NULL, -1, -1);
673 return CONFD_OK;
674 }
675
676 if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry)
677 != 0) {
678 /* List entry doesn't exist anymore. */
679 confd_data_reply_next_key(tctx, NULL, -1, -1);
680 return CONFD_OK;
681 }
682
683 nb_next = nb_callback_get_next(nb_node, parent_list_entry,
684 (next == -1) ? NULL : (void *)next);
685 if (!nb_next) {
686 /* End of the list or leaf-list. */
687 confd_data_reply_next_key(tctx, NULL, -1, -1);
688 return CONFD_OK;
689 }
690
691 switch (nb_node->snode->nodetype) {
692 case LYS_LIST:
693 if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
694 struct yang_list_keys keys;
695
696 memset(&keys, 0, sizeof(keys));
697 if (nb_callback_get_keys(nb_node, nb_next, &keys)
698 != NB_OK) {
699 flog_warn(EC_LIB_NB_CB_STATE,
700 "%s: failed to get list keys",
701 __func__);
702 confd_data_reply_next_key(tctx, NULL, -1, -1);
703 return CONFD_OK;
704 }
705
706 /* Feed keys to ConfD. */
707 for (size_t i = 0; i < keys.num; i++)
708 CONFD_SET_STR(&v[i], keys.key[i]);
709 confd_data_reply_next_key(tctx, v, keys.num,
710 (long)nb_next);
711 } else {
712 char pointer_str[32];
713
714 /*
715 * ConfD 6.6 user guide, chapter 6.11 (Operational data
716 * lists without keys):
717 * "To support this without having completely separate
718 * APIs, we use a "pseudo" key in the ConfD APIs for
719 * this type of list. This key is not part of the data
720 * model, and completely hidden in the northbound agent
721 * interfaces, but is used with e.g. the get_next() and
722 * get_elem() callbacks as if it were a normal key. This
723 * "pseudo" key is always a single signed 64-bit
724 * integer, i.e. the confd_value_t type is C_INT64. The
725 * values can be chosen arbitrarily by the application,
726 * as long as a key value returned by get_next() can be
727 * used to get the data for the corresponding list entry
728 * with get_elem() or get_object() as usual. It could
729 * e.g. be an index into an array that holds the data,
730 * or even a memory address in integer form".
731 *
732 * Since we're using the CONFD_DAEMON_FLAG_STRINGSONLY
733 * option, we must convert our pseudo-key (a void
734 * pointer) to a string before sending it to confd.
735 */
736 snprintf(pointer_str, sizeof(pointer_str), "%lu",
737 (unsigned long)nb_next);
738 CONFD_SET_STR(&v[0], pointer_str);
739 confd_data_reply_next_key(tctx, v, 1, (long)nb_next);
740 }
741 break;
742 case LYS_LEAFLIST:
743 data = nb_callback_get_elem(nb_node, xpath, nb_next);
744 if (data) {
745 if (data->value) {
746 CONFD_SET_STR(&v[0], data->value);
747 confd_data_reply_next_key(tctx, v, 1,
748 (long)nb_next);
749 }
750 yang_data_free(data);
751 } else
752 confd_data_reply_next_key(tctx, NULL, -1, -1);
753 break;
754 default:
755 break;
756 }
757
758 return CONFD_OK;
759 }
760
761 /*
762 * Optional callback - implemented for performance reasons.
763 */
764 static int frr_confd_data_get_object(struct confd_trans_ctx *tctx,
765 confd_hkeypath_t *kp)
766 {
767 struct nb_node *nb_node;
768 const struct lysc_node *child;
769 char xpath[XPATH_MAXLEN];
770 char xpath_child[XPATH_MAXLEN * 2];
771 struct list *elements;
772 struct yang_data *data;
773 const void *list_entry;
774 confd_value_t values[CONFD_MAX_CHILD_NODES];
775 size_t nvalues = 0;
776
777 frr_confd_get_xpath(kp, xpath, sizeof(xpath));
778
779 nb_node = nb_node_find(xpath);
780 if (!nb_node) {
781 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
782 "%s: unknown data path: %s", __func__, xpath);
783 confd_data_reply_not_found(tctx);
784 return CONFD_ERR;
785 }
786
787 if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) {
788 confd_data_reply_not_found(tctx);
789 return CONFD_OK;
790 }
791
792 elements = yang_data_list_new();
793
794 /* Loop through list child nodes. */
795 LY_LIST_FOR (lysc_node_child(nb_node->snode), child) {
796 struct nb_node *nb_node_child = child->priv;
797 confd_value_t *v;
798
799 if (nvalues > CONFD_MAX_CHILD_NODES)
800 break;
801
802 v = &values[nvalues++];
803
804 /* Non-presence containers, lists and leaf-lists. */
805 if (!nb_node_child->cbs.get_elem) {
806 CONFD_SET_NOEXISTS(v);
807 continue;
808 }
809
810 snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
811 child->name);
812 data = nb_callback_get_elem(nb_node_child, xpath_child,
813 list_entry);
814 if (data) {
815 if (data->value)
816 CONFD_SET_STR(v, data->value);
817 else {
818 /* Presence containers and empty leafs. */
819 CONFD_SET_XMLTAG(
820 v, nb_node_child->confd_hash,
821 confd_str2hash(nb_node_child->snode
822 ->module->ns));
823 }
824 listnode_add(elements, data);
825 } else
826 CONFD_SET_NOEXISTS(v);
827 }
828
829 confd_data_reply_value_array(tctx, values, nvalues);
830
831 /* Release memory. */
832 list_delete(&elements);
833
834 return CONFD_OK;
835 }
836
837 /*
838 * Optional callback - implemented for performance reasons.
839 */
840 static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
841 confd_hkeypath_t *kp, long next)
842 {
843 char xpath[XPATH_MAXLEN];
844 struct nb_node *nb_node;
845 struct list *elements;
846 const void *parent_list_entry;
847 const void *nb_next;
848 #define CONFD_OBJECTS_PER_TIME 100
849 struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1];
850 char pseudo_keys[CONFD_OBJECTS_PER_TIME][32];
851 int nobjects = 0;
852
853 frr_confd_get_xpath(kp, xpath, sizeof(xpath));
854
855 nb_node = nb_node_find(xpath);
856 if (!nb_node) {
857 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
858 "%s: unknown data path: %s", __func__, xpath);
859 confd_data_reply_next_object_array(tctx, NULL, 0, 0);
860 return CONFD_OK;
861 }
862
863 if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry)
864 != 0) {
865 confd_data_reply_next_object_array(tctx, NULL, 0, 0);
866 return CONFD_OK;
867 }
868
869 elements = yang_data_list_new();
870 nb_next = (next == -1) ? NULL : (void *)next;
871
872 memset(objects, 0, sizeof(objects));
873 for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) {
874 struct confd_next_object *object;
875 const struct lysc_node *child;
876 struct yang_data *data;
877 size_t nvalues = 0;
878
879 object = &objects[j];
880
881 nb_next = nb_callback_get_next(nb_node, parent_list_entry,
882 nb_next);
883 if (!nb_next)
884 /* End of the list. */
885 break;
886
887 object->next = (long)nb_next;
888
889 /* Leaf-lists require special handling. */
890 if (nb_node->snode->nodetype == LYS_LEAFLIST) {
891 object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t));
892 data = nb_callback_get_elem(nb_node, xpath, nb_next);
893 assert(data && data->value);
894 CONFD_SET_STR(object->v, data->value);
895 nvalues++;
896 listnode_add(elements, data);
897 goto next;
898 }
899
900 object->v =
901 XMALLOC(MTYPE_CONFD,
902 CONFD_MAX_CHILD_NODES * sizeof(confd_value_t));
903
904 /*
905 * ConfD 6.6 user guide, chapter 6.11 (Operational data lists
906 * without keys):
907 * "In the response to the get_next_object() callback, the data
908 * provider is expected to provide the key values along with the
909 * other leafs in an array that is populated according to the
910 * data model. This must be done also for this type of list,
911 * even though the key isn't actually in the data model. The
912 * "pseudo" key must always be the first element in the array".
913 */
914 if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST)) {
915 confd_value_t *v;
916
917 snprintf(pseudo_keys[j], sizeof(pseudo_keys[j]), "%lu",
918 (unsigned long)nb_next);
919
920 v = &object->v[nvalues++];
921 CONFD_SET_STR(v, pseudo_keys[j]);
922 }
923
924 /* Loop through list child nodes. */
925 LY_LIST_FOR (lysc_node_child(nb_node->snode), child) {
926 struct nb_node *nb_node_child = child->priv;
927 char xpath_child[XPATH_MAXLEN * 2];
928 confd_value_t *v;
929
930 if (nvalues > CONFD_MAX_CHILD_NODES)
931 break;
932
933 v = &object->v[nvalues++];
934
935 /* Non-presence containers, lists and leaf-lists. */
936 if (!nb_node_child->cbs.get_elem) {
937 CONFD_SET_NOEXISTS(v);
938 continue;
939 }
940
941 snprintf(xpath_child, sizeof(xpath_child), "%s/%s",
942 xpath, child->name);
943 data = nb_callback_get_elem(nb_node_child, xpath_child,
944 nb_next);
945 if (data) {
946 if (data->value)
947 CONFD_SET_STR(v, data->value);
948 else {
949 /*
950 * Presence containers and empty leafs.
951 */
952 CONFD_SET_XMLTAG(
953 v, nb_node_child->confd_hash,
954 confd_str2hash(
955 nb_node_child->snode
956 ->module->ns));
957 }
958 listnode_add(elements, data);
959 } else
960 CONFD_SET_NOEXISTS(v);
961 }
962 next:
963 object->n = nvalues;
964 nobjects++;
965 }
966
967 if (nobjects == 0) {
968 confd_data_reply_next_object_array(tctx, NULL, 0, 0);
969 list_delete(&elements);
970 return CONFD_OK;
971 }
972
973 /* Detect end of the list. */
974 if (!nb_next) {
975 nobjects++;
976 objects[nobjects].v = NULL;
977 }
978
979 /* Reply to ConfD. */
980 confd_data_reply_next_object_arrays(tctx, objects, nobjects, 0);
981 if (!nb_next)
982 nobjects--;
983
984 /* Release memory. */
985 list_delete(&elements);
986 for (int j = 0; j < nobjects; j++) {
987 struct confd_next_object *object;
988
989 object = &objects[j];
990 XFREE(MTYPE_CONFD, object->v);
991 }
992
993 return CONFD_OK;
994 }
995
996 static int frr_confd_notification_send(const char *xpath,
997 struct list *arguments)
998 {
999 struct nb_node *nb_node;
1000 struct yang_module *module;
1001 struct confd_datetime now;
1002 confd_tag_value_t *values;
1003 int nvalues;
1004 int i = 0;
1005 struct yang_data *data;
1006 struct listnode *node;
1007 int ret;
1008
1009 nb_node = nb_node_find(xpath);
1010 if (!nb_node) {
1011 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1012 "%s: unknown data path: %s", __func__, xpath);
1013 return -1;
1014 }
1015 module = yang_module_find(nb_node->snode->module->name);
1016 assert(module);
1017
1018 nvalues = 2;
1019 if (arguments)
1020 nvalues += listcount(arguments);
1021
1022 values = XMALLOC(MTYPE_CONFD, nvalues * sizeof(*values));
1023
1024 CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash,
1025 module->confd_hash);
1026 for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) {
1027 struct nb_node *nb_node_arg;
1028
1029 nb_node_arg = nb_node_find(data->xpath);
1030 if (!nb_node_arg) {
1031 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1032 "%s: unknown data path: %s", __func__,
1033 data->xpath);
1034 XFREE(MTYPE_CONFD, values);
1035 return NB_ERR;
1036 }
1037
1038 CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash,
1039 data->value);
1040 }
1041 CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash,
1042 module->confd_hash);
1043
1044 getdatetime(&now);
1045 ret = confd_notification_send(live_ctx, &now, values, nvalues);
1046
1047 /* Release memory. */
1048 XFREE(MTYPE_CONFD, values);
1049
1050 /* Map ConfD return code to northbound return code. */
1051 switch (ret) {
1052 case CONFD_OK:
1053 return NB_OK;
1054 default:
1055 return NB_ERR;
1056 }
1057 }
1058
1059 static int frr_confd_action_init(struct confd_user_info *uinfo)
1060 {
1061 confd_action_set_fd(uinfo, dp_worker_sock);
1062
1063 return CONFD_OK;
1064 }
1065
1066 static int frr_confd_action_execute(struct confd_user_info *uinfo,
1067 struct xml_tag *name, confd_hkeypath_t *kp,
1068 confd_tag_value_t *params, int nparams)
1069 {
1070 char xpath[XPATH_MAXLEN];
1071 struct nb_node *nb_node;
1072 struct list *input;
1073 struct list *output;
1074 struct yang_data *data;
1075 confd_tag_value_t *reply;
1076 int ret = CONFD_OK;
1077 char errmsg[BUFSIZ] = {0};
1078
1079 /* Getting the XPath is tricky. */
1080 if (kp) {
1081 /* This is a YANG RPC. */
1082 frr_confd_get_xpath(kp, xpath, sizeof(xpath));
1083 strlcat(xpath, "/", sizeof(xpath));
1084 strlcat(xpath, confd_hash2str(name->tag), sizeof(xpath));
1085 } else {
1086 /* This is a YANG action. */
1087 snprintf(xpath, sizeof(xpath), "/%s:%s",
1088 confd_ns2prefix(name->ns), confd_hash2str(name->tag));
1089 }
1090
1091 nb_node = nb_node_find(xpath);
1092 if (!nb_node) {
1093 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1094 "%s: unknown data path: %s", __func__, xpath);
1095 return CONFD_ERR;
1096 }
1097
1098 input = yang_data_list_new();
1099 output = yang_data_list_new();
1100
1101 /* Process input nodes. */
1102 for (int i = 0; i < nparams; i++) {
1103 char xpath_input[XPATH_MAXLEN * 2];
1104 char value_str[YANG_VALUE_MAXLEN];
1105
1106 snprintf(xpath_input, sizeof(xpath_input), "%s/%s", xpath,
1107 confd_hash2str(params[i].tag.tag));
1108
1109 if (frr_confd_val2str(xpath_input, &params[i].v, value_str,
1110 sizeof(value_str))
1111 != 0) {
1112 flog_err(
1113 EC_LIB_CONFD_DATA_CONVERT,
1114 "%s: failed to convert ConfD value to a string",
1115 __func__);
1116 ret = CONFD_ERR;
1117 goto exit;
1118 }
1119
1120 data = yang_data_new(xpath_input, value_str);
1121 listnode_add(input, data);
1122 }
1123
1124 /* Execute callback registered for this XPath. */
1125 if (nb_callback_rpc(nb_node, xpath, input, output, errmsg,
1126 sizeof(errmsg))
1127 != NB_OK) {
1128 flog_warn(EC_LIB_NB_CB_RPC, "%s: rpc callback failed: %s",
1129 __func__, xpath);
1130 ret = CONFD_ERR;
1131 goto exit;
1132 }
1133
1134 /* Process output nodes. */
1135 if (listcount(output) > 0) {
1136 struct listnode *node;
1137 int i = 0;
1138
1139 reply = XMALLOC(MTYPE_CONFD,
1140 listcount(output) * sizeof(*reply));
1141
1142 for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
1143 struct nb_node *nb_node_output;
1144 int hash;
1145
1146 nb_node_output = nb_node_find(data->xpath);
1147 if (!nb_node_output) {
1148 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
1149 "%s: unknown data path: %s", __func__,
1150 data->xpath);
1151 goto exit;
1152 }
1153
1154 hash = confd_str2hash(nb_node_output->snode->name);
1155 CONFD_SET_TAG_STR(&reply[i++], hash, data->value);
1156 }
1157 confd_action_reply_values(uinfo, reply, listcount(output));
1158 XFREE(MTYPE_CONFD, reply);
1159 }
1160
1161 exit:
1162 /* Release memory. */
1163 list_delete(&input);
1164 list_delete(&output);
1165
1166 return ret;
1167 }
1168
1169
1170 static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd)
1171 {
1172 int ret;
1173
1174 ret = confd_fd_ready(dctx, fd);
1175 if (ret == CONFD_EOF) {
1176 flog_err_confd("confd_fd_ready");
1177 frr_confd_finish();
1178 return -1;
1179 } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) {
1180 flog_err_confd("confd_fd_ready");
1181 frr_confd_finish();
1182 return -1;
1183 }
1184
1185 return 0;
1186 }
1187
1188 static void frr_confd_dp_ctl_read(struct thread *thread)
1189 {
1190 struct confd_daemon_ctx *dctx = THREAD_ARG(thread);
1191 int fd = THREAD_FD(thread);
1192
1193 thread_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl);
1194
1195 frr_confd_dp_read(dctx, fd);
1196 }
1197
1198 static void frr_confd_dp_worker_read(struct thread *thread)
1199 {
1200 struct confd_daemon_ctx *dctx = THREAD_ARG(thread);
1201 int fd = THREAD_FD(thread);
1202
1203 thread_add_read(master, frr_confd_dp_worker_read, dctx, fd, &t_dp_worker);
1204
1205 frr_confd_dp_read(dctx, fd);
1206 }
1207
1208 static int frr_confd_subscribe_state(const struct lysc_node *snode, void *arg)
1209 {
1210 struct nb_node *nb_node = snode->priv;
1211 struct confd_data_cbs *data_cbs = arg;
1212
1213 if (!nb_node || !CHECK_FLAG(snode->flags, LYS_CONFIG_R))
1214 return YANG_ITER_CONTINUE;
1215 /* We only need to subscribe to the root of the state subtrees. */
1216 if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
1217 return YANG_ITER_CONTINUE;
1218
1219 DEBUGD(&nb_dbg_client_confd,
1220 "%s: providing data to '%s' (callpoint %s)", __func__,
1221 nb_node->xpath, snode->name);
1222
1223 strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint));
1224 if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK)
1225 flog_err_confd("confd_register_data_cb");
1226
1227 return YANG_ITER_CONTINUE;
1228 }
1229
1230 static int frr_confd_init_dp(const char *program_name)
1231 {
1232 struct confd_trans_cbs trans_cbs;
1233 struct confd_data_cbs data_cbs;
1234 struct confd_notification_stream_cbs ncbs;
1235 struct confd_action_cbs acbs;
1236
1237 /* Initialize daemon context. */
1238 dctx = confd_init_daemon(program_name);
1239 if (!dctx) {
1240 flog_err_confd("confd_init_daemon");
1241 goto error;
1242 }
1243
1244 /*
1245 * Inform we want to receive YANG values as raw strings, and that we
1246 * want to provide only strings in the reply functions, regardless of
1247 * the YANG type.
1248 */
1249 confd_set_daemon_flags(dctx, CONFD_DAEMON_FLAG_STRINGSONLY);
1250
1251 /* Establish a control socket. */
1252 dp_ctl_sock = socket(PF_INET, SOCK_STREAM, 0);
1253 if (dp_ctl_sock < 0) {
1254 flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
1255 __func__, safe_strerror(errno));
1256 goto error;
1257 }
1258
1259 if (confd_connect(dctx, dp_ctl_sock, CONTROL_SOCKET, &confd_addr,
1260 sizeof(struct sockaddr_in))
1261 != CONFD_OK) {
1262 flog_err_confd("confd_connect");
1263 goto error;
1264 }
1265
1266 /*
1267 * Establish a worker socket (only one since this plugin runs on a
1268 * single thread).
1269 */
1270 dp_worker_sock = socket(PF_INET, SOCK_STREAM, 0);
1271 if (dp_worker_sock < 0) {
1272 flog_err(EC_LIB_SOCKET, "%s: failed to create socket: %s",
1273 __func__, safe_strerror(errno));
1274 goto error;
1275 }
1276 if (confd_connect(dctx, dp_worker_sock, WORKER_SOCKET, &confd_addr,
1277 sizeof(struct sockaddr_in))
1278 != CONFD_OK) {
1279 flog_err_confd("confd_connect");
1280 goto error;
1281 }
1282
1283 /* Register transaction callback functions. */
1284 memset(&trans_cbs, 0, sizeof(trans_cbs));
1285 trans_cbs.init = frr_confd_transaction_init;
1286 confd_register_trans_cb(dctx, &trans_cbs);
1287
1288 /* Register our read/write callbacks. */
1289 memset(&data_cbs, 0, sizeof(data_cbs));
1290 data_cbs.get_elem = frr_confd_data_get_elem;
1291 data_cbs.exists_optional = frr_confd_data_get_elem;
1292 data_cbs.get_next = frr_confd_data_get_next;
1293 data_cbs.get_object = frr_confd_data_get_object;
1294 data_cbs.get_next_object = frr_confd_data_get_next_object;
1295
1296 /*
1297 * Iterate over all loaded YANG modules and subscribe to the paths
1298 * referent to state data.
1299 */
1300 yang_snodes_iterate(NULL, frr_confd_subscribe_state, 0, &data_cbs);
1301
1302 /* Register notification stream. */
1303 memset(&ncbs, 0, sizeof(ncbs));
1304 ncbs.fd = dp_worker_sock;
1305 /*
1306 * RFC 5277 - Section 3.2.3:
1307 * A NETCONF server implementation supporting the notification
1308 * capability MUST support the "NETCONF" notification event
1309 * stream. This stream contains all NETCONF XML event notifications
1310 * supported by the NETCONF server.
1311 */
1312 strlcpy(ncbs.streamname, "NETCONF", sizeof(ncbs.streamname));
1313 if (confd_register_notification_stream(dctx, &ncbs, &live_ctx)
1314 != CONFD_OK) {
1315 flog_err_confd("confd_register_notification_stream");
1316 goto error;
1317 }
1318
1319 /* Register the action handler callback. */
1320 memset(&acbs, 0, sizeof(acbs));
1321 strlcpy(acbs.actionpoint, "actionpoint", sizeof(acbs.actionpoint));
1322 acbs.init = frr_confd_action_init;
1323 acbs.action = frr_confd_action_execute;
1324 if (confd_register_action_cbs(dctx, &acbs) != CONFD_OK) {
1325 flog_err_confd("confd_register_action_cbs");
1326 goto error;
1327 }
1328
1329 /* Notify we registered all callbacks we wanted. */
1330 if (confd_register_done(dctx) != CONFD_OK) {
1331 flog_err_confd("confd_register_done");
1332 goto error;
1333 }
1334
1335 thread_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock,
1336 &t_dp_ctl);
1337 thread_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock,
1338 &t_dp_worker);
1339
1340 return 0;
1341
1342 error:
1343 frr_confd_finish_dp();
1344
1345 return -1;
1346 }
1347
1348 static void frr_confd_finish_dp(void)
1349 {
1350 if (dp_worker_sock > 0) {
1351 THREAD_OFF(t_dp_worker);
1352 close(dp_worker_sock);
1353 }
1354 if (dp_ctl_sock > 0) {
1355 THREAD_OFF(t_dp_ctl);
1356 close(dp_ctl_sock);
1357 }
1358 if (dctx != NULL)
1359 confd_release_daemon(dctx);
1360 }
1361
1362 /* ------------ CLI ------------ */
1363
1364 DEFUN (debug_nb_confd,
1365 debug_nb_confd_cmd,
1366 "[no] debug northbound client confd",
1367 NO_STR
1368 DEBUG_STR
1369 "Northbound debugging\n"
1370 "Client\n"
1371 "ConfD\n")
1372 {
1373 uint32_t mode = DEBUG_NODE2MODE(vty->node);
1374 bool no = strmatch(argv[0]->text, "no");
1375
1376 DEBUG_MODE_SET(&nb_dbg_client_confd, mode, !no);
1377
1378 return CMD_SUCCESS;
1379 }
1380
1381 static int frr_confd_debug_config_write(struct vty *vty)
1382 {
1383 if (DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_CONF))
1384 vty_out(vty, "debug northbound client confd\n");
1385
1386 return 0;
1387 }
1388
1389 static int frr_confd_debug_set_all(uint32_t flags, bool set)
1390 {
1391 DEBUG_FLAGS_SET(&nb_dbg_client_confd, flags, set);
1392
1393 /* If all modes have been turned off, don't preserve options. */
1394 if (!DEBUG_MODE_CHECK(&nb_dbg_client_confd, DEBUG_MODE_ALL))
1395 DEBUG_CLEAR(&nb_dbg_client_confd);
1396
1397 return 0;
1398 }
1399
1400 static void frr_confd_cli_init(void)
1401 {
1402 hook_register(nb_client_debug_config_write,
1403 frr_confd_debug_config_write);
1404 hook_register(nb_client_debug_set_all, frr_confd_debug_set_all);
1405
1406 install_element(ENABLE_NODE, &debug_nb_confd_cmd);
1407 install_element(CONFIG_NODE, &debug_nb_confd_cmd);
1408 }
1409
1410 /* ------------ Main ------------ */
1411
1412 static int frr_confd_calculate_snode_hash(const struct lysc_node *snode,
1413 void *arg)
1414 {
1415 struct nb_node *nb_node = snode->priv;
1416
1417 if (nb_node)
1418 nb_node->confd_hash = confd_str2hash(snode->name);
1419
1420 return YANG_ITER_CONTINUE;
1421 }
1422
1423 static int frr_confd_init(const char *program_name)
1424 {
1425 struct sockaddr_in *confd_addr4 = (struct sockaddr_in *)&confd_addr;
1426 int debuglevel = CONFD_SILENT;
1427 int ret = -1;
1428
1429 /* Initialize ConfD library. */
1430 confd_init(program_name, stderr, debuglevel);
1431
1432 confd_addr4->sin_family = AF_INET;
1433 confd_addr4->sin_addr.s_addr = inet_addr("127.0.0.1");
1434 confd_addr4->sin_port = htons(CONFD_PORT);
1435 if (confd_load_schemas(&confd_addr, sizeof(struct sockaddr_in))
1436 != CONFD_OK) {
1437 flog_err_confd("confd_load_schemas");
1438 return -1;
1439 }
1440
1441 ret = frr_confd_init_cdb();
1442 if (ret != 0)
1443 goto error;
1444
1445 ret = frr_confd_init_dp(program_name);
1446 if (ret != 0) {
1447 frr_confd_finish_cdb();
1448 goto error;
1449 }
1450
1451 yang_snodes_iterate(NULL, frr_confd_calculate_snode_hash, 0, NULL);
1452
1453 hook_register(nb_notification_send, frr_confd_notification_send);
1454
1455 confd_connected = true;
1456 return 0;
1457
1458 error:
1459 confd_free_schemas();
1460
1461 return ret;
1462 }
1463
1464 static int frr_confd_finish(void)
1465 {
1466 if (!confd_connected)
1467 return 0;
1468
1469 frr_confd_finish_cdb();
1470 frr_confd_finish_dp();
1471
1472 confd_free_schemas();
1473
1474 confd_connected = false;
1475
1476 return 0;
1477 }
1478
1479 static int frr_confd_module_late_init(struct thread_master *tm)
1480 {
1481 master = tm;
1482
1483 if (frr_confd_init(frr_get_progname()) < 0) {
1484 flog_err(EC_LIB_CONFD_INIT,
1485 "failed to initialize the ConfD module");
1486 return -1;
1487 }
1488
1489 hook_register(frr_fini, frr_confd_finish);
1490 frr_confd_cli_init();
1491
1492 return 0;
1493 }
1494
1495 static int frr_confd_module_init(void)
1496 {
1497 hook_register(frr_late_init, frr_confd_module_late_init);
1498
1499 return 0;
1500 }
1501
1502 FRR_MODULE_SETUP(.name = "frr_confd", .version = FRR_VERSION,
1503 .description = "FRR ConfD integration module",
1504 .init = frr_confd_module_init,
1505 );