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
;
45 static void frr_confd_finish_cdb(void);
46 static void frr_confd_finish_dp(void);
47 static int frr_confd_finish(void);
49 #define flog_err_confd(funcname) \
50 flog_err(EC_LIB_LIBCONFD, "%s: %s() failed: %s (%d): %s", __func__, \
51 (funcname), confd_strerror(confd_errno), confd_errno, \
55 /* ------------ Utils ------------ */
57 /* Get XPath string from ConfD hashed keypath. */
58 static void frr_confd_get_xpath(const confd_hkeypath_t
*kp
, char *xpath
,
63 confd_xpath_pp_kpath(xpath
, len
, 0, kp
);
66 * Replace double quotes by single quotes (the format accepted by the
70 while ((p
= strchr(p
, '"')) != NULL
)
74 /* Convert ConfD binary value to a string. */
75 static int frr_confd_val2str(const char *xpath
, const confd_value_t
*value
,
76 char *string
, size_t string_size
)
78 struct confd_cs_node
*csp
;
80 csp
= confd_cs_node_cd(NULL
, xpath
);
82 flog_err_confd("confd_cs_node_cd");
85 if (confd_val2str(csp
->info
.type
, value
, string
, string_size
)
87 flog_err_confd("confd_val2str");
94 /* Obtain list entry from ConfD hashed keypath. */
95 static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t
*kp
,
96 struct nb_node
*nb_node
,
97 const void **list_entry
)
99 struct nb_node
*nb_node_list
;
100 int parent_lists
= 0;
106 * Count the number of YANG lists in the path, disconsidering the
109 nb_node_list
= nb_node
;
110 while (nb_node_list
->parent_list
) {
111 nb_node_list
= nb_node_list
->parent_list
;
114 if (nb_node
->snode
->nodetype
!= LYS_LIST
&& parent_lists
== 0)
117 /* Start from the beginning and move down the tree. */
118 for (int i
= kp
->len
; i
>= 0; i
--) {
119 struct yang_list_keys keys
;
121 /* Not a YANG list. */
122 if (kp
->v
[i
][0].type
!= C_BUF
)
125 /* Obtain list keys. */
126 memset(&keys
, 0, sizeof(keys
));
127 for (int j
= 0; kp
->v
[i
][j
].type
!= C_NOEXISTS
; j
++) {
128 strlcpy(keys
.key
[keys
.num
],
129 (char *)kp
->v
[i
][j
].val
.buf
.ptr
,
130 sizeof(keys
.key
[keys
.num
]));
134 /* Obtain northbound node associated to the YANG list. */
135 nb_node_list
= nb_node
;
136 for (int j
= curr_list
; j
< parent_lists
; j
++)
137 nb_node_list
= nb_node_list
->parent_list
;
139 /* Obtain list entry. */
141 nb_node_list
->cbs
.lookup_entry(*list_entry
, &keys
);
142 if (*list_entry
== NULL
)
151 /* Fill the current date and time into a confd_datetime structure. */
152 static void getdatetime(struct confd_datetime
*datetime
)
157 gettimeofday(&tv
, NULL
);
158 gmtime_r(&tv
.tv_sec
, &tm
);
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
;
172 /* ------------ CDB code ------------ */
174 struct cdb_iter_args
{
175 struct nb_config
*candidate
;
179 static enum cdb_iter_ret
180 frr_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
)
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
;
192 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
195 * HACK: obtain value of leaf-list elements from the XPath due to
196 * a bug in the ConfD API.
199 sb1
= strrchr(xpath
, '[');
200 sb2
= strrchr(xpath
, ']');
201 if (sb1
&& sb2
&& !strchr(sb1
, '=')) {
203 strlcpy(value_str
, sb1
+ 1, sizeof(value_str
));
207 nb_node
= nb_node_find(xpath
);
209 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
210 "%s: unknown data path: %s", __func__
, xpath
);
211 iter_args
->error
= true;
215 /* Map operation values. */
218 nb_op
= NB_OP_CREATE
;
221 nb_op
= NB_OP_DELETE
;
224 if (nb_operation_is_valid(NB_OP_MODIFY
, nb_node
->snode
))
225 nb_op
= NB_OP_MODIFY
;
227 /* Ignore list keys modifications. */
230 case MOP_MOVED_AFTER
:
234 /* We're not interested on this. */
237 flog_err(EC_LIB_DEVELOPMENT
,
238 "%s: unexpected operation %u [xpath %s]", __func__
,
240 iter_args
->error
= true;
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
,
249 flog_err(EC_LIB_CONFD_DATA_CONVERT
,
250 "%s: failed to convert ConfD value to a string",
252 iter_args
->error
= true;
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
,
260 yang_data_free(data
);
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;
273 static int frr_confd_cdb_read_cb(struct thread
*thread
)
275 int fd
= THREAD_FD(thread
);
277 enum cdb_sub_notification cdb_ev
;
280 struct nb_config
*candidate
;
281 struct cdb_iter_args iter_args
;
285 thread_add_read(master
, frr_confd_cdb_read_cb
, NULL
, fd
, &thread
);
287 if (cdb_read_subscription_socket2(fd
, &cdb_ev
, &flags
, &subp
, &reslen
)
289 flog_err_confd("cdb_read_subscription_socket2");
294 * Ignore CDB_SUB_ABORT and CDB_SUB_COMMIT. We'll leverage the
295 * northbound layer itself to abort or apply the configuration changes
296 * when a transaction is created.
298 if (cdb_ev
!= CDB_SUB_PREPARE
) {
300 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
)
302 flog_err_confd("cdb_sync_subscription_socket");
308 candidate
= nb_config_dup(running_config
);
310 /* Iterate over all configuration changes. */
311 iter_args
.candidate
= candidate
;
312 iter_args
.error
= false;
313 for (int i
= 0; i
< reslen
; i
++) {
314 if (cdb_diff_iterate(fd
, subp
[i
], frr_confd_cdb_diff_iter
,
315 ITER_WANT_PREV
, &iter_args
)
317 flog_err_confd("cdb_diff_iterate");
322 if (iter_args
.error
) {
323 nb_config_free(candidate
);
325 if (cdb_sub_abort_trans(
326 cdb_sub_sock
, CONFD_ERRCODE_APPLICATION_INTERNAL
, 0,
327 0, "Couldn't apply configuration changes")
329 flog_err_confd("cdb_sub_abort_trans");
335 ret
= nb_candidate_commit(candidate
, NB_CLIENT_CONFD
, true, NULL
, NULL
);
336 nb_config_free(candidate
);
337 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
) {
338 enum confd_errcode errcode
;
343 errcode
= CONFD_ERRCODE_IN_USE
;
344 errmsg
= "Configuration is locked by another process";
346 case NB_ERR_RESOURCE
:
347 errcode
= CONFD_ERRCODE_RESOURCE_DENIED
;
348 errmsg
= "Failed do allocate resources";
351 errcode
= CONFD_ERRCODE_INTERNAL
;
352 errmsg
= "Internal error";
356 if (cdb_sub_abort_trans(cdb_sub_sock
, errcode
, 0, 0, "%s",
359 flog_err_confd("cdb_sub_abort_trans");
363 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
)
365 flog_err_confd("cdb_sync_subscription_socket");
373 /* Trigger CDB subscriptions to read the startup configuration. */
374 static void *thread_cdb_trigger_subscriptions(void *data
)
377 int *sub_points
= NULL
, len
= 0;
378 struct listnode
*node
;
382 /* Create CDB data socket. */
383 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
385 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
386 __func__
, safe_strerror(errno
));
390 if (cdb_connect(sock
, CDB_DATA_SOCKET
, &confd_addr
,
391 sizeof(struct sockaddr_in
))
393 flog_err_confd("cdb_connect");
398 * Fill array containing the subscription point of all loaded YANG
401 len
= listcount(confd_spoints
);
402 sub_points
= XCALLOC(MTYPE_CONFD
, len
* sizeof(int));
403 for (ALL_LIST_ELEMENTS_RO(confd_spoints
, node
, spoint
))
404 sub_points
[i
++] = *spoint
;
406 if (cdb_trigger_subscriptions(sock
, sub_points
, len
) != CONFD_OK
) {
407 flog_err_confd("cdb_trigger_subscriptions");
411 /* Cleanup and exit thread. */
412 XFREE(MTYPE_CONFD
, sub_points
);
418 static int frr_confd_init_cdb(void)
420 struct yang_module
*module
;
421 pthread_t cdb_trigger_thread
;
423 /* Create CDB subscription socket. */
424 cdb_sub_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
425 if (cdb_sub_sock
< 0) {
426 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
427 __func__
, safe_strerror(errno
));
431 if (cdb_connect(cdb_sub_sock
, CDB_SUBSCRIPTION_SOCKET
, &confd_addr
,
432 sizeof(struct sockaddr_in
))
434 flog_err_confd("cdb_connect");
438 /* Subscribe to all loaded YANG data modules. */
439 confd_spoints
= list_new();
440 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
441 struct lys_node
*snode
;
443 module
->confd_hash
= confd_str2hash(module
->info
->ns
);
444 if (module
->confd_hash
== 0) {
447 "%s: failed to find hash value for namespace %s",
448 __func__
, module
->info
->ns
);
453 * The CDB API doesn't provide a mechanism to subscribe to an
454 * entire YANG module. So we have to find the top level
455 * nodes ourselves and subscribe to their paths.
457 LY_TREE_FOR (module
->info
->data
, snode
) {
458 struct nb_node
*nb_node
;
462 switch (snode
->nodetype
) {
472 if (CHECK_FLAG(snode
->flags
, LYS_CONFIG_R
))
475 nb_node
= snode
->priv
;
476 if (debug_northbound
)
477 zlog_debug("%s: subscribing to '%s'", __func__
,
480 spoint
= XMALLOC(MTYPE_CONFD
, sizeof(*spoint
));
481 ret
= cdb_subscribe2(
482 cdb_sub_sock
, CDB_SUB_RUNNING_TWOPHASE
,
483 CDB_SUB_WANT_ABORT_ON_ABORT
, 3, spoint
,
484 module
->confd_hash
, nb_node
->xpath
);
485 if (ret
!= CONFD_OK
) {
486 flog_err_confd("cdb_subscribe2");
487 XFREE(MTYPE_CONFD
, spoint
);
489 listnode_add(confd_spoints
, spoint
);
493 if (cdb_subscribe_done(cdb_sub_sock
) != CONFD_OK
) {
494 flog_err_confd("cdb_subscribe_done");
498 /* Create short lived pthread to trigger the CDB subscriptions. */
499 if (pthread_create(&cdb_trigger_thread
, NULL
,
500 thread_cdb_trigger_subscriptions
, NULL
)) {
501 flog_err(EC_LIB_SYSTEM_CALL
, "%s: error creating pthread: %s",
502 __func__
, safe_strerror(errno
));
505 pthread_detach(cdb_trigger_thread
);
507 thread_add_read(master
, frr_confd_cdb_read_cb
, NULL
, cdb_sub_sock
,
513 frr_confd_finish_cdb();
518 static void frr_confd_finish_cdb(void)
520 if (cdb_sub_sock
> 0) {
521 THREAD_OFF(t_cdb_sub
);
522 cdb_close(cdb_sub_sock
);
526 /* ------------ DP code ------------ */
528 static int frr_confd_transaction_init(struct confd_trans_ctx
*tctx
)
530 confd_trans_set_fd(tctx
, dp_worker_sock
);
535 #define CONFD_MAX_CHILD_NODES 32
537 static int frr_confd_data_get_elem(struct confd_trans_ctx
*tctx
,
538 confd_hkeypath_t
*kp
)
540 struct nb_node
*nb_node
;
542 struct yang_data
*data
;
544 const void *list_entry
= NULL
;
546 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
548 nb_node
= nb_node_find(xpath
);
550 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
551 "%s: unknown data path: %s", __func__
, xpath
);
552 confd_data_reply_not_found(tctx
);
556 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &list_entry
) != 0) {
557 confd_data_reply_not_found(tctx
);
561 data
= nb_node
->cbs
.get_elem(xpath
, list_entry
);
564 CONFD_SET_STR(&v
, data
->value
);
565 confd_data_reply_value(tctx
, &v
);
567 confd_data_reply_found(tctx
);
568 yang_data_free(data
);
570 confd_data_reply_not_found(tctx
);
575 static int frr_confd_data_get_next(struct confd_trans_ctx
*tctx
,
576 confd_hkeypath_t
*kp
, long next
)
578 struct nb_node
*nb_node
;
580 struct yang_list_keys keys
;
581 struct yang_data
*data
;
582 const void *parent_list_entry
, *nb_next
;
583 confd_value_t v
[LIST_MAXKEYS
];
585 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
587 nb_node
= nb_node_find(xpath
);
589 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
590 "%s: unknown data path: %s", __func__
, xpath
);
591 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
595 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &parent_list_entry
)
597 /* List entry doesn't exist anymore. */
598 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
602 nb_next
= nb_node
->cbs
.get_next(parent_list_entry
,
603 (next
== -1) ? NULL
: (void *)next
);
605 /* End of the list or leaf-list. */
606 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
610 switch (nb_node
->snode
->nodetype
) {
612 memset(&keys
, 0, sizeof(keys
));
613 if (nb_node
->cbs
.get_keys(nb_next
, &keys
) != NB_OK
) {
614 flog_warn(EC_LIB_NB_CB_STATE
,
615 "%s: failed to get list keys", __func__
);
616 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
620 /* Feed keys to ConfD. */
621 for (size_t i
= 0; i
< keys
.num
; i
++)
622 CONFD_SET_STR(&v
[i
], keys
.key
[i
]);
623 confd_data_reply_next_key(tctx
, v
, keys
.num
, (long)nb_next
);
626 data
= nb_node
->cbs
.get_elem(xpath
, nb_next
);
629 CONFD_SET_STR(&v
[0], data
->value
);
630 confd_data_reply_next_key(tctx
, v
, 1,
633 yang_data_free(data
);
635 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
645 * Optional callback - implemented for performance reasons.
647 static int frr_confd_data_get_object(struct confd_trans_ctx
*tctx
,
648 confd_hkeypath_t
*kp
)
650 struct nb_node
*nb_node
;
651 const struct lys_node
*child
;
653 char xpath_child
[XPATH_MAXLEN
];
654 struct list
*elements
;
655 struct yang_data
*data
;
656 const void *list_entry
;
657 confd_value_t values
[CONFD_MAX_CHILD_NODES
];
660 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
662 nb_node
= nb_node_find(xpath
);
664 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
665 "%s: unknown data path: %s", __func__
, xpath
);
666 confd_data_reply_not_found(tctx
);
670 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &list_entry
) != 0) {
671 confd_data_reply_not_found(tctx
);
675 elements
= yang_data_list_new();
677 /* Loop through list child nodes. */
678 LY_TREE_FOR (nb_node
->snode
->child
, child
) {
679 struct nb_node
*nb_node_child
= child
->priv
;
682 if (nvalues
> CONFD_MAX_CHILD_NODES
)
685 v
= &values
[nvalues
++];
687 /* Non-presence containers, lists and leaf-lists. */
688 if (!nb_node_child
->cbs
.get_elem
) {
689 CONFD_SET_NOEXISTS(v
);
693 snprintf(xpath_child
, sizeof(xpath_child
), "%s/%s", xpath
,
695 data
= nb_node_child
->cbs
.get_elem(xpath_child
, list_entry
);
698 CONFD_SET_STR(v
, data
->value
);
700 /* Presence containers and empty leafs. */
702 v
, nb_node_child
->confd_hash
,
703 confd_str2hash(nb_node_child
->snode
706 listnode_add(elements
, data
);
708 CONFD_SET_NOEXISTS(v
);
711 confd_data_reply_value_array(tctx
, values
, nvalues
);
713 /* Release memory. */
714 list_delete(&elements
);
720 * Optional callback - implemented for performance reasons.
722 static int frr_confd_data_get_next_object(struct confd_trans_ctx
*tctx
,
723 confd_hkeypath_t
*kp
, long next
)
726 struct nb_node
*nb_node
;
727 struct list
*elements
;
728 const void *parent_list_entry
;
730 #define CONFD_OBJECTS_PER_TIME 100
731 struct confd_next_object objects
[CONFD_OBJECTS_PER_TIME
+ 1];
734 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
736 nb_node
= nb_node_find(xpath
);
738 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
739 "%s: unknown data path: %s", __func__
, xpath
);
740 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
744 if (frr_confd_hkeypath_get_list_entry(kp
, nb_node
, &parent_list_entry
)
746 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
750 elements
= yang_data_list_new();
751 nb_next
= (next
== -1) ? NULL
: (void *)next
;
753 memset(objects
, 0, sizeof(objects
));
754 for (int j
= 0; j
< CONFD_OBJECTS_PER_TIME
; j
++) {
755 struct confd_next_object
*object
;
756 struct lys_node
*child
;
757 struct yang_data
*data
;
760 object
= &objects
[j
];
762 nb_next
= nb_node
->cbs
.get_next(parent_list_entry
, nb_next
);
764 /* End of the list. */
767 object
->next
= (long)nb_next
;
769 /* Leaf-lists require special handling. */
770 if (nb_node
->snode
->nodetype
== LYS_LEAFLIST
) {
771 object
->v
= XMALLOC(MTYPE_CONFD
, sizeof(confd_value_t
));
772 data
= nb_node
->cbs
.get_elem(xpath
, nb_next
);
773 assert(data
&& data
->value
);
774 CONFD_SET_STR(object
->v
, data
->value
);
776 listnode_add(elements
, data
);
782 CONFD_MAX_CHILD_NODES
* sizeof(confd_value_t
));
784 /* Loop through list child nodes. */
785 LY_TREE_FOR (nb_node
->snode
->child
, child
) {
786 struct nb_node
*nb_node_child
= child
->priv
;
787 char xpath_child
[XPATH_MAXLEN
];
790 if (nvalues
> CONFD_MAX_CHILD_NODES
)
793 v
= &object
->v
[nvalues
++];
795 /* Non-presence containers, lists and leaf-lists. */
796 if (!nb_node_child
->cbs
.get_elem
) {
797 CONFD_SET_NOEXISTS(v
);
801 snprintf(xpath_child
, sizeof(xpath_child
), "%s/%s",
803 data
= nb_node_child
->cbs
.get_elem(xpath_child
,
807 CONFD_SET_STR(v
, data
->value
);
810 * Presence containers and empty leafs.
813 v
, nb_node_child
->confd_hash
,
818 listnode_add(elements
, data
);
820 CONFD_SET_NOEXISTS(v
);
828 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
829 list_delete(&elements
);
833 /* Detect end of the list. */
836 objects
[nobjects
].v
= NULL
;
839 /* Reply to ConfD. */
840 confd_data_reply_next_object_arrays(tctx
, objects
, nobjects
, 0);
844 /* Release memory. */
845 list_delete(&elements
);
846 for (int j
= 0; j
< nobjects
; j
++) {
847 struct confd_next_object
*object
;
849 object
= &objects
[j
];
850 XFREE(MTYPE_CONFD
, object
->v
);
856 static int frr_confd_notification_send(const char *xpath
,
857 struct list
*arguments
)
859 struct nb_node
*nb_node
;
860 struct yang_module
*module
;
861 struct confd_datetime now
;
862 confd_tag_value_t
*values
;
865 struct yang_data
*data
;
866 struct listnode
*node
;
869 nb_node
= nb_node_find(xpath
);
871 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
872 "%s: unknown data path: %s", __func__
, xpath
);
875 module
= yang_module_find(nb_node
->snode
->module
->name
);
880 nvalues
+= listcount(arguments
);
882 values
= XMALLOC(MTYPE_CONFD
, nvalues
* sizeof(*values
));
884 CONFD_SET_TAG_XMLBEGIN(&values
[i
++], nb_node
->confd_hash
,
886 for (ALL_LIST_ELEMENTS_RO(arguments
, node
, data
)) {
887 struct nb_node
*nb_node_arg
;
889 nb_node_arg
= nb_node_find(data
->xpath
);
891 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
892 "%s: unknown data path: %s", __func__
,
894 XFREE(MTYPE_CONFD
, values
);
898 CONFD_SET_TAG_STR(&values
[i
++], nb_node_arg
->confd_hash
,
901 CONFD_SET_TAG_XMLEND(&values
[i
++], nb_node
->confd_hash
,
905 ret
= confd_notification_send(live_ctx
, &now
, values
, nvalues
);
907 /* Release memory. */
908 XFREE(MTYPE_CONFD
, values
);
910 /* Map ConfD return code to northbound return code. */
919 static int frr_confd_action_init(struct confd_user_info
*uinfo
)
921 confd_action_set_fd(uinfo
, dp_worker_sock
);
926 static int frr_confd_action_execute(struct confd_user_info
*uinfo
,
927 struct xml_tag
*name
, confd_hkeypath_t
*kp
,
928 confd_tag_value_t
*params
, int nparams
)
931 struct nb_node
*nb_node
;
934 struct yang_data
*data
;
935 confd_tag_value_t
*reply
;
938 /* Getting the XPath is tricky. */
940 /* This is a YANG RPC. */
941 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
942 strlcat(xpath
, "/", sizeof(xpath
));
943 strlcat(xpath
, confd_hash2str(name
->tag
), sizeof(xpath
));
945 /* This is a YANG action. */
946 snprintf(xpath
, sizeof(xpath
), "/%s:%s",
947 confd_ns2prefix(name
->ns
), confd_hash2str(name
->tag
));
950 nb_node
= nb_node_find(xpath
);
952 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
953 "%s: unknown data path: %s", __func__
, xpath
);
957 input
= yang_data_list_new();
958 output
= yang_data_list_new();
960 /* Process input nodes. */
961 for (int i
= 0; i
< nparams
; i
++) {
962 char xpath_input
[BUFSIZ
];
963 char value_str
[YANG_VALUE_MAXLEN
];
965 snprintf(xpath_input
, sizeof(xpath_input
), "%s/%s", xpath
,
966 confd_hash2str(params
[i
].tag
.tag
));
968 if (frr_confd_val2str(xpath_input
, ¶ms
[i
].v
, value_str
,
972 EC_LIB_CONFD_DATA_CONVERT
,
973 "%s: failed to convert ConfD value to a string",
979 data
= yang_data_new(xpath_input
, value_str
);
980 listnode_add(input
, data
);
983 /* Execute callback registered for this XPath. */
984 if (nb_node
->cbs
.rpc(xpath
, input
, output
) != NB_OK
) {
985 flog_warn(EC_LIB_NB_CB_RPC
, "%s: rpc callback failed: %s",
991 /* Process output nodes. */
992 if (listcount(output
) > 0) {
993 struct listnode
*node
;
996 reply
= XMALLOC(MTYPE_CONFD
,
997 listcount(output
) * sizeof(*reply
));
999 for (ALL_LIST_ELEMENTS_RO(output
, node
, data
)) {
1000 struct nb_node
*nb_node_output
;
1003 nb_node_output
= nb_node_find(data
->xpath
);
1004 if (!nb_node_output
) {
1005 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
1006 "%s: unknown data path: %s", __func__
,
1011 hash
= confd_str2hash(nb_node_output
->snode
->name
);
1012 CONFD_SET_TAG_STR(&reply
[i
++], hash
, data
->value
);
1014 confd_action_reply_values(uinfo
, reply
, listcount(output
));
1015 XFREE(MTYPE_CONFD
, reply
);
1019 /* Release memory. */
1020 list_delete(&input
);
1021 list_delete(&output
);
1027 static int frr_confd_dp_read(struct thread
*thread
)
1029 struct confd_daemon_ctx
*dctx
= THREAD_ARG(thread
);
1030 int fd
= THREAD_FD(thread
);
1034 thread_add_read(master
, frr_confd_dp_read
, dctx
, fd
, &thread
);
1036 ret
= confd_fd_ready(dctx
, fd
);
1037 if (ret
== CONFD_EOF
) {
1038 flog_err_confd("confd_fd_ready");
1041 } else if (ret
== CONFD_ERR
&& confd_errno
!= CONFD_ERR_EXTERNAL
) {
1042 flog_err_confd("confd_fd_ready");
1050 static int frr_confd_subscribe_state(const struct lys_node
*snode
, void *arg
)
1052 struct nb_node
*nb_node
= snode
->priv
;
1053 struct confd_data_cbs
*data_cbs
= arg
;
1055 if (!CHECK_FLAG(snode
->flags
, LYS_CONFIG_R
))
1056 return YANG_ITER_CONTINUE
;
1057 /* We only need to subscribe to the root of the state subtrees. */
1058 if (snode
->parent
&& CHECK_FLAG(snode
->parent
->flags
, LYS_CONFIG_R
))
1059 return YANG_ITER_CONTINUE
;
1061 if (debug_northbound
)
1062 zlog_debug("%s: providing data to '%s' (callpoint %s)",
1063 __func__
, nb_node
->xpath
, snode
->name
);
1065 strlcpy(data_cbs
->callpoint
, snode
->name
, sizeof(data_cbs
->callpoint
));
1066 if (confd_register_data_cb(dctx
, data_cbs
) != CONFD_OK
)
1067 flog_err_confd("confd_register_data_cb");
1069 return YANG_ITER_CONTINUE
;
1072 static int frr_confd_init_dp(const char *program_name
)
1074 struct confd_trans_cbs trans_cbs
;
1075 struct confd_data_cbs data_cbs
;
1076 struct confd_notification_stream_cbs ncbs
;
1077 struct confd_action_cbs acbs
;
1079 /* Initialize daemon context. */
1080 dctx
= confd_init_daemon(program_name
);
1082 flog_err_confd("confd_init_daemon");
1087 * Inform we want to receive YANG values as raw strings, and that we
1088 * want to provide only strings in the reply functions, regardless of
1091 confd_set_daemon_flags(dctx
, CONFD_DAEMON_FLAG_STRINGSONLY
);
1093 /* Establish a control socket. */
1094 dp_ctl_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
1095 if (dp_ctl_sock
< 0) {
1096 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
1097 __func__
, safe_strerror(errno
));
1101 if (confd_connect(dctx
, dp_ctl_sock
, CONTROL_SOCKET
, &confd_addr
,
1102 sizeof(struct sockaddr_in
))
1104 flog_err_confd("confd_connect");
1109 * Establish a worker socket (only one since this plugin runs on a
1112 dp_worker_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
1113 if (dp_worker_sock
< 0) {
1114 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
1115 __func__
, safe_strerror(errno
));
1118 if (confd_connect(dctx
, dp_worker_sock
, WORKER_SOCKET
, &confd_addr
,
1119 sizeof(struct sockaddr_in
))
1121 flog_err_confd("confd_connect");
1125 /* Register transaction callback functions. */
1126 memset(&trans_cbs
, 0, sizeof(trans_cbs
));
1127 trans_cbs
.init
= frr_confd_transaction_init
;
1128 confd_register_trans_cb(dctx
, &trans_cbs
);
1130 /* Register our read/write callbacks. */
1131 memset(&data_cbs
, 0, sizeof(data_cbs
));
1132 data_cbs
.get_elem
= frr_confd_data_get_elem
;
1133 data_cbs
.exists_optional
= frr_confd_data_get_elem
;
1134 data_cbs
.get_next
= frr_confd_data_get_next
;
1135 data_cbs
.get_object
= frr_confd_data_get_object
;
1136 data_cbs
.get_next_object
= frr_confd_data_get_next_object
;
1139 * Iterate over all loaded YANG modules and subscribe to the paths
1140 * referent to state data.
1142 yang_snodes_iterate_all(frr_confd_subscribe_state
, 0, &data_cbs
);
1144 /* Register notification stream. */
1145 memset(&ncbs
, 0, sizeof(ncbs
));
1146 ncbs
.fd
= dp_worker_sock
;
1148 * RFC 5277 - Section 3.2.3:
1149 * A NETCONF server implementation supporting the notification
1150 * capability MUST support the "NETCONF" notification event
1151 * stream. This stream contains all NETCONF XML event notifications
1152 * supported by the NETCONF server.
1154 strlcpy(ncbs
.streamname
, "NETCONF", sizeof(ncbs
.streamname
));
1155 if (confd_register_notification_stream(dctx
, &ncbs
, &live_ctx
)
1157 flog_err_confd("confd_register_notification_stream");
1161 /* Register the action handler callback. */
1162 memset(&acbs
, 0, sizeof(acbs
));
1163 strlcpy(acbs
.actionpoint
, "actionpoint", sizeof(acbs
.actionpoint
));
1164 acbs
.init
= frr_confd_action_init
;
1165 acbs
.action
= frr_confd_action_execute
;
1166 if (confd_register_action_cbs(dctx
, &acbs
) != CONFD_OK
) {
1167 flog_err_confd("confd_register_action_cbs");
1171 /* Notify we registered all callbacks we wanted. */
1172 if (confd_register_done(dctx
) != CONFD_OK
) {
1173 flog_err_confd("confd_register_done");
1177 thread_add_read(master
, frr_confd_dp_read
, dctx
, dp_ctl_sock
,
1179 thread_add_read(master
, frr_confd_dp_read
, dctx
, dp_worker_sock
,
1185 frr_confd_finish_dp();
1190 static void frr_confd_finish_dp(void)
1192 if (dp_worker_sock
> 0) {
1193 THREAD_OFF(t_dp_worker
);
1194 close(dp_worker_sock
);
1196 if (dp_ctl_sock
> 0) {
1197 THREAD_OFF(t_dp_ctl
);
1201 confd_release_daemon(dctx
);
1204 /* ------------ Main ------------ */
1206 static int frr_confd_calculate_snode_hash(const struct lys_node
*snode
,
1209 struct nb_node
*nb_node
= snode
->priv
;
1211 nb_node
->confd_hash
= confd_str2hash(snode
->name
);
1213 return YANG_ITER_CONTINUE
;
1216 static int frr_confd_init(const char *program_name
)
1218 struct sockaddr_in
*confd_addr4
= (struct sockaddr_in
*)&confd_addr
;
1219 int debuglevel
= CONFD_SILENT
;
1222 /* Initialize ConfD library. */
1223 confd_init(program_name
, stderr
, debuglevel
);
1225 confd_addr4
->sin_family
= AF_INET
;
1226 confd_addr4
->sin_addr
.s_addr
= inet_addr("127.0.0.1");
1227 confd_addr4
->sin_port
= htons(CONFD_PORT
);
1228 if (confd_load_schemas(&confd_addr
, sizeof(struct sockaddr_in
))
1230 flog_err_confd("confd_load_schemas");
1234 ret
= frr_confd_init_cdb();
1238 ret
= frr_confd_init_dp(program_name
);
1240 frr_confd_finish_cdb();
1244 yang_snodes_iterate_all(frr_confd_calculate_snode_hash
, 0, NULL
);
1246 hook_register(nb_notification_send
, frr_confd_notification_send
);
1248 confd_connected
= true;
1252 confd_free_schemas();
1257 static int frr_confd_finish(void)
1259 if (confd_connected
== false)
1262 frr_confd_finish_cdb();
1263 frr_confd_finish_dp();
1265 confd_free_schemas();
1267 confd_connected
= false;
1272 static int frr_confd_module_late_init(struct thread_master
*tm
)
1276 if (frr_confd_init(frr_get_progname()) < 0) {
1277 flog_err(EC_LIB_CONFD_INIT
,
1278 "failed to initialize the ConfD module");
1282 hook_register(frr_fini
, frr_confd_finish
);
1287 static int frr_confd_module_init(void)
1289 hook_register(frr_late_init
, frr_confd_module_late_init
);
1294 FRR_MODULE_SETUP(.name
= "frr_confd", .version
= FRR_VERSION
,
1295 .description
= "FRR ConfD integration module",
1296 .init
= frr_confd_module_init
, )