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 keys from ConfD hashed keypath. */
95 static void frr_confd_hkeypath_get_keys(const confd_hkeypath_t
*kp
,
96 struct yang_list_keys
*keys
)
98 memset(keys
, 0, sizeof(*keys
));
99 for (int i
= 0; i
< kp
->len
; i
++) {
100 if (kp
->v
[i
][0].type
!= C_BUF
)
103 for (int j
= 0; kp
->v
[i
][j
].type
!= C_NOEXISTS
; j
++) {
104 strlcpy(keys
->key
[keys
->num
].value
,
105 (char *)kp
->v
[i
][j
].val
.buf
.ptr
,
106 sizeof(keys
->key
[keys
->num
].value
));
112 /* Fill the current date and time into a confd_datetime structure. */
113 static void getdatetime(struct confd_datetime
*datetime
)
118 gettimeofday(&tv
, NULL
);
119 gmtime_r(&tv
.tv_sec
, &tm
);
121 memset(datetime
, 0, sizeof(*datetime
));
122 datetime
->year
= 1900 + tm
.tm_year
;
123 datetime
->month
= tm
.tm_mon
+ 1;
124 datetime
->day
= tm
.tm_mday
;
125 datetime
->sec
= tm
.tm_sec
;
126 datetime
->micro
= tv
.tv_usec
;
127 datetime
->timezone
= 0;
128 datetime
->timezone_minutes
= 0;
129 datetime
->hour
= tm
.tm_hour
;
130 datetime
->min
= tm
.tm_min
;
133 /* ------------ CDB code ------------ */
135 struct cdb_iter_args
{
136 struct nb_config
*candidate
;
140 static enum cdb_iter_ret
141 frr_confd_cdb_diff_iter(confd_hkeypath_t
*kp
, enum cdb_iter_op cdb_op
,
142 confd_value_t
*oldv
, confd_value_t
*newv
, void *args
)
144 char xpath
[XPATH_MAXLEN
];
145 struct nb_node
*nb_node
;
146 enum nb_operation nb_op
;
147 struct cdb_iter_args
*iter_args
= args
;
148 char value_str
[YANG_VALUE_MAXLEN
];
149 struct yang_data
*data
;
153 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
156 * HACK: obtain value of leaf-list elements from the XPath due to
157 * a bug in the ConfD API.
160 sb1
= strrchr(xpath
, '[');
161 sb2
= strrchr(xpath
, ']');
162 if (sb1
&& sb2
&& !strchr(sb1
, '=')) {
164 strlcpy(value_str
, sb1
+ 1, sizeof(value_str
));
168 nb_node
= nb_node_find(xpath
);
170 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
171 "%s: unknown data path: %s", __func__
, xpath
);
172 iter_args
->error
= true;
176 /* Map operation values. */
179 nb_op
= NB_OP_CREATE
;
182 nb_op
= NB_OP_DELETE
;
185 if (nb_operation_is_valid(NB_OP_MODIFY
, nb_node
->snode
))
186 nb_op
= NB_OP_MODIFY
;
188 /* Ignore list keys modifications. */
191 case MOP_MOVED_AFTER
:
195 /* We're not interested on this. */
198 flog_err(EC_LIB_DEVELOPMENT
,
199 "%s: unexpected operation %u [xpath %s]", __func__
,
201 iter_args
->error
= true;
205 /* Convert ConfD value to a string. */
206 if (nb_node
->snode
->nodetype
!= LYS_LEAFLIST
&& newv
207 && frr_confd_val2str(nb_node
->xpath
, newv
, value_str
,
210 flog_err(EC_LIB_CONFD_DATA_CONVERT
,
211 "%s: failed to convert ConfD value to a string",
213 iter_args
->error
= true;
217 /* Edit the candidate configuration. */
218 data
= yang_data_new(xpath
, value_str
);
219 ret
= nb_candidate_edit(iter_args
->candidate
, nb_node
, nb_op
, xpath
,
221 yang_data_free(data
);
224 EC_LIB_NB_CANDIDATE_EDIT_ERROR
,
225 "%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
226 __func__
, nb_operation_name(nb_op
), xpath
);
227 iter_args
->error
= true;
234 static int frr_confd_cdb_read_cb(struct thread
*thread
)
236 int fd
= THREAD_FD(thread
);
238 enum cdb_sub_notification cdb_ev
;
241 struct nb_config
*candidate
;
242 struct cdb_iter_args iter_args
;
246 thread_add_read(master
, frr_confd_cdb_read_cb
, NULL
, fd
, &thread
);
248 if (cdb_read_subscription_socket2(fd
, &cdb_ev
, &flags
, &subp
, &reslen
)
250 flog_err_confd("cdb_read_subscription_socket2");
255 * Ignore CDB_SUB_ABORT and CDB_SUB_COMMIT. We'll leverage the
256 * northbound layer itself to abort or apply the configuration changes
257 * when a transaction is created.
259 if (cdb_ev
!= CDB_SUB_PREPARE
) {
261 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
)
263 flog_err_confd("cdb_sync_subscription_socket");
269 candidate
= nb_config_dup(running_config
);
271 /* Iterate over all configuration changes. */
272 iter_args
.candidate
= candidate
;
273 iter_args
.error
= false;
274 for (int i
= 0; i
< reslen
; i
++) {
275 if (cdb_diff_iterate(fd
, subp
[i
], frr_confd_cdb_diff_iter
,
276 ITER_WANT_PREV
, &iter_args
)
278 flog_err_confd("cdb_diff_iterate");
283 if (iter_args
.error
) {
284 nb_config_free(candidate
);
286 if (cdb_sub_abort_trans(
287 cdb_sub_sock
, CONFD_ERRCODE_APPLICATION_INTERNAL
, 0,
288 0, "Couldn't apply configuration changes")
290 flog_err_confd("cdb_sub_abort_trans");
296 ret
= nb_candidate_commit(candidate
, NB_CLIENT_CONFD
, true, NULL
, NULL
);
297 nb_config_free(candidate
);
298 if (ret
!= NB_OK
&& ret
!= NB_ERR_NO_CHANGES
) {
299 enum confd_errcode errcode
;
304 errcode
= CONFD_ERRCODE_IN_USE
;
305 errmsg
= "Configuration is locked by another process";
307 case NB_ERR_RESOURCE
:
308 errcode
= CONFD_ERRCODE_RESOURCE_DENIED
;
309 errmsg
= "Failed do allocate resources";
312 errcode
= CONFD_ERRCODE_INTERNAL
;
313 errmsg
= "Internal error";
317 if (cdb_sub_abort_trans(cdb_sub_sock
, errcode
, 0, 0, "%s",
320 flog_err_confd("cdb_sub_abort_trans");
324 if (cdb_sync_subscription_socket(fd
, CDB_DONE_PRIORITY
)
326 flog_err_confd("cdb_sync_subscription_socket");
334 /* Trigger CDB subscriptions to read the startup configuration. */
335 static void *thread_cdb_trigger_subscriptions(void *data
)
338 int *sub_points
= NULL
, len
= 0;
339 struct listnode
*node
;
343 /* Create CDB data socket. */
344 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
346 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
347 __func__
, safe_strerror(errno
));
351 if (cdb_connect(sock
, CDB_DATA_SOCKET
, &confd_addr
,
352 sizeof(struct sockaddr_in
))
354 flog_err_confd("cdb_connect");
359 * Fill array containing the subscription point of all loaded YANG
362 len
= listcount(confd_spoints
);
363 sub_points
= XCALLOC(MTYPE_CONFD
, len
* sizeof(int));
364 for (ALL_LIST_ELEMENTS_RO(confd_spoints
, node
, spoint
))
365 sub_points
[i
++] = *spoint
;
367 if (cdb_trigger_subscriptions(sock
, sub_points
, len
) != CONFD_OK
) {
368 flog_err_confd("cdb_trigger_subscriptions");
372 /* Cleanup and exit thread. */
373 XFREE(MTYPE_CONFD
, sub_points
);
379 static int frr_confd_init_cdb(void)
381 struct yang_module
*module
;
382 pthread_t cdb_trigger_thread
;
384 /* Create CDB subscription socket. */
385 cdb_sub_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
386 if (cdb_sub_sock
< 0) {
387 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
388 __func__
, safe_strerror(errno
));
392 if (cdb_connect(cdb_sub_sock
, CDB_SUBSCRIPTION_SOCKET
, &confd_addr
,
393 sizeof(struct sockaddr_in
))
395 flog_err_confd("cdb_connect");
399 /* Subscribe to all loaded YANG data modules. */
400 confd_spoints
= list_new();
401 RB_FOREACH (module
, yang_modules
, &yang_modules
) {
402 struct lys_node
*snode
;
404 module
->confd_hash
= confd_str2hash(module
->info
->ns
);
405 if (module
->confd_hash
== 0) {
408 "%s: failed to find hash value for namespace %s",
409 __func__
, module
->info
->ns
);
414 * The CDB API doesn't provide a mechanism to subscribe to an
415 * entire YANG module. So we have to find the top level
416 * nodes ourselves and subscribe to their paths.
418 LY_TREE_FOR (module
->info
->data
, snode
) {
419 struct nb_node
*nb_node
;
423 switch (snode
->nodetype
) {
433 nb_node
= snode
->priv
;
434 if (debug_northbound
)
435 zlog_debug("%s: subscribing to '%s'", __func__
,
438 spoint
= XMALLOC(MTYPE_CONFD
, sizeof(*spoint
));
439 ret
= cdb_subscribe2(
440 cdb_sub_sock
, CDB_SUB_RUNNING_TWOPHASE
,
441 CDB_SUB_WANT_ABORT_ON_ABORT
, 3, spoint
,
442 module
->confd_hash
, nb_node
->xpath
);
443 if (ret
!= CONFD_OK
) {
444 flog_err_confd("cdb_subscribe2");
445 XFREE(MTYPE_CONFD
, spoint
);
447 listnode_add(confd_spoints
, spoint
);
451 if (cdb_subscribe_done(cdb_sub_sock
) != CONFD_OK
) {
452 flog_err_confd("cdb_subscribe_done");
456 /* Create short lived pthread to trigger the CDB subscriptions. */
457 if (pthread_create(&cdb_trigger_thread
, NULL
,
458 thread_cdb_trigger_subscriptions
, NULL
)) {
459 flog_err(EC_LIB_SYSTEM_CALL
, "%s: error creating pthread: %s",
460 __func__
, safe_strerror(errno
));
463 pthread_detach(cdb_trigger_thread
);
465 thread_add_read(master
, frr_confd_cdb_read_cb
, NULL
, cdb_sub_sock
,
471 frr_confd_finish_cdb();
476 static void frr_confd_finish_cdb(void)
478 if (cdb_sub_sock
> 0) {
479 THREAD_OFF(t_cdb_sub
);
480 cdb_close(cdb_sub_sock
);
484 /* ------------ DP code ------------ */
486 static int frr_confd_transaction_init(struct confd_trans_ctx
*tctx
)
488 confd_trans_set_fd(tctx
, dp_worker_sock
);
493 static int frr_confd_data_get_elem(struct confd_trans_ctx
*tctx
,
494 confd_hkeypath_t
*kp
)
496 struct nb_node
*nb_node
, *parent_list
;
498 struct yang_list_keys keys
;
499 struct yang_data
*data
;
501 const void *list_entry
= NULL
;
503 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
505 nb_node
= nb_node_find(xpath
);
507 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
508 "%s: unknown data path: %s", __func__
, xpath
);
509 confd_data_reply_not_found(tctx
);
513 parent_list
= nb_node
->parent_list
;
515 frr_confd_hkeypath_get_keys(kp
, &keys
);
516 list_entry
= parent_list
->cbs
.lookup_entry(&keys
);
518 flog_warn(EC_LIB_NB_CB_STATE
,
519 "%s: list entry not found: %s", __func__
,
521 confd_data_reply_not_found(tctx
);
526 data
= nb_node
->cbs
.get_elem(xpath
, list_entry
);
529 CONFD_SET_STR(&v
, data
->value
);
530 confd_data_reply_value(tctx
, &v
);
532 confd_data_reply_found(tctx
);
533 yang_data_free(data
);
535 confd_data_reply_not_found(tctx
);
540 static int frr_confd_data_get_next(struct confd_trans_ctx
*tctx
,
541 confd_hkeypath_t
*kp
, long next
)
543 struct nb_node
*nb_node
;
545 struct yang_list_keys keys
;
547 confd_value_t v
[LIST_MAXKEYS
];
549 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
551 nb_node
= nb_node_find(xpath
);
553 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
554 "%s: unknown data path: %s", __func__
, xpath
);
555 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
559 nb_next
= nb_node
->cbs
.get_next(xpath
,
560 (next
== -1) ? NULL
: (void *)next
);
562 /* End of the list. */
563 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
566 if (nb_node
->cbs
.get_keys(nb_next
, &keys
) != NB_OK
) {
567 flog_warn(EC_LIB_NB_CB_STATE
, "%s: failed to get list keys",
569 confd_data_reply_next_key(tctx
, NULL
, -1, -1);
573 /* Feed keys to ConfD. */
574 for (size_t i
= 0; i
< keys
.num
; i
++)
575 CONFD_SET_STR(&v
[i
], keys
.key
[i
].value
);
576 confd_data_reply_next_key(tctx
, v
, keys
.num
, (long)nb_next
);
582 * Optional callback - implemented for performance reasons.
584 static int frr_confd_data_get_object(struct confd_trans_ctx
*tctx
,
585 confd_hkeypath_t
*kp
)
587 struct nb_node
*nb_node
;
589 char xpath_children
[XPATH_MAXLEN
];
590 char xpath_child
[XPATH_MAXLEN
];
591 struct yang_list_keys keys
;
592 struct list
*elements
;
593 struct yang_data
*data
;
594 const void *list_entry
;
596 confd_value_t
*values
;
598 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
600 nb_node
= nb_node_find(xpath
);
602 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
603 "%s: unknown data path: %s", __func__
, xpath
);
604 confd_data_reply_not_found(tctx
);
608 frr_confd_hkeypath_get_keys(kp
, &keys
);
609 list_entry
= nb_node
->cbs
.lookup_entry(&keys
);
611 flog_warn(EC_LIB_NB_CB_STATE
, "%s: list entry not found: %s",
613 confd_data_reply_not_found(tctx
);
617 /* Find list child nodes. */
618 snprintf(xpath_children
, sizeof(xpath_children
), "%s/*", xpath
);
619 set
= lys_find_path(nb_node
->snode
->module
, NULL
, xpath_children
);
621 flog_warn(EC_LIB_LIBYANG
, "%s: lys_find_path() failed",
626 elements
= yang_data_list_new();
627 values
= XMALLOC(MTYPE_CONFD
, set
->number
* sizeof(*values
));
629 /* Loop through list child nodes. */
630 for (size_t i
= 0; i
< set
->number
; i
++) {
631 struct lys_node
*child
;
632 struct nb_node
*nb_node_child
;
634 child
= set
->set
.s
[i
];
635 nb_node_child
= child
->priv
;
637 snprintf(xpath_child
, sizeof(xpath_child
), "%s/%s", xpath
,
640 data
= nb_node_child
->cbs
.get_elem(xpath_child
, list_entry
);
643 CONFD_SET_STR(&values
[i
], data
->value
);
645 CONFD_SET_NOEXISTS(&values
[i
]);
646 listnode_add(elements
, data
);
648 CONFD_SET_NOEXISTS(&values
[i
]);
651 confd_data_reply_value_array(tctx
, values
, set
->number
);
653 /* Release memory. */
655 XFREE(MTYPE_CONFD
, values
);
656 list_delete(&elements
);
662 * Optional callback - implemented for performance reasons.
664 static int frr_confd_data_get_next_object(struct confd_trans_ctx
*tctx
,
665 confd_hkeypath_t
*kp
, long next
)
668 char xpath_children
[XPATH_MAXLEN
];
669 struct nb_node
*nb_node
;
671 struct list
*elements
;
673 #define CONFD_OBJECTS_PER_TIME 100
674 struct confd_next_object objects
[CONFD_OBJECTS_PER_TIME
+ 1];
677 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
679 nb_node
= nb_node_find(xpath
);
681 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
682 "%s: unknown data path: %s", __func__
, xpath
);
683 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
687 /* Find list child nodes. */
688 snprintf(xpath_children
, sizeof(xpath_children
), "%s/*", xpath
);
689 set
= lys_find_path(nb_node
->snode
->module
, NULL
, xpath_children
);
691 flog_warn(EC_LIB_LIBYANG
, "%s: lys_find_path() failed",
696 elements
= yang_data_list_new();
697 nb_next
= (next
== -1) ? NULL
: (void *)next
;
699 memset(objects
, 0, sizeof(objects
));
700 for (int j
= 0; j
< CONFD_OBJECTS_PER_TIME
; j
++) {
701 struct confd_next_object
*object
;
702 struct yang_list_keys keys
;
703 struct yang_data
*data
;
704 const void *list_entry
;
706 object
= &objects
[j
];
708 nb_next
= nb_node
->cbs
.get_next(xpath
, nb_next
);
710 /* End of the list. */
713 if (nb_node
->cbs
.get_keys(nb_next
, &keys
) != NB_OK
) {
714 flog_warn(EC_LIB_NB_CB_STATE
,
715 "%s: failed to get list keys", __func__
);
718 object
->next
= (long)nb_next
;
720 list_entry
= nb_node
->cbs
.lookup_entry(&keys
);
722 flog_warn(EC_LIB_NB_CB_STATE
,
723 "%s: failed to lookup list entry", __func__
);
727 object
->v
= XMALLOC(MTYPE_CONFD
,
728 set
->number
* sizeof(confd_value_t
));
730 /* Loop through list child nodes. */
731 for (unsigned int i
= 0; i
< set
->number
; i
++) {
732 struct lys_node
*child
;
733 struct nb_node
*nb_node_child
;
734 char xpath_child
[XPATH_MAXLEN
];
735 confd_value_t
*v
= &object
->v
[i
];
737 child
= set
->set
.s
[i
];
738 nb_node_child
= child
->priv
;
740 snprintf(xpath_child
, sizeof(xpath_child
), "%s/%s",
743 data
= nb_node_child
->cbs
.get_elem(xpath_child
,
747 CONFD_SET_STR(v
, data
->value
);
749 CONFD_SET_NOEXISTS(v
);
750 listnode_add(elements
, data
);
752 CONFD_SET_NOEXISTS(v
);
754 object
->n
= set
->number
;
760 confd_data_reply_next_object_array(tctx
, NULL
, 0, 0);
761 list_delete(&elements
);
765 /* Detect end of the list. */
768 objects
[nobjects
].v
= NULL
;
771 /* Reply to ConfD. */
772 confd_data_reply_next_object_arrays(tctx
, objects
, nobjects
, 0);
776 /* Release memory. */
777 list_delete(&elements
);
778 for (int j
= 0; j
< nobjects
; j
++) {
779 struct confd_next_object
*object
;
781 object
= &objects
[j
];
782 XFREE(MTYPE_CONFD
, object
->v
);
788 static int frr_confd_notification_send(const char *xpath
,
789 struct list
*arguments
)
791 struct nb_node
*nb_node
;
792 struct yang_module
*module
;
793 struct confd_datetime now
;
794 confd_tag_value_t
*values
;
797 struct yang_data
*data
;
798 struct listnode
*node
;
801 nb_node
= nb_node_find(xpath
);
803 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
804 "%s: unknown data path: %s", __func__
, xpath
);
807 module
= yang_module_find(nb_node
->snode
->module
->name
);
812 nvalues
+= listcount(arguments
);
814 values
= XMALLOC(MTYPE_CONFD
, nvalues
* sizeof(*values
));
816 CONFD_SET_TAG_XMLBEGIN(&values
[i
++], nb_node
->confd_hash
,
818 for (ALL_LIST_ELEMENTS_RO(arguments
, node
, data
)) {
819 struct nb_node
*option_arg
;
821 option_arg
= data
->snode
->priv
;
822 CONFD_SET_TAG_STR(&values
[i
++], option_arg
->confd_hash
,
825 CONFD_SET_TAG_XMLEND(&values
[i
++], nb_node
->confd_hash
,
829 ret
= confd_notification_send(live_ctx
, &now
, values
, nvalues
);
831 /* Release memory. */
832 XFREE(MTYPE_CONFD
, values
);
834 /* Map ConfD return code to northbound return code. */
843 static int frr_confd_action_init(struct confd_user_info
*uinfo
)
845 confd_action_set_fd(uinfo
, dp_worker_sock
);
850 static int frr_confd_action_execute(struct confd_user_info
*uinfo
,
851 struct xml_tag
*name
, confd_hkeypath_t
*kp
,
852 confd_tag_value_t
*params
, int nparams
)
855 struct nb_node
*nb_node
;
858 struct yang_data
*data
;
859 confd_tag_value_t
*reply
;
862 /* Getting the XPath is tricky. */
864 /* This is a YANG RPC. */
865 frr_confd_get_xpath(kp
, xpath
, sizeof(xpath
));
866 strlcat(xpath
, "/", sizeof(xpath
));
867 strlcat(xpath
, confd_hash2str(name
->tag
), sizeof(xpath
));
869 /* This is a YANG action. */
870 snprintf(xpath
, sizeof(xpath
), "/%s:%s",
871 confd_ns2prefix(name
->ns
), confd_hash2str(name
->tag
));
874 nb_node
= nb_node_find(xpath
);
876 flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH
,
877 "%s: unknown data path: %s", __func__
, xpath
);
881 input
= yang_data_list_new();
882 output
= yang_data_list_new();
884 /* Process input nodes. */
885 for (int i
= 0; i
< nparams
; i
++) {
886 char xpath_input
[BUFSIZ
];
887 char value_str
[YANG_VALUE_MAXLEN
];
889 snprintf(xpath_input
, sizeof(xpath_input
), "%s/%s", xpath
,
890 confd_hash2str(params
[i
].tag
.tag
));
892 if (frr_confd_val2str(xpath_input
, ¶ms
[i
].v
, value_str
,
896 EC_LIB_CONFD_DATA_CONVERT
,
897 "%s: failed to convert ConfD value to a string",
903 data
= yang_data_new(xpath_input
, value_str
);
904 listnode_add(input
, data
);
907 /* Execute callback registered for this XPath. */
908 if (nb_node
->cbs
.rpc(xpath
, input
, output
) != NB_OK
) {
909 flog_warn(EC_LIB_NB_CB_RPC
, "%s: rpc callback failed: %s",
915 /* Process output nodes. */
916 if (listcount(output
) > 0) {
917 struct listnode
*node
;
920 reply
= XMALLOC(MTYPE_CONFD
,
921 listcount(output
) * sizeof(*reply
));
923 for (ALL_LIST_ELEMENTS_RO(output
, node
, data
)) {
926 hash
= confd_str2hash(data
->snode
->name
);
927 CONFD_SET_TAG_STR(&reply
[i
++], hash
, data
->value
);
929 confd_action_reply_values(uinfo
, reply
, listcount(output
));
930 XFREE(MTYPE_CONFD
, reply
);
934 /* Release memory. */
936 list_delete(&output
);
942 static int frr_confd_dp_read(struct thread
*thread
)
944 struct confd_daemon_ctx
*dctx
= THREAD_ARG(thread
);
945 int fd
= THREAD_FD(thread
);
949 thread_add_read(master
, frr_confd_dp_read
, dctx
, fd
, &thread
);
951 ret
= confd_fd_ready(dctx
, fd
);
952 if (ret
== CONFD_EOF
) {
953 flog_err_confd("confd_fd_ready");
955 } else if (ret
== CONFD_ERR
&& confd_errno
!= CONFD_ERR_EXTERNAL
) {
956 flog_err_confd("confd_fd_ready");
963 static void frr_confd_subscribe_state(const struct lys_node
*snode
, void *arg1
,
966 struct nb_node
*nb_node
= snode
->priv
;
967 struct confd_data_cbs
*data_cbs
= arg1
;
969 if (!(snode
->flags
& LYS_CONFIG_R
))
971 /* We only need to subscribe to the root of the state subtrees. */
972 if (snode
->parent
&& (snode
->parent
->flags
& LYS_CONFIG_R
))
975 if (debug_northbound
)
976 zlog_debug("%s: providing data to '%s' (callpoint %s)",
977 __func__
, nb_node
->xpath
, snode
->name
);
979 strlcpy(data_cbs
->callpoint
, snode
->name
, sizeof(data_cbs
->callpoint
));
980 if (confd_register_data_cb(dctx
, data_cbs
) != CONFD_OK
)
981 flog_err_confd("confd_register_data_cb");
984 static int frr_confd_init_dp(const char *program_name
)
986 struct confd_trans_cbs trans_cbs
;
987 struct confd_data_cbs data_cbs
;
988 struct confd_notification_stream_cbs ncbs
;
989 struct confd_action_cbs acbs
;
991 /* Initialize daemon context. */
992 dctx
= confd_init_daemon(program_name
);
994 flog_err_confd("confd_init_daemon");
999 * Inform we want to receive YANG values as raw strings, and that we
1000 * want to provide only strings in the reply functions, regardless of
1003 confd_set_daemon_flags(dctx
, CONFD_DAEMON_FLAG_STRINGSONLY
);
1005 /* Establish a control socket. */
1006 dp_ctl_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
1007 if (dp_ctl_sock
< 0) {
1008 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
1009 __func__
, safe_strerror(errno
));
1013 if (confd_connect(dctx
, dp_ctl_sock
, CONTROL_SOCKET
, &confd_addr
,
1014 sizeof(struct sockaddr_in
))
1016 flog_err_confd("confd_connect");
1021 * Establish a worker socket (only one since this plugin runs on a
1024 dp_worker_sock
= socket(PF_INET
, SOCK_STREAM
, 0);
1025 if (dp_worker_sock
< 0) {
1026 flog_err(EC_LIB_SOCKET
, "%s: failed to create socket: %s",
1027 __func__
, safe_strerror(errno
));
1030 if (confd_connect(dctx
, dp_worker_sock
, WORKER_SOCKET
, &confd_addr
,
1031 sizeof(struct sockaddr_in
))
1033 flog_err_confd("confd_connect");
1037 /* Register transaction callback functions. */
1038 memset(&trans_cbs
, 0, sizeof(trans_cbs
));
1039 trans_cbs
.init
= frr_confd_transaction_init
;
1040 confd_register_trans_cb(dctx
, &trans_cbs
);
1042 /* Register our read/write callbacks. */
1043 memset(&data_cbs
, 0, sizeof(data_cbs
));
1044 data_cbs
.get_elem
= frr_confd_data_get_elem
;
1045 data_cbs
.exists_optional
= frr_confd_data_get_elem
;
1046 data_cbs
.get_next
= frr_confd_data_get_next
;
1047 data_cbs
.get_object
= frr_confd_data_get_object
;
1048 data_cbs
.get_next_object
= frr_confd_data_get_next_object
;
1051 * Iterate over all loaded YANG modules and subscribe to the paths
1052 * referent to state data.
1054 yang_all_snodes_iterate(frr_confd_subscribe_state
, 0, &data_cbs
, NULL
);
1056 /* Register notification stream. */
1057 memset(&ncbs
, 0, sizeof(ncbs
));
1058 ncbs
.fd
= dp_worker_sock
;
1060 * RFC 5277 - Section 3.2.3:
1061 * A NETCONF server implementation supporting the notification
1062 * capability MUST support the "NETCONF" notification event
1063 * stream. This stream contains all NETCONF XML event notifications
1064 * supported by the NETCONF server.
1066 strlcpy(ncbs
.streamname
, "NETCONF", sizeof(ncbs
.streamname
));
1067 if (confd_register_notification_stream(dctx
, &ncbs
, &live_ctx
)
1069 flog_err_confd("confd_register_notification_stream");
1073 /* Register the action handler callback. */
1074 memset(&acbs
, 0, sizeof(acbs
));
1075 strlcpy(acbs
.actionpoint
, "actionpoint", sizeof(acbs
.actionpoint
));
1076 acbs
.init
= frr_confd_action_init
;
1077 acbs
.action
= frr_confd_action_execute
;
1078 if (confd_register_action_cbs(dctx
, &acbs
) != CONFD_OK
) {
1079 flog_err_confd("confd_register_action_cbs");
1083 /* Notify we registered all callbacks we wanted. */
1084 if (confd_register_done(dctx
) != CONFD_OK
) {
1085 flog_err_confd("confd_register_done");
1089 thread_add_read(master
, frr_confd_dp_read
, dctx
, dp_ctl_sock
,
1091 thread_add_read(master
, frr_confd_dp_read
, dctx
, dp_worker_sock
,
1097 frr_confd_finish_dp();
1102 static void frr_confd_finish_dp(void)
1104 if (dp_worker_sock
> 0) {
1105 THREAD_OFF(t_dp_worker
);
1106 close(dp_worker_sock
);
1108 if (dp_ctl_sock
> 0) {
1109 THREAD_OFF(t_dp_ctl
);
1113 confd_release_daemon(dctx
);
1116 /* ------------ Main ------------ */
1118 static void frr_confd_calculate_snode_hash(const struct lys_node
*snode
,
1119 void *arg1
, void *arg2
)
1121 struct nb_node
*nb_node
= snode
->priv
;
1123 nb_node
->confd_hash
= confd_str2hash(snode
->name
);
1126 static int frr_confd_init(const char *program_name
)
1128 struct sockaddr_in
*confd_addr4
= (struct sockaddr_in
*)&confd_addr
;
1129 int debuglevel
= CONFD_SILENT
;
1132 /* Initialize ConfD library. */
1133 confd_init(program_name
, stderr
, debuglevel
);
1135 confd_addr4
->sin_family
= AF_INET
;
1136 confd_addr4
->sin_addr
.s_addr
= inet_addr("127.0.0.1");
1137 confd_addr4
->sin_port
= htons(CONFD_PORT
);
1138 if (confd_load_schemas(&confd_addr
, sizeof(struct sockaddr_in
))
1140 flog_err_confd("confd_load_schemas");
1144 ret
= frr_confd_init_cdb();
1148 ret
= frr_confd_init_dp(program_name
);
1150 frr_confd_finish_cdb();
1154 yang_all_snodes_iterate(frr_confd_calculate_snode_hash
, 0, NULL
, NULL
);
1156 hook_register(nb_notification_send
, frr_confd_notification_send
);
1158 confd_connected
= true;
1162 confd_free_schemas();
1167 static int frr_confd_finish(void)
1169 if (confd_connected
== false)
1172 frr_confd_finish_cdb();
1173 frr_confd_finish_dp();
1175 confd_free_schemas();
1177 confd_connected
= false;
1182 static int frr_confd_module_late_init(struct thread_master
*tm
)
1186 if (frr_confd_init(frr_get_progname()) < 0) {
1187 flog_err(EC_LIB_CONFD_INIT
,
1188 "failed to initialize the ConfD module");
1192 hook_register(frr_fini
, frr_confd_finish
);
1197 static int frr_confd_module_init(void)
1199 hook_register(frr_late_init
, frr_confd_module_late_init
);
1204 FRR_MODULE_SETUP(.name
= "frr_confd", .version
= FRR_VERSION
,
1205 .description
= "FRR ConfD integration module",
1206 .init
= frr_confd_module_init
, )