2 * Copyright (C) 2018 NetDEF, Inc.
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)
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
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
23 #include "lib_errors.h"
27 #include "northbound.h"
29 #include <confd_lib.h>
30 #include <confd_cdb.h>
32 #include <confd_maapi.h>
34 DEFINE_MTYPE_STATIC(LIB
, CONFD
, "ConfD module")
36 static struct thread_master
*master
;
37 static struct sockaddr confd_addr
;
38 static int cdb_sub_sock
, dp_ctl_sock
, dp_worker_sock
;
39 static struct thread
*t_cdb_sub
, *t_dp_ctl
, *t_dp_worker
;
40 static struct confd_daemon_ctx
*dctx
;
41 static struct confd_notification_ctx
*live_ctx
;
42 static bool confd_connected
;
43 static struct list
*confd_spoints
;
44 static struct nb_transaction
*transaction
;
46 static void frr_confd_finish_cdb(void);
47 static void frr_confd_finish_dp(void);
48 static int frr_confd_finish(void);
50 #define flog_err_confd(funcname) \
51 flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \
52 (funcname), confd_strerror(confd_errno), confd_errno, \
56 /* ------------ Utils ------------ */
58 /* Get XPath string from ConfD hashed keypath. */
59 static void frr_confd_get_xpath(const confd_hkeypath_t
*kp
, char *xpath
,
64 confd_xpath_pp_kpath(xpath
, len
, 0, kp
);
67 * Replace double quotes by single quotes (the format accepted by the
71 while ((p
= strchr(p
, '"')) != NULL
)
75 /* Convert ConfD binary value to a string. */
76 static int frr_confd_val2str(const char *xpath
, const confd_value_t
*value
,
77 char *string
, size_t string_size
)
79 struct confd_cs_node
*csp
;
81 csp
= confd_cs_node_cd(NULL
, xpath
);
83 flog_err_confd("confd_cs_node_cd");
86 if (confd_val2str(csp
->info
.type
, value
, string
, string_size
)
88 flog_err_confd("confd_val2str");
95 /* Obtain list entry from ConfD hashed keypath. */
96 static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t
*kp
,
97 struct nb_node
*nb_node
,
98 const void **list_entry
)
100 struct nb_node
*nb_node_list
;
101 int parent_lists
= 0;
107 * Count the number of YANG lists in the path, disconsidering the
110 nb_node_list
= nb_node
;
111 while (nb_node_list
->parent_list
) {
112 nb_node_list
= nb_node_list
->parent_list
;
115 if (nb_node
->snode
->nodetype
!= LYS_LIST
&& parent_lists
== 0)
118 /* Start from the beginning and move down the tree. */
119 for (int i
= kp
->len
; i
>= 0; i
--) {
120 struct yang_list_keys keys
;
122 /* Not a YANG list. */
123 if (kp
->v
[i
][0].type
!= C_BUF
)
126 /* Obtain list keys. */
127 memset(&keys
, 0, sizeof(keys
));
128 for (int j
= 0; kp
->v
[i
][j
].type
!= C_NOEXISTS
; j
++) {
129 strlcpy(keys
.key
[keys
.num
],
130 (char *)kp
->v
[i
][j
].val
.buf
.ptr
,
131 sizeof(keys
.key
[keys
.num
]));
135 /* Obtain northbound node associated to the YANG list. */
136 nb_node_list
= nb_node
;
137 for (int j
= curr_list
; j
< parent_lists
; j
++)
138 nb_node_list
= nb_node_list
->parent_list
;
140 /* Obtain list entry. */
142 nb_node_list
->cbs
.lookup_entry(*list_entry
, &keys
);
143 if (*list_entry
== NULL
)
152 /* Fill the current date and time into a confd_datetime structure. */
153 static void getdatetime(struct confd_datetime
*datetime
)
158 gettimeofday(&tv
, NULL
);
159 gmtime_r(&tv
.tv_sec
, &tm
);
161 memset(datetime
, 0, sizeof(*datetime
));
162 datetime
->year
= 1900 + tm
.tm_year
;
163 datetime
->month
= tm
.tm_mon
+ 1;
164 datetime
->day
= tm
.tm_mday
;
165 datetime
->sec
= tm
.tm_sec
;
166 datetime
->micro
= tv
.tv_usec
;
167 datetime
->timezone
= 0;
168 datetime
->timezone_minutes
= 0;
169 datetime
->hour
= tm
.tm_hour
;
170 datetime
->min
= tm
.tm_min
;
173 /* ------------ CDB code ------------ */
175 struct cdb_iter_args
{
176 struct nb_config
*candidate
;
180 static enum cdb_iter_ret
181 frr_confd_cdb_diff_iter(confd_hkeypath_t
*kp
, enum cdb_iter_op cdb_op
,
182 confd_value_t
*oldv
, confd_value_t
*newv
, void *args
)
184 char xpath
[XPATH_MAXLEN
];
185 struct nb_node
*nb_node
;
186 enum nb_operation nb_op
;
187 struct cdb_iter_args
*iter_args
= args
;
188 char value_str
[YANG_VALUE_MAXLEN
];
189 struct yang_data
*data
;
193 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
196 * HACK: obtain value of leaf-list elements from the XPath due to
197 * a bug in the ConfD API.
200 sb1
= strrchr(xpath
, '[');
201 sb2
= strrchr(xpath
, ']');
202 if (sb1
&& sb2
&& !strchr(sb1
, '=')) {
204 strlcpy(value_str
, sb1
+ 1, sizeof(value_str
));
208 nb_node
= nb_node_find(xpath
);
210 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
211 "%s: unknown data path: %s", __func__
, xpath
);
212 iter_args
->error
= true;
216 /* Map operation values. */
219 nb_op
= NB_OP_CREATE
;
222 nb_op
= NB_OP_DELETE
;
225 if (nb_operation_is_valid(NB_OP_MODIFY
, nb_node
->snode
))
226 nb_op
= NB_OP_MODIFY
;
228 /* Ignore list keys modifications. */
231 case MOP_MOVED_AFTER
:
235 /* We're not interested on this. */
238 flog_err(EC_LIB_DEVELOPMENT
,
239 "%s: unexpected operation %u [xpath %s]", __func__
,
241 iter_args
->error
= true;
245 /* Convert ConfD value to a string. */
246 if (nb_node
->snode
->nodetype
!= LYS_LEAFLIST
&& newv
247 && frr_confd_val2str(nb_node
->xpath
, newv
, value_str
,
250 flog_err(EC_LIB_CONFD_DATA_CONVERT
,
251 "%s: failed to convert ConfD value to a string",
253 iter_args
->error
= true;
257 /* Edit the candidate configuration. */
258 data
= yang_data_new(xpath
, value_str
);
259 ret
= nb_candidate_edit(iter_args
->candidate
, nb_node
, nb_op
, xpath
,
261 yang_data_free(data
);
264 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
265 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
266 __func__
, nb_operation_name(nb_op
), xpath
);
267 iter_args
->error
= true;
274 static int frr_confd_cdb_read_cb_prepare(int fd
, int *subp
, int reslen
)
276 struct nb_config
*candidate
;
277 struct cdb_iter_args iter_args
;
280 candidate
= nb_config_dup(running_config
);
282 /* Iterate over all configuration changes. */
283 iter_args
.candidate
= candidate
;
284 iter_args
.error
= false;
285 for (int i
= 0; i
< reslen
; i
++) {
286 if (cdb_diff_iterate(fd
, subp
[i
], frr_confd_cdb_diff_iter
,
287 ITER_WANT_PREV
, &iter_args
)
289 flog_err_confd("cdb_diff_iterate");
294 if (iter_args
.error
) {
295 nb_config_free(candidate
);
297 if (cdb_sub_abort_trans(
298 cdb_sub_sock
, CONFD_ERRCODE_APPLICATION_INTERNAL
, 0,
299 0, "Couldn't apply configuration changes")
301 flog_err_confd("cdb_sub_abort_trans");
308 * Validate the configuration changes and allocate all resources
309 * required to apply them.
312 ret
= nb_candidate_commit_prepare(candidate
, NB_CLIENT_CONFD
, NULL
,
314 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
) {
315 enum confd_errcode errcode
;
320 errcode
= CONFD_ERRCODE_IN_USE
;
321 errmsg
= "Configuration is locked by another process";
323 case NB_ERR_RESOURCE
:
324 errcode
= CONFD_ERRCODE_RESOURCE_DENIED
;
325 errmsg
= "Failed do allocate resources";
328 errcode
= CONFD_ERRCODE_INTERNAL
;
329 errmsg
= "Internal error";
333 /* Reject the configuration changes. */
334 if (cdb_sub_abort_trans(cdb_sub_sock
, errcode
, 0, 0, "%s",
337 flog_err_confd("cdb_sub_abort_trans");
341 /* Acknowledge the notification. */
342 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
)
344 flog_err_confd("cdb_sync_subscription_socket");
348 /* No configuration changes. */
350 nb_config_free(candidate
);
356 static int frr_confd_cdb_read_cb_commit(int fd
, int *subp
, int reslen
)
359 * No need to process the configuration changes again as we're already
360 * keeping track of them in the "transaction" variable.
364 /* Apply the transaction. */
366 struct nb_config
*candidate
= transaction
->config
;
368 nb_candidate_commit_apply(transaction
, true, NULL
);
369 nb_config_free(candidate
);
372 /* Acknowledge the notification. */
373 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
) != CONFD_OK
) {
374 flog_err_confd("cdb_sync_subscription_socket");
381 static int frr_confd_cdb_read_cb_abort(int fd
, int *subp
, int reslen
)
384 * No need to process the configuration changes again as we're already
385 * keeping track of them in the "transaction" variable.
389 /* Abort the transaction. */
391 struct nb_config
*candidate
= transaction
->config
;
393 nb_candidate_commit_abort(transaction
);
394 nb_config_free(candidate
);
397 /* Acknowledge the notification. */
398 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
) != CONFD_OK
) {
399 flog_err_confd("cdb_sync_subscription_socket");
406 static int frr_confd_cdb_read_cb(struct thread
*thread
)
408 int fd
= THREAD_FD(thread
);
409 enum cdb_sub_notification cdb_ev
;
415 thread_add_read(master
, frr_confd_cdb_read_cb
, NULL
, fd
, &thread
);
417 if (cdb_read_subscription_socket2(fd
, &cdb_ev
, &flags
, &subp
, &reslen
)
419 flog_err_confd("cdb_read_subscription_socket2");
424 case CDB_SUB_PREPARE
:
425 return frr_confd_cdb_read_cb_prepare(fd
, subp
, reslen
);
427 return frr_confd_cdb_read_cb_commit(fd
, subp
, reslen
);
429 return frr_confd_cdb_read_cb_abort(fd
, subp
, reslen
);
431 flog_err_confd("unknown CDB event");
436 /* Trigger CDB subscriptions to read the startup configuration. */
437 static void *thread_cdb_trigger_subscriptions(void *data
)
440 int *sub_points
= NULL
, len
= 0;
441 struct listnode
*node
;
445 /* Create CDB data socket. */
446 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
448 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
449 __func__
, safe_strerror(errno
));
453 if (cdb_connect(sock
, CDB_DATA_SOCKET
, &confd_addr
,
454 sizeof(struct sockaddr_in
))
456 flog_err_confd("cdb_connect");
461 * Fill array containing the subscription point of all loaded YANG
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
;
469 if (cdb_trigger_subscriptions(sock
, sub_points
, len
) != CONFD_OK
) {
470 flog_err_confd("cdb_trigger_subscriptions");
474 /* Cleanup and exit thread. */
475 XFREE(MTYPE_CONFD
, sub_points
);
481 static int frr_confd_init_cdb(void)
483 struct yang_module
*module
;
484 pthread_t cdb_trigger_thread
;
486 /* Create CDB subscription socket. */
487 cdb_sub_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
488 if (cdb_sub_sock
< 0) {
489 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
490 __func__
, safe_strerror(errno
));
494 if (cdb_connect(cdb_sub_sock
, CDB_SUBSCRIPTION_SOCKET
, &confd_addr
,
495 sizeof(struct sockaddr_in
))
497 flog_err_confd("cdb_connect");
501 /* Subscribe to all loaded YANG data modules. */
502 confd_spoints
= list_new();
503 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
504 struct lys_node
*snode
;
506 module
->confd_hash
= confd_str2hash(module
->info
->ns
);
507 if (module
->confd_hash
== 0) {
510 "%s: failed to find hash value for namespace %s",
511 __func__
, module
->info
->ns
);
516 * The CDB API doesn't provide a mechanism to subscribe to an
517 * entire YANG module. So we have to find the top level
518 * nodes ourselves and subscribe to their paths.
520 LY_TREE_FOR (module
->info
->data
, snode
) {
521 struct nb_node
*nb_node
;
525 switch (snode
->nodetype
) {
535 if (CHECK_FLAG(snode
->flags
, LYS_CONFIG_R
))
538 nb_node
= snode
->priv
;
539 if (debug_northbound
)
540 zlog_debug("%s: subscribing to '%s'", __func__
,
543 spoint
= XMALLOC(MTYPE_CONFD
, sizeof(*spoint
));
544 ret
= cdb_subscribe2(
545 cdb_sub_sock
, CDB_SUB_RUNNING_TWOPHASE
,
546 CDB_SUB_WANT_ABORT_ON_ABORT
, 3, spoint
,
547 module
->confd_hash
, nb_node
->xpath
);
548 if (ret
!= CONFD_OK
) {
549 flog_err_confd("cdb_subscribe2");
550 XFREE(MTYPE_CONFD
, spoint
);
552 listnode_add(confd_spoints
, spoint
);
556 if (cdb_subscribe_done(cdb_sub_sock
) != CONFD_OK
) {
557 flog_err_confd("cdb_subscribe_done");
561 /* Create short lived pthread to trigger the CDB subscriptions. */
562 if (pthread_create(&cdb_trigger_thread
, NULL
,
563 thread_cdb_trigger_subscriptions
, NULL
)) {
564 flog_err(EC_LIB_SYSTEM_CALL
, "%s: error creating pthread: %s",
565 __func__
, safe_strerror(errno
));
568 pthread_detach(cdb_trigger_thread
);
570 thread_add_read(master
, frr_confd_cdb_read_cb
, NULL
, cdb_sub_sock
,
576 frr_confd_finish_cdb();
581 static void frr_confd_finish_cdb(void)
583 if (cdb_sub_sock
> 0) {
584 THREAD_OFF(t_cdb_sub
);
585 cdb_close(cdb_sub_sock
);
589 /* ------------ DP code ------------ */
591 static int frr_confd_transaction_init(struct confd_trans_ctx
*tctx
)
593 confd_trans_set_fd(tctx
, dp_worker_sock
);
598 #define CONFD_MAX_CHILD_NODES 32
600 static int frr_confd_data_get_elem(struct confd_trans_ctx
*tctx
,
601 confd_hkeypath_t
*kp
)
603 struct nb_node
*nb_node
;
605 struct yang_data
*data
;
607 const void *list_entry
= NULL
;
609 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
611 nb_node
= nb_node_find(xpath
);
613 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
614 "%s: unknown data path: %s", __func__
, xpath
);
615 confd_data_reply_not_found(tctx
);
619 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &list_entry
) != 0) {
620 confd_data_reply_not_found(tctx
);
624 data
= nb_node
->cbs
.get_elem(xpath
, list_entry
);
627 CONFD_SET_STR(&v
, data
->value
);
628 confd_data_reply_value(tctx
, &v
);
630 confd_data_reply_found(tctx
);
631 yang_data_free(data
);
633 confd_data_reply_not_found(tctx
);
638 static int frr_confd_data_get_next(struct confd_trans_ctx
*tctx
,
639 confd_hkeypath_t
*kp
, long next
)
641 struct nb_node
*nb_node
;
643 struct yang_list_keys keys
;
644 struct yang_data
*data
;
645 const void *parent_list_entry
, *nb_next
;
646 confd_value_t v
[LIST_MAXKEYS
];
648 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
650 nb_node
= nb_node_find(xpath
);
652 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
653 "%s: unknown data path: %s", __func__
, xpath
);
654 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
658 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &parent_list_entry
)
660 /* List entry doesn't exist anymore. */
661 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
665 nb_next
= nb_node
->cbs
.get_next(parent_list_entry
,
666 (next
== -1) ? NULL
: (void *)next
);
668 /* End of the list or leaf-list. */
669 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
673 switch (nb_node
->snode
->nodetype
) {
675 memset(&keys
, 0, sizeof(keys
));
676 if (nb_node
->cbs
.get_keys(nb_next
, &keys
) != NB_OK
) {
677 flog_warn(EC_LIB_NB_CB_STATE
,
678 "%s: failed to get list keys", __func__
);
679 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
683 /* Feed keys to ConfD. */
684 for (size_t i
= 0; i
< keys
.num
; i
++)
685 CONFD_SET_STR(&v
[i
], keys
.key
[i
]);
686 confd_data_reply_next_key(tctx
, v
, keys
.num
, (long)nb_next
);
689 data
= nb_node
->cbs
.get_elem(xpath
, nb_next
);
692 CONFD_SET_STR(&v
[0], data
->value
);
693 confd_data_reply_next_key(tctx
, v
, 1,
696 yang_data_free(data
);
698 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
708 * Optional callback - implemented for performance reasons.
710 static int frr_confd_data_get_object(struct confd_trans_ctx
*tctx
,
711 confd_hkeypath_t
*kp
)
713 struct nb_node
*nb_node
;
714 const struct lys_node
*child
;
716 char xpath_child
[XPATH_MAXLEN
];
717 struct list
*elements
;
718 struct yang_data
*data
;
719 const void *list_entry
;
720 confd_value_t values
[CONFD_MAX_CHILD_NODES
];
723 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
725 nb_node
= nb_node_find(xpath
);
727 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
728 "%s: unknown data path: %s", __func__
, xpath
);
729 confd_data_reply_not_found(tctx
);
733 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &list_entry
) != 0) {
734 confd_data_reply_not_found(tctx
);
738 elements
= yang_data_list_new();
740 /* Loop through list child nodes. */
741 LY_TREE_FOR (nb_node
->snode
->child
, child
) {
742 struct nb_node
*nb_node_child
= child
->priv
;
745 if (nvalues
> CONFD_MAX_CHILD_NODES
)
748 v
= &values
[nvalues
++];
750 /* Non-presence containers, lists and leaf-lists. */
751 if (!nb_node_child
->cbs
.get_elem
) {
752 CONFD_SET_NOEXISTS(v
);
756 snprintf(xpath_child
, sizeof(xpath_child
), "%s/%s", xpath
,
758 data
= nb_node_child
->cbs
.get_elem(xpath_child
, list_entry
);
761 CONFD_SET_STR(v
, data
->value
);
763 /* Presence containers and empty leafs. */
765 v
, nb_node_child
->confd_hash
,
766 confd_str2hash(nb_node_child
->snode
769 listnode_add(elements
, data
);
771 CONFD_SET_NOEXISTS(v
);
774 confd_data_reply_value_array(tctx
, values
, nvalues
);
776 /* Release memory. */
777 list_delete(&elements
);
783 * Optional callback - implemented for performance reasons.
785 static int frr_confd_data_get_next_object(struct confd_trans_ctx
*tctx
,
786 confd_hkeypath_t
*kp
, long next
)
789 struct nb_node
*nb_node
;
790 struct list
*elements
;
791 const void *parent_list_entry
;
793 #define CONFD_OBJECTS_PER_TIME 100
794 struct confd_next_object objects
[CONFD_OBJECTS_PER_TIME
+ 1];
797 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
799 nb_node
= nb_node_find(xpath
);
801 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
802 "%s: unknown data path: %s", __func__
, xpath
);
803 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
807 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &parent_list_entry
)
809 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
813 elements
= yang_data_list_new();
814 nb_next
= (next
== -1) ? NULL
: (void *)next
;
816 memset(objects
, 0, sizeof(objects
));
817 for (int j
= 0; j
< CONFD_OBJECTS_PER_TIME
; j
++) {
818 struct confd_next_object
*object
;
819 struct lys_node
*child
;
820 struct yang_data
*data
;
823 object
= &objects
[j
];
825 nb_next
= nb_node
->cbs
.get_next(parent_list_entry
, nb_next
);
827 /* End of the list. */
830 object
->next
= (long)nb_next
;
832 /* Leaf-lists require special handling. */
833 if (nb_node
->snode
->nodetype
== LYS_LEAFLIST
) {
834 object
->v
= XMALLOC(MTYPE_CONFD
, sizeof(confd_value_t
));
835 data
= nb_node
->cbs
.get_elem(xpath
, nb_next
);
836 assert(data
&& data
->value
);
837 CONFD_SET_STR(object
->v
, data
->value
);
839 listnode_add(elements
, data
);
845 CONFD_MAX_CHILD_NODES
* sizeof(confd_value_t
));
847 /* Loop through list child nodes. */
848 LY_TREE_FOR (nb_node
->snode
->child
, child
) {
849 struct nb_node
*nb_node_child
= child
->priv
;
850 char xpath_child
[XPATH_MAXLEN
];
853 if (nvalues
> CONFD_MAX_CHILD_NODES
)
856 v
= &object
->v
[nvalues
++];
858 /* Non-presence containers, lists and leaf-lists. */
859 if (!nb_node_child
->cbs
.get_elem
) {
860 CONFD_SET_NOEXISTS(v
);
864 snprintf(xpath_child
, sizeof(xpath_child
), "%s/%s",
866 data
= nb_node_child
->cbs
.get_elem(xpath_child
,
870 CONFD_SET_STR(v
, data
->value
);
873 * Presence containers and empty leafs.
876 v
, nb_node_child
->confd_hash
,
881 listnode_add(elements
, data
);
883 CONFD_SET_NOEXISTS(v
);
891 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
892 list_delete(&elements
);
896 /* Detect end of the list. */
899 objects
[nobjects
].v
= NULL
;
902 /* Reply to ConfD. */
903 confd_data_reply_next_object_arrays(tctx
, objects
, nobjects
, 0);
907 /* Release memory. */
908 list_delete(&elements
);
909 for (int j
= 0; j
< nobjects
; j
++) {
910 struct confd_next_object
*object
;
912 object
= &objects
[j
];
913 XFREE(MTYPE_CONFD
, object
->v
);
919 static int frr_confd_notification_send(const char *xpath
,
920 struct list
*arguments
)
922 struct nb_node
*nb_node
;
923 struct yang_module
*module
;
924 struct confd_datetime now
;
925 confd_tag_value_t
*values
;
928 struct yang_data
*data
;
929 struct listnode
*node
;
932 nb_node
= nb_node_find(xpath
);
934 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
935 "%s: unknown data path: %s", __func__
, xpath
);
938 module
= yang_module_find(nb_node
->snode
->module
->name
);
943 nvalues
+= listcount(arguments
);
945 values
= XMALLOC(MTYPE_CONFD
, nvalues
* sizeof(*values
));
947 CONFD_SET_TAG_XMLBEGIN(&values
[i
++], nb_node
->confd_hash
,
949 for (ALL_LIST_ELEMENTS_RO(arguments
, node
, data
)) {
950 struct nb_node
*nb_node_arg
;
952 nb_node_arg
= nb_node_find(data
->xpath
);
954 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
955 "%s: unknown data path: %s", __func__
,
957 XFREE(MTYPE_CONFD
, values
);
961 CONFD_SET_TAG_STR(&values
[i
++], nb_node_arg
->confd_hash
,
964 CONFD_SET_TAG_XMLEND(&values
[i
++], nb_node
->confd_hash
,
968 ret
= confd_notification_send(live_ctx
, &now
, values
, nvalues
);
970 /* Release memory. */
971 XFREE(MTYPE_CONFD
, values
);
973 /* Map ConfD return code to northbound return code. */
982 static int frr_confd_action_init(struct confd_user_info
*uinfo
)
984 confd_action_set_fd(uinfo
, dp_worker_sock
);
989 static int frr_confd_action_execute(struct confd_user_info
*uinfo
,
990 struct xml_tag
*name
, confd_hkeypath_t
*kp
,
991 confd_tag_value_t
*params
, int nparams
)
994 struct nb_node
*nb_node
;
997 struct yang_data
*data
;
998 confd_tag_value_t
*reply
;
1001 /* Getting the XPath is tricky. */
1003 /* This is a YANG RPC. */
1004 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
1005 strlcat(xpath
, "/", sizeof(xpath
));
1006 strlcat(xpath
, confd_hash2str(name
->tag
), sizeof(xpath
));
1008 /* This is a YANG action. */
1009 snprintf(xpath
, sizeof(xpath
), "/%s:%s",
1010 confd_ns2prefix(name
->ns
), confd_hash2str(name
->tag
));
1013 nb_node
= nb_node_find(xpath
);
1015 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
1016 "%s: unknown data path: %s", __func__
, xpath
);
1020 input
= yang_data_list_new();
1021 output
= yang_data_list_new();
1023 /* Process input nodes. */
1024 for (int i
= 0; i
< nparams
; i
++) {
1025 char xpath_input
[BUFSIZ
];
1026 char value_str
[YANG_VALUE_MAXLEN
];
1028 snprintf(xpath_input
, sizeof(xpath_input
), "%s/%s", xpath
,
1029 confd_hash2str(params
[i
].tag
.tag
));
1031 if (frr_confd_val2str(xpath_input
, ¶ms
[i
].v
, value_str
,
1035 EC_LIB_CONFD_DATA_CONVERT
,
1036 "%s: failed to convert ConfD value to a string",
1042 data
= yang_data_new(xpath_input
, value_str
);
1043 listnode_add(input
, data
);
1046 /* Execute callback registered for this XPath. */
1047 if (nb_node
->cbs
.rpc(xpath
, input
, output
) != NB_OK
) {
1048 flog_warn(EC_LIB_NB_CB_RPC
, "%s: rpc callback failed: %s",
1054 /* Process output nodes. */
1055 if (listcount(output
) > 0) {
1056 struct listnode
*node
;
1059 reply
= XMALLOC(MTYPE_CONFD
,
1060 listcount(output
) * sizeof(*reply
));
1062 for (ALL_LIST_ELEMENTS_RO(output
, node
, data
)) {
1063 struct nb_node
*nb_node_output
;
1066 nb_node_output
= nb_node_find(data
->xpath
);
1067 if (!nb_node_output
) {
1068 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
1069 "%s: unknown data path: %s", __func__
,
1074 hash
= confd_str2hash(nb_node_output
->snode
->name
);
1075 CONFD_SET_TAG_STR(&reply
[i
++], hash
, data
->value
);
1077 confd_action_reply_values(uinfo
, reply
, listcount(output
));
1078 XFREE(MTYPE_CONFD
, reply
);
1082 /* Release memory. */
1083 list_delete(&input
);
1084 list_delete(&output
);
1090 static int frr_confd_dp_read(struct thread
*thread
)
1092 struct confd_daemon_ctx
*dctx
= THREAD_ARG(thread
);
1093 int fd
= THREAD_FD(thread
);
1097 thread_add_read(master
, frr_confd_dp_read
, dctx
, fd
, &thread
);
1099 ret
= confd_fd_ready(dctx
, fd
);
1100 if (ret
== CONFD_EOF
) {
1101 flog_err_confd("confd_fd_ready");
1104 } else if (ret
== CONFD_ERR
&& confd_errno
!= CONFD_ERR_EXTERNAL
) {
1105 flog_err_confd("confd_fd_ready");
1113 static int frr_confd_subscribe_state(const struct lys_node
*snode
, void *arg
)
1115 struct nb_node
*nb_node
= snode
->priv
;
1116 struct confd_data_cbs
*data_cbs
= arg
;
1118 if (!CHECK_FLAG(snode
->flags
, LYS_CONFIG_R
))
1119 return YANG_ITER_CONTINUE
;
1120 /* We only need to subscribe to the root of the state subtrees. */
1121 if (snode
->parent
&& CHECK_FLAG(snode
->parent
->flags
, LYS_CONFIG_R
))
1122 return YANG_ITER_CONTINUE
;
1124 if (debug_northbound
)
1125 zlog_debug("%s: providing data to '%s' (callpoint %s)",
1126 __func__
, nb_node
->xpath
, snode
->name
);
1128 strlcpy(data_cbs
->callpoint
, snode
->name
, sizeof(data_cbs
->callpoint
));
1129 if (confd_register_data_cb(dctx
, data_cbs
) != CONFD_OK
)
1130 flog_err_confd("confd_register_data_cb");
1132 return YANG_ITER_CONTINUE
;
1135 static int frr_confd_init_dp(const char *program_name
)
1137 struct confd_trans_cbs trans_cbs
;
1138 struct confd_data_cbs data_cbs
;
1139 struct confd_notification_stream_cbs ncbs
;
1140 struct confd_action_cbs acbs
;
1142 /* Initialize daemon context. */
1143 dctx
= confd_init_daemon(program_name
);
1145 flog_err_confd("confd_init_daemon");
1150 * Inform we want to receive YANG values as raw strings, and that we
1151 * want to provide only strings in the reply functions, regardless of
1154 confd_set_daemon_flags(dctx
, CONFD_DAEMON_FLAG_STRINGSONLY
);
1156 /* Establish a control socket. */
1157 dp_ctl_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
1158 if (dp_ctl_sock
< 0) {
1159 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
1160 __func__
, safe_strerror(errno
));
1164 if (confd_connect(dctx
, dp_ctl_sock
, CONTROL_SOCKET
, &confd_addr
,
1165 sizeof(struct sockaddr_in
))
1167 flog_err_confd("confd_connect");
1172 * Establish a worker socket (only one since this plugin runs on a
1175 dp_worker_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
1176 if (dp_worker_sock
< 0) {
1177 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
1178 __func__
, safe_strerror(errno
));
1181 if (confd_connect(dctx
, dp_worker_sock
, WORKER_SOCKET
, &confd_addr
,
1182 sizeof(struct sockaddr_in
))
1184 flog_err_confd("confd_connect");
1188 /* Register transaction callback functions. */
1189 memset(&trans_cbs
, 0, sizeof(trans_cbs
));
1190 trans_cbs
.init
= frr_confd_transaction_init
;
1191 confd_register_trans_cb(dctx
, &trans_cbs
);
1193 /* Register our read/write callbacks. */
1194 memset(&data_cbs
, 0, sizeof(data_cbs
));
1195 data_cbs
.get_elem
= frr_confd_data_get_elem
;
1196 data_cbs
.exists_optional
= frr_confd_data_get_elem
;
1197 data_cbs
.get_next
= frr_confd_data_get_next
;
1198 data_cbs
.get_object
= frr_confd_data_get_object
;
1199 data_cbs
.get_next_object
= frr_confd_data_get_next_object
;
1202 * Iterate over all loaded YANG modules and subscribe to the paths
1203 * referent to state data.
1205 yang_snodes_iterate_all(frr_confd_subscribe_state
, 0, &data_cbs
);
1207 /* Register notification stream. */
1208 memset(&ncbs
, 0, sizeof(ncbs
));
1209 ncbs
.fd
= dp_worker_sock
;
1211 * RFC 5277 - Section 3.2.3:
1212 * A NETCONF server implementation supporting the notification
1213 * capability MUST support the "NETCONF" notification event
1214 * stream. This stream contains all NETCONF XML event notifications
1215 * supported by the NETCONF server.
1217 strlcpy(ncbs
.streamname
, "NETCONF", sizeof(ncbs
.streamname
));
1218 if (confd_register_notification_stream(dctx
, &ncbs
, &live_ctx
)
1220 flog_err_confd("confd_register_notification_stream");
1224 /* Register the action handler callback. */
1225 memset(&acbs
, 0, sizeof(acbs
));
1226 strlcpy(acbs
.actionpoint
, "actionpoint", sizeof(acbs
.actionpoint
));
1227 acbs
.init
= frr_confd_action_init
;
1228 acbs
.action
= frr_confd_action_execute
;
1229 if (confd_register_action_cbs(dctx
, &acbs
) != CONFD_OK
) {
1230 flog_err_confd("confd_register_action_cbs");
1234 /* Notify we registered all callbacks we wanted. */
1235 if (confd_register_done(dctx
) != CONFD_OK
) {
1236 flog_err_confd("confd_register_done");
1240 thread_add_read(master
, frr_confd_dp_read
, dctx
, dp_ctl_sock
,
1242 thread_add_read(master
, frr_confd_dp_read
, dctx
, dp_worker_sock
,
1248 frr_confd_finish_dp();
1253 static void frr_confd_finish_dp(void)
1255 if (dp_worker_sock
> 0) {
1256 THREAD_OFF(t_dp_worker
);
1257 close(dp_worker_sock
);
1259 if (dp_ctl_sock
> 0) {
1260 THREAD_OFF(t_dp_ctl
);
1264 confd_release_daemon(dctx
);
1267 /* ------------ Main ------------ */
1269 static int frr_confd_calculate_snode_hash(const struct lys_node
*snode
,
1272 struct nb_node
*nb_node
= snode
->priv
;
1274 nb_node
->confd_hash
= confd_str2hash(snode
->name
);
1276 return YANG_ITER_CONTINUE
;
1279 static int frr_confd_init(const char *program_name
)
1281 struct sockaddr_in
*confd_addr4
= (struct sockaddr_in
*)&confd_addr
;
1282 int debuglevel
= CONFD_SILENT
;
1285 /* Initialize ConfD library. */
1286 confd_init(program_name
, stderr
, debuglevel
);
1288 confd_addr4
->sin_family
= AF_INET
;
1289 confd_addr4
->sin_addr
.s_addr
= inet_addr("127.0.0.1");
1290 confd_addr4
->sin_port
= htons(CONFD_PORT
);
1291 if (confd_load_schemas(&confd_addr
, sizeof(struct sockaddr_in
))
1293 flog_err_confd("confd_load_schemas");
1297 ret
= frr_confd_init_cdb();
1301 ret
= frr_confd_init_dp(program_name
);
1303 frr_confd_finish_cdb();
1307 yang_snodes_iterate_all(frr_confd_calculate_snode_hash
, 0, NULL
);
1309 hook_register(nb_notification_send
, frr_confd_notification_send
);
1311 confd_connected
= true;
1315 confd_free_schemas();
1320 static int frr_confd_finish(void)
1322 if (confd_connected
== false)
1325 frr_confd_finish_cdb();
1326 frr_confd_finish_dp();
1328 confd_free_schemas();
1330 confd_connected
= false;
1335 static int frr_confd_module_late_init(struct thread_master
*tm
)
1339 if (frr_confd_init(frr_get_progname()) < 0) {
1340 flog_err(EC_LIB_CONFD_INIT
,
1341 "failed to initialize the ConfD module");
1345 hook_register(frr_fini
, frr_confd_finish
);
1350 static int frr_confd_module_init(void)
1352 hook_register(frr_late_init
, frr_confd_module_late_init
);
1357 FRR_MODULE_SETUP(.name
= "frr_confd", .version
= FRR_VERSION
,
1358 .description
= "FRR ConfD integration module",
1359 .init
= frr_confd_module_init
, )