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