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