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