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