config UPDATE Call Home over TLS
Added support for Call Home over TLS. Call Home connection type and
reconnect strategy parameters are now configurable.
diff --git a/src/config.h.in b/src/config.h.in
index 7f36b81..6dc9fa1 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -80,11 +80,6 @@
*/
#define NC_TIMEOUT_STEP @TIMEOUT_STEP@
-/*
- * Time waited between Call Home endpoint session creation attempts (s).
- */
-#define NC_CH_ENDPT_BACKOFF_WAIT @CALL_HOME_BACKOFF_WAIT@
-
/* Portability feature-check macros. */
#cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
diff --git a/src/config_new.c b/src/config_new.c
index b36bfef..45a1e12 100644
--- a/src/config_new.c
+++ b/src/config_new.c
@@ -715,91 +715,6 @@
return nc_config_new_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']", ch_client_name);
}
-int
-nc_config_new_delete(struct lyd_node **tree, const char *path_fmt, ...)
-{
- int ret = 0;
- va_list ap;
- char *path = NULL;
- struct lyd_node *sub = NULL;
-
- va_start(ap, path_fmt);
-
- /* create the path from the format */
- ret = vasprintf(&path, path_fmt, ap);
- if (ret == -1) {
- ERRMEM;
- path = NULL;
- goto cleanup;
- }
-
- /* find the node we want to delete */
- ret = lyd_find_path(*tree, path, 0, &sub);
- if (ret) {
- goto cleanup;
- }
-
- lyd_free_tree(sub);
-
- /* set the node to top level container */
- ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
- if (ret) {
- goto cleanup;
- }
-
- /* add all default nodes */
- ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
- if (ret) {
- goto cleanup;
- }
-
-cleanup:
- free(path);
- va_end(ap);
- return ret;
-}
-
-int
-nc_config_new_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...)
-{
- int ret = 0;
- va_list ap;
- char *path = NULL;
-
- va_start(ap, path_fmt);
-
- /* create the path from the format */
- ret = vasprintf(&path, path_fmt, ap);
- if (ret == -1) {
- ERRMEM;
- path = NULL;
- goto cleanup;
- }
-
- /* create the nodes in the path */
- ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
- if (ret) {
- goto cleanup;
- }
-
- /* set the node to the top level node */
- ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
- if (ret) {
- goto cleanup;
- }
-
- /* add all default nodes */
- ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
- if (ret) {
- goto cleanup;
- }
-
-cleanup:
- free(path);
- va_end(ap);
- return ret;
-}
-
API int
nc_server_config_new_keystore_asym_key(const struct ly_ctx *ctx, const char *name, const char *privkey_path,
const char *pubkey_path, struct lyd_node **config)
@@ -904,3 +819,295 @@
}
#endif /* NC_ENABLED_SSH_TLS */
+
+int
+nc_config_new_delete(struct lyd_node **tree, const char *path_fmt, ...)
+{
+ int ret = 0;
+ va_list ap;
+ char *path = NULL;
+ struct lyd_node *sub = NULL;
+
+ va_start(ap, path_fmt);
+
+ /* create the path from the format */
+ ret = vasprintf(&path, path_fmt, ap);
+ if (ret == -1) {
+ ERRMEM;
+ path = NULL;
+ goto cleanup;
+ }
+
+ /* find the node we want to delete */
+ ret = lyd_find_path(*tree, path, 0, &sub);
+ if (ret) {
+ goto cleanup;
+ }
+
+ lyd_free_tree(sub);
+
+ /* set the node to top level container */
+ ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* add all default nodes */
+ ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ va_end(ap);
+ return ret;
+}
+
+int
+nc_config_new_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...)
+{
+ int ret = 0;
+ va_list ap;
+ char *path = NULL;
+
+ va_start(ap, path_fmt);
+
+ /* create the path from the format */
+ ret = vasprintf(&path, path_fmt, ap);
+ if (ret == -1) {
+ ERRMEM;
+ path = NULL;
+ goto cleanup;
+ }
+
+ /* create the nodes in the path */
+ ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* set the node to the top level node */
+ ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* add all default nodes */
+ ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ va_end(ap);
+ return ret;
+}
+
+int
+nc_config_new_create_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
+ const char *value, struct lyd_node **tree)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ /* create the path by appending child to the parent path */
+ ret = asprintf(&path, "%s/%s", parent_path, child_name);
+ if (ret == -1) {
+ ERRMEM;
+ path = NULL;
+ goto cleanup;
+ }
+
+ /* create the nodes in the path */
+ ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* set the node to the top level node */
+ ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
+ if (ret) {
+ goto cleanup;
+ }
+
+ /* add all default nodes */
+ ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+int
+nc_config_new_check_delete(struct lyd_node **tree, const char *path_fmt, ...)
+{
+ int ret = 0;
+ va_list ap;
+ char *path = NULL;
+ struct lyd_node *sub = NULL;
+
+ va_start(ap, path_fmt);
+
+ /* create the path from the format */
+ ret = vasprintf(&path, path_fmt, ap);
+ if (ret == -1) {
+ ERRMEM;
+ path = NULL;
+ goto cleanup;
+ }
+
+ /* find the node we want to delete */
+ ret = lyd_find_path(*tree, path, 0, &sub);
+ if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) {
+ ret = 0;
+ goto cleanup;
+ } else if (ret) {
+ ERR(NULL, "Unable to delete node in the path \"%s\".", path);
+ goto cleanup;
+ }
+
+ lyd_free_tree(sub);
+
+ /* set the node to top level container */
+ ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree);
+ if (ret) {
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ va_end(ap);
+ return ret;
+}
+
+API int
+nc_server_config_new_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config)
+{
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
+
+ /* delete periodic tree if exists */
+ if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) {
+ return 1;
+ }
+
+ return nc_config_new_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/persistent", ch_client_name);
+}
+
+API int
+nc_server_config_new_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
+ struct lyd_node **config)
+{
+ char buf[6] = {0};
+
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, period, 1);
+
+ /* delete persistent tree if exists */
+ if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
+ return 1;
+ }
+
+ sprintf(buf, "%u", period);
+ return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name);
+}
+
+API int
+nc_server_config_new_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
+ const char *anchor_time, struct lyd_node **config)
+{
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, 1);
+
+ /* delete persistent tree if exists */
+ if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
+ return 1;
+ }
+
+ return nc_config_new_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name);
+}
+
+API int
+nc_server_config_new_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
+ uint16_t idle_timeout, struct lyd_node **config)
+{
+ char buf[6] = {0};
+
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, 1);
+
+ /* delete persistent tree if exists */
+ if (nc_config_new_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) {
+ return 1;
+ }
+
+ sprintf(buf, "%u", idle_timeout);
+ return nc_config_new_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name);
+}
+
+API int
+nc_server_config_new_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
+ NC_CH_START_WITH start_with, uint8_t max_attempts, uint16_t max_wait, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+ char buf[6] = {0};
+ const char *start_with_val;
+
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1);
+
+ /* prepared the path */
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/reconnect-strategy", ch_client_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (start_with) {
+ /* get string value from enum */
+ if (start_with == NC_CH_FIRST_LISTED) {
+ start_with_val = "first-listed";
+ } else if (start_with == NC_CH_LAST_CONNECTED) {
+ start_with_val = "last-connected";
+ } else {
+ start_with_val = "random-selection";
+ }
+
+ ret = nc_config_new_create_append(ctx, path, "start-with", start_with_val, config);
+ if (ret) {
+ goto cleanup;
+ }
+ }
+
+ if (max_attempts) {
+ sprintf(buf, "%u", max_attempts);
+ ret = nc_config_new_create_append(ctx, path, "max-attempts", buf, config);
+ if (ret) {
+ goto cleanup;
+ }
+ memset(buf, 0, 6);
+ }
+
+ if (max_wait) {
+ sprintf(buf, "%u", max_wait);
+ ret = nc_config_new_create_append(ctx, path, "max-wait", buf, config);
+ if (ret) {
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
diff --git a/src/config_new.h b/src/config_new.h
index 3388f05..985ab34 100644
--- a/src/config_new.h
+++ b/src/config_new.h
@@ -87,7 +87,7 @@
/**
* @brief Creates YANG data nodes in a path and gives the final node a value.
*
- * @param[in] ctx libyang context
+ * @param[in] ctx libyang context.
* @param[in, out] tree The YANG data tree where the insertion will happen. On success
* the top level container is always returned.
* @param[in] value Value assigned to the final node in the path.
@@ -98,6 +98,20 @@
int nc_config_new_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...);
/**
+ * @brief Creates new YANG data nodes in a path and gives the final node a value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] parent_path Path to the parent node.
+ * @param[in] child_name Name of the parent's child node to be created.
+ * @param[in] value Value to give to the child node.
+ * @param[out] tree YANG data tree where the insertion will happen. On success
+ * the top level container is always returned.
+ * @return 0 on success, 1 otherwise.
+ */
+int nc_config_new_create_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name,
+ const char *value, struct lyd_node **tree);
+
+/**
* @brief Deletes a subtree from the YANG data.
*
* @param tree YANG data from which the subtree will be deleted.
diff --git a/src/config_new_tls.c b/src/config_new_tls.c
index 3d14ad0..f1616cb 100644
--- a/src/config_new_tls.c
+++ b/src/config_new_tls.c
@@ -31,8 +31,8 @@
#include "session.h"
#include "session_p.h"
-API int
-nc_server_config_new_tls_server_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *pubkey_path,
+static int
+_nc_server_config_new_tls_server_certificate(const struct ly_ctx *ctx, const char *tree_path, const char *pubkey_path,
const char *privkey_path, const char *certificate_path, struct lyd_node **config)
{
int ret = 0;
@@ -41,9 +41,6 @@
NC_PUBKEY_FORMAT pubkey_type;
const char *privkey_format, *pubkey_format;
- NC_CHECK_ARG_RET(NULL, ctx, endpt_name, privkey_path, certificate_path, 1);
- NC_CHECK_ARG_RET(NULL, config, 1);
-
/* get the keys as a string from the given files */
ret = nc_server_config_new_get_keys(privkey_path, pubkey_path, &privkey, &pubkey, &privkey_type, &pubkey_type);
if (ret) {
@@ -72,32 +69,27 @@
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, pubkey_format, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
- "tls/tls-server-parameters/server-identity/certificate/inline-definition/public-key-format", endpt_name);
+ ret = nc_config_new_create_append(ctx, tree_path, "public-key-format", pubkey_format, config);
if (ret) {
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, pubkey, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
- "tls/tls-server-parameters/server-identity/certificate/inline-definition/public-key", endpt_name);
+ ret = nc_config_new_create_append(ctx, tree_path, "public-key", pubkey, config);
if (ret) {
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, privkey_format, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
- "tls/tls-server-parameters/server-identity/certificate/inline-definition/private-key-format", endpt_name);
+ ret = nc_config_new_create_append(ctx, tree_path, "private-key-format", privkey_format, config);
if (ret) {
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, privkey, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
- "tls/tls-server-parameters/server-identity/certificate/inline-definition/cleartext-private-key", endpt_name);
+ ret = nc_config_new_create_append(ctx, tree_path, "cleartext-private-key", privkey, config);
if (ret) {
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, cert, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
- "tls/tls-server-parameters/server-identity/certificate/inline-definition/cert-data", endpt_name);
+ ret = nc_config_new_create_append(ctx, tree_path, "cert-data", cert, config);
if (ret) {
goto cleanup;
}
@@ -110,22 +102,79 @@
}
API int
-nc_server_config_new_tls_client_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
+nc_server_config_new_tls_server_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *pubkey_path,
+ const char *privkey_path, const char *certificate_path, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ctx, endpt_name, privkey_path, certificate_path, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/"
+ "tls/tls-server-parameters/server-identity/certificate/inline-definition", endpt_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_server_certificate(ctx, path, pubkey_path, privkey_path,
+ certificate_path, config);
+ if (ret) {
+ ERR(NULL, "Creating new TLS server certificate YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+API int
+nc_server_config_new_ch_tls_server_certificate(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ const char *pubkey_path, const char *privkey_path, const char *certificate_path, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, endpt_name, privkey_path, certificate_path, 1);
+ NC_CHECK_ARG_RET(NULL, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/"
+ "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/"
+ "certificate/inline-definition", ch_client_name, endpt_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_server_certificate(ctx, path, pubkey_path, privkey_path,
+ certificate_path, config);
+ if (ret) {
+ ERR(NULL, "Creating new CH TLS server certificate YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+static int
+_nc_server_config_new_tls_client_certificate(const struct ly_ctx *ctx, const char *tree_path,
const char *cert_path, struct lyd_node **config)
{
int ret = 0;
char *cert = NULL;
- NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
-
ret = nc_server_config_new_read_certificate(cert_path, &cert);
if (ret) {
ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path);
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, cert, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
- "client-authentication/ee-certs/inline-definition/certificate[name='%s']/cert-data", endpt_name, cert_name);
+ ret = nc_config_new_create_append(ctx, tree_path, "cert-data", cert, config);
if (ret) {
goto cleanup;
}
@@ -136,28 +185,118 @@
}
API int
-nc_server_config_new_tls_client_ca(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
+nc_server_config_new_tls_client_certificate(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
const char *cert_path, struct lyd_node **config)
{
int ret = 0;
- char *cert = NULL;
+ char *path = NULL;
NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
- ret = nc_server_config_new_read_certificate(cert_path, &cert);
- if (ret) {
- ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path);
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
+ "client-authentication/ee-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, cert, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
- "client-authentication/ca-certs/inline-definition/certificate[name='%s']/cert-data", endpt_name, cert_name);
+ ret = _nc_server_config_new_tls_client_certificate(ctx, path, cert_path, config);
if (ret) {
+ ERR(NULL, "Creating new TLS client certificate YANG data failed.");
goto cleanup;
}
cleanup:
- free(cert);
+ free(path);
+ return ret;
+}
+
+API int
+nc_server_config_new_ch_tls_client_certificate(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ const char *cert_name, const char *cert_path, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, endpt_name, cert_name, cert_path, 1);
+ NC_CHECK_ARG_RET(NULL, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
+ "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/"
+ "inline-definition/certificate[name='%s']", ch_client_name, endpt_name, cert_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_client_certificate(ctx, path, cert_path, config);
+ if (ret) {
+ ERR(NULL, "Creating new CH TLS client certificate YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+API int
+nc_server_config_new_tls_client_ca(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name,
+ const char *cert_path, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/"
+ "client-authentication/ca-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_client_certificate(ctx, path, cert_path, config);
+ if (ret) {
+ ERR(NULL, "Creating new TLS client certificate authority YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+API int
+nc_server_config_new_ch_tls_client_ca(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ const char *cert_name, const char *cert_path, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, endpt_name, cert_name, cert_path, 1);
+ NC_CHECK_ARG_RET(NULL, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
+ "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/"
+ "inline-definition/certificate[name='%s']", ch_client_name, endpt_name, cert_name) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_client_certificate(ctx, path, cert_path, config);
+ if (ret) {
+ ERR(NULL, "Creating new CH TLS client certificate authority YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
return ret;
}
@@ -184,20 +323,16 @@
}
}
-API int
-nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint,
+static int
+_nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *tree_path, const char *fingerprint,
NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
{
int ret = 0;
const char *map;
- NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, map_type, name, 1);
- NC_CHECK_ARG_RET(NULL, config, 1);
-
if (fingerprint) {
/* optional */
- ret = nc_config_new_create(ctx, config, fingerprint, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/"
- "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%d']/fingerprint", endpt_name, id);
+ ret = nc_config_new_create_append(ctx, tree_path, "fingerprint", fingerprint, config);
if (ret) {
goto cleanup;
}
@@ -210,14 +345,12 @@
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, map, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/"
- "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%d']/map-type", endpt_name, id);
+ ret = nc_config_new_create_append(ctx, tree_path, "map-type", map, config);
if (ret) {
goto cleanup;
}
- ret = nc_config_new_create(ctx, config, name, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/"
- "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%d']/name", endpt_name, id);
+ ret = nc_config_new_create_append(ctx, tree_path, "name", name, config);
if (ret) {
goto cleanup;
}
@@ -226,6 +359,63 @@
return ret;
}
+API int
+nc_server_config_new_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint,
+ NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, name, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/netconf-server-parameters/"
+ "client-identity-mappings/cert-to-name[id='%u']", endpt_name, id) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_ctn(ctx, path, fingerprint, map_type, name, config);
+ if (ret) {
+ ERR(NULL, "Creating new TLS cert-to-name YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
+API int
+nc_server_config_new_ch_tls_ctn(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config)
+{
+ int ret = 0;
+ char *path = NULL;
+
+ NC_CHECK_ARG_RET(NULL, ch_client_name, endpt_name, id, name, config, 1);
+
+ if (asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/"
+ "endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/"
+ "cert-to-name[id='%u']", ch_client_name, endpt_name, id) == -1) {
+ ERRMEM;
+ path = NULL;
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = _nc_server_config_new_tls_ctn(ctx, path, fingerprint, map_type, name, config);
+ if (ret) {
+ ERR(NULL, "Creating new TLS cert-to-name YANG data failed.");
+ goto cleanup;
+ }
+
+cleanup:
+ free(path);
+ return ret;
+}
+
static const char *
nc_config_new_tls_tlsversion2str(NC_TLS_VERSION version)
{
diff --git a/src/server_config.c b/src/server_config.c
index 1a46b21..a33fbcf 100644
--- a/src/server_config.c
+++ b/src/server_config.c
@@ -414,23 +414,46 @@
}
static int
+nc_server_config_get_tls_opts(const struct lyd_node *node, struct nc_server_tls_opts **opts)
+{
+ struct nc_endpt *endpt;
+ struct nc_ch_endpt *ch_endpt;
+
+ assert(node && opts);
+
+ if (is_listen(node)) {
+ if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ return 1;
+ }
+ *opts = endpt->opts.tls;
+ } else {
+ if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
+ return 1;
+ }
+ *opts = ch_endpt->opts.tls;
+ }
+
+ return 0;
+}
+
+static int
nc_server_config_get_cert(const struct lyd_node *node, int is_ee, struct nc_certificate **cert)
{
uint16_t i;
const char *cert_name;
- struct nc_endpt *endpt;
struct nc_cert_grouping *auth_client;
+ struct nc_server_tls_opts *opts;
assert(node && cert);
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
return 1;
}
if (is_ee) {
- auth_client = &endpt->opts.tls->ee_certs;
+ auth_client = &opts->ee_certs;
} else {
- auth_client = &endpt->opts.tls->ca_certs;
+ auth_client = &opts->ca_certs;
}
while (node) {
@@ -465,11 +488,11 @@
{
uint32_t id;
struct nc_ctn *iter;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
assert(node && ctn);
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
return 1;
}
@@ -490,7 +513,7 @@
assert(!strcmp(LYD_NAME(node), "id"));
id = strtoul(lyd_get_value(node), NULL, 10);
- iter = endpt->opts.tls->ctn;
+ iter = opts->ctn;
while (iter) {
if (iter->id == id) {
*ctn = iter;
@@ -1067,6 +1090,29 @@
}
static void
+nc_server_config_ch_del_tls(struct nc_server_tls_opts *opts)
+{
+ if (opts->store == NC_STORE_LOCAL) {
+ nc_server_config_tls_del_public_key(opts);
+ nc_server_config_tls_del_cleartext_private_key(opts);
+ nc_server_config_tls_del_cert_data(opts);
+ }
+
+ nc_server_config_tls_del_certs(&opts->ca_certs);
+ nc_server_config_tls_del_certs(&opts->ee_certs);
+
+ nc_server_config_del_path(opts);
+ nc_server_config_del_url(opts);
+ X509_STORE_free(opts->crl_store);
+ opts->crl_store = NULL;
+
+ nc_server_config_tls_del_ctns(opts);
+ nc_server_config_tls_del_ciphers(opts);
+
+ free(opts);
+}
+
+static void
nc_server_config_ch_del_endpt_address(struct nc_ch_endpt *ch_endpt)
{
free(ch_endpt->address);
@@ -1094,6 +1140,9 @@
case NC_TI_LIBSSH:
nc_server_config_ch_del_ssh(ch_endpt->opts.ssh);
break;
+ case NC_TI_OPENSSL:
+ nc_server_config_ch_del_tls(ch_endpt->opts.tls);
+ break;
#endif /* NC_ENABLED_SSH_TLS */
default:
ERRINT;
@@ -1121,13 +1170,14 @@
pthread_mutex_lock(&ch_client->session->opts.server.ch_lock);
pthread_cond_signal(&ch_client->session->opts.server.ch_cond);
pthread_mutex_unlock(&ch_client->session->opts.server.ch_lock);
+ ch_client->session = NULL;
}
- ch_client->session = NULL;
-
pthread_rwlock_unlock(&server_opts.ch_client_lock);
- pthread_join(ch_client->tid, NULL);
+ if (ch_client->tid) {
+ pthread_join(ch_client->tid, NULL);
+ }
pthread_rwlock_wrlock(&server_opts.ch_client_lock);
@@ -1164,15 +1214,28 @@
static int
nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op)
{
+ struct nc_ch_client *ch_client;
+
assert(!strcmp(LYD_NAME(node), "idle-timeout"));
- if (equal_parent_name(node, 1, "listen")) {
+ if (is_listen(node)) {
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
server_opts.idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
} else {
/* default value */
server_opts.idle_timeout = 3600;
}
+ } else {
+ /* call-home idle timeout */
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ return 1;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ ch_client->idle_timeout = strtoul(lyd_get_value(node), NULL, 10);
+ } else if (op == NC_OP_DELETE) {
+ ch_client->idle_timeout = 180;
+ }
}
return 0;
@@ -1351,6 +1414,8 @@
if (ret) {
goto cleanup;
}
+ } else if (op == NC_OP_DELETE) {
+ nc_server_config_ch_del_ssh(ch_endpt->opts.ssh);
}
}
@@ -1372,26 +1437,54 @@
}
static int
+nc_server_config_ch_create_tls(struct nc_ch_endpt *ch_endpt)
+{
+ ch_endpt->ti = NC_TI_OPENSSL;
+ ch_endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts));
+ if (!ch_endpt->opts.tls) {
+ ERRMEM;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op)
{
struct nc_endpt *endpt;
struct nc_bind *bind;
+ struct nc_ch_endpt *ch_endpt;
int ret = 0;
assert(!strcmp(LYD_NAME(node), "tls"));
- if (nc_server_config_get_endpt(node, &endpt, &bind)) {
- ret = 1;
- goto cleanup;
- }
-
- if (op == NC_OP_CREATE) {
- ret = nc_server_config_create_tls(endpt);
- if (ret) {
+ if (is_listen(node)) {
+ if (nc_server_config_get_endpt(node, &endpt, &bind)) {
+ ret = 1;
goto cleanup;
}
- } else if (op == NC_OP_DELETE) {
- nc_server_config_del_tls(bind, endpt->opts.tls);
+
+ if (op == NC_OP_CREATE) {
+ ret = nc_server_config_create_tls(endpt);
+ if (ret) {
+ goto cleanup;
+ }
+ } else if (op == NC_OP_DELETE) {
+ nc_server_config_del_tls(bind, endpt->opts.tls);
+ }
+ } else {
+ if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (op == NC_OP_CREATE) {
+ ret = nc_server_config_ch_create_tls(ch_endpt);
+ if (ret) {
+ goto cleanup;
+ }
+ }
}
cleanup:
@@ -1756,9 +1849,9 @@
int ret = 0;
const char *format;
NC_PUBKEY_FORMAT pubkey_type;
- struct nc_endpt *endpt;
struct nc_public_key *pubkey;
struct nc_hostkey *hostkey;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "public-key-format"));
@@ -1793,15 +1886,15 @@
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
pubkey->type = pubkey_type;
}
- } else if (is_listen(node) && is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
- /* TLS listen server-identity */
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
+ /* TLS server-identity */
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- endpt->opts.tls->pubkey_type = pubkey_type;
+ opts->pubkey_type = pubkey_type;
}
}
@@ -1866,10 +1959,10 @@
nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
struct nc_hostkey *hostkey;
struct nc_client_auth *auth_client;
struct nc_public_key *pubkey;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "public-key"));
@@ -1927,18 +2020,18 @@
} else {
nc_server_config_del_auth_client_pubkey_pub_base64(pubkey);
}
- } else if (is_listen(node) && is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
- /* tls listen server-identity */
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) {
+ /* TLS server-identity */
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
/* set to local */
- endpt->opts.tls->store = NC_STORE_LOCAL;
+ opts->store = NC_STORE_LOCAL;
- ret = nc_server_config_tls_replace_server_public_key(node, endpt->opts.tls);
+ ret = nc_server_config_tls_replace_server_public_key(node, opts);
if (ret) {
goto cleanup;
}
@@ -1956,8 +2049,8 @@
int ret = 0;
const char *format;
NC_PRIVKEY_FORMAT privkey_type;
- struct nc_endpt *endpt;
struct nc_hostkey *hostkey;
+ struct nc_server_tls_opts *opts;
(void) op;
@@ -1984,14 +2077,14 @@
}
hostkey->key.privkey_type = privkey_type;
- } else if (is_listen(node) && is_tls(node)) {
- /* listen tls */
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ } else if (is_tls(node)) {
+ /* tls */
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
- endpt->opts.tls->privkey_type = privkey_type;
+ opts->privkey_type = privkey_type;
}
cleanup:
@@ -2028,8 +2121,8 @@
nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
struct nc_hostkey *hostkey;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "cleartext-private-key"));
@@ -2048,20 +2141,20 @@
} else {
nc_server_config_del_private_key(hostkey);
}
- } else if (is_listen(node) && is_tls(node)) {
- /* listen tls */
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ } else if (is_tls(node)) {
+ /* tls */
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- ret = nc_server_config_tls_replace_cleartext_private_key(node, endpt->opts.tls);
+ ret = nc_server_config_tls_replace_cleartext_private_key(node, opts);
if (ret) {
goto cleanup;
}
} else {
- nc_server_config_tls_del_cleartext_private_key(endpt->opts.tls);
+ nc_server_config_tls_del_cleartext_private_key(opts);
}
}
@@ -2955,24 +3048,24 @@
nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
struct nc_certificate *cert;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "cert-data"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
- ret = 1;
- goto cleanup;
- }
+ if (equal_parent_name(node, 3, "server-identity")) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
+ ret = 1;
+ goto cleanup;
+ }
- if ((equal_parent_name(node, 3, "server-identity")) && (is_listen(node))) {
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- ret = nc_server_config_tls_replace_cert_data(node, endpt->opts.tls);
+ ret = nc_server_config_tls_replace_cert_data(node, opts);
if (ret) {
goto cleanup;
}
}
- } else if ((equal_parent_name(node, 3, "ca-certs")) && (is_listen(node))) {
+ } else if (equal_parent_name(node, 3, "ca-certs")) {
if (nc_server_config_get_cert(node, 0, &cert)) {
ret = 1;
goto cleanup;
@@ -2986,7 +3079,7 @@
} else {
nc_server_config_tls_del_cert_data_certificate(cert);
}
- } else if ((equal_parent_name(node, 3, "ee-certs")) && (is_listen(node))) {
+ } else if (equal_parent_name(node, 3, "ee-certs")) {
if (nc_server_config_get_cert(node, 1, &cert)) {
ret = 1;
goto cleanup;
@@ -3059,7 +3152,7 @@
}
static int
-nc_server_config_tls_create_certificate_ref(const struct lyd_node *node, struct nc_endpt *endpt, struct nc_asymmetric_key *key)
+nc_server_config_tls_create_certificate_ref(const struct lyd_node *node, struct nc_server_tls_opts *opts, struct nc_asymmetric_key *key)
{
uint16_t i;
@@ -3075,13 +3168,13 @@
return 1;
}
- endpt->opts.tls->cert_ref = &key->certs[i];
+ opts->cert_ref = &key->certs[i];
return 0;
}
static struct nc_asymmetric_key *
-cert_get_asymmetric_key(const struct lyd_node *node)
+nc_server_config_cert_get_asymmetric_key(const struct lyd_node *node)
{
uint16_t i;
struct nc_keystore *ks = &server_opts.keystore;
@@ -3131,62 +3224,61 @@
nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
struct nc_asymmetric_key *key;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "certificate"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
- if ((equal_parent_name(node, 1, "keystore-reference")) && (is_listen(node))) {
- /* server-identity TLS listen */
-
+ if (equal_parent_name(node, 1, "keystore-reference")) {
+ /* TLS server-identity */
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
/* set to keystore */
- endpt->opts.tls->store = NC_STORE_KEYSTORE;
+ opts->store = NC_STORE_KEYSTORE;
- if (!endpt->opts.tls->key_ref) {
+ if (!opts->key_ref) {
/* we don't have a key from which we need the cert yet */
- key = cert_get_asymmetric_key(node);
+ key = nc_server_config_cert_get_asymmetric_key(node);
if (!key) {
ret = 1;
goto cleanup;
}
} else {
/* we have the key */
- key = endpt->opts.tls->key_ref;
+ key = opts->key_ref;
}
/* find the given cert in the key and set it */
- ret = nc_server_config_tls_create_certificate_ref(node, endpt, key);
+ ret = nc_server_config_tls_create_certificate_ref(node, opts, key);
if (ret) {
goto cleanup;
}
} else {
- endpt->opts.tls->cert_ref = NULL;
+ opts->cert_ref = NULL;
}
- } else if ((equal_parent_name(node, 2, "ca-certs")) && (is_listen(node))) {
- /* client auth TLS listen */
+ } else if (equal_parent_name(node, 2, "ca-certs")) {
+ /* TLS client auth certificate authority */
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- ret = nc_server_config_create_ca_certs_certificate(node, endpt->opts.tls);
+ ret = nc_server_config_create_ca_certs_certificate(node, opts);
if (ret) {
goto cleanup;
}
} else {
- nc_server_config_tls_del_certs(&endpt->opts.tls->ca_certs);
+ nc_server_config_tls_del_certs(&opts->ca_certs);
}
- } else if ((equal_parent_name(node, 2, "ee-certs")) && (is_listen(node))) {
- /* client auth TLS listen */
+ } else if (equal_parent_name(node, 2, "ee-certs")) {
+ /* TLS client auth end entity */
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- ret = nc_server_config_create_ee_certs_certificate(node, endpt->opts.tls);
+ ret = nc_server_config_create_ee_certs_certificate(node, opts);
if (ret) {
goto cleanup;
}
} else {
- nc_server_config_tls_del_certs(&endpt->opts.tls->ee_certs);
+ nc_server_config_tls_del_certs(&opts->ee_certs);
}
}
@@ -3288,19 +3380,19 @@
nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
struct lyd_node *key;
struct nc_ctn *ctn;
assert(!strcmp(LYD_NAME(node), "cert-to-name"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- ret = nc_server_config_create_cert_to_name(node, endpt->opts.tls);
+ ret = nc_server_config_create_cert_to_name(node, opts);
if (ret) {
goto cleanup;
}
@@ -3312,7 +3404,7 @@
ret = 1;
goto cleanup;
}
- nc_server_config_del_ctn(endpt->opts.tls, ctn);
+ nc_server_config_del_ctn(opts, ctn);
}
cleanup:
@@ -3373,25 +3465,25 @@
nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
const char *version = NULL;
assert(!strcmp(LYD_NAME(node), "tls-version"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
version = ((struct lyd_node_term *)node)->value.ident->name;
if (!strcmp(version, "tls10")) {
- nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_10, op);
+ nc_server_config_set_tls_version(opts, NC_TLS_VERSION_10, op);
} else if (!strcmp(version, "tls11")) {
- nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_11, op);
+ nc_server_config_set_tls_version(opts, NC_TLS_VERSION_11, op);
} else if (!strcmp(version, "tls12")) {
- nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_12, op);
+ nc_server_config_set_tls_version(opts, NC_TLS_VERSION_12, op);
} else if (!strcmp(version, "tls13")) {
- nc_server_config_set_tls_version(endpt->opts.tls, NC_TLS_VERSION_13, op);
+ nc_server_config_set_tls_version(opts, NC_TLS_VERSION_13, op);
} else {
ERR(NULL, "TLS version \"%s\" not supported.", version);
ret = 1;
@@ -3453,7 +3545,7 @@
}
static int
-nc_server_config_del_concrete_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
+nc_server_config_del_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher)
{
int cipher_found = 0;
char *haystack, *substr;
@@ -3490,24 +3582,24 @@
nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
const char *cipher = NULL;
assert(!strcmp(LYD_NAME(node), "cipher-suite"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
cipher = ((struct lyd_node_term *)node)->value.ident->name;
if (op == NC_OP_CREATE) {
- ret = nc_server_config_create_cipher_suite(endpt->opts.tls, cipher);
+ ret = nc_server_config_create_cipher_suite(opts, cipher);
if (ret) {
goto cleanup;
}
} else if (op == NC_OP_DELETE) {
- ret = nc_server_config_del_concrete_cipher_suite(endpt->opts.tls, cipher);
+ ret = nc_server_config_del_cipher_suite(opts, cipher);
if (ret) {
goto cleanup;
}
@@ -3521,25 +3613,25 @@
nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "crl-url"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- nc_server_config_del_url(endpt->opts.tls);
- endpt->opts.tls->crl_url = strdup(lyd_get_value(node));
- if (!endpt->opts.tls->crl_url) {
+ nc_server_config_del_url(opts);
+ opts->crl_url = strdup(lyd_get_value(node));
+ if (!opts->crl_url) {
ERRMEM;
ret = 1;
goto cleanup;
}
} else if (op == NC_OP_DELETE) {
- nc_server_config_del_url(endpt->opts.tls);
+ nc_server_config_del_url(opts);
}
cleanup:
@@ -3550,25 +3642,25 @@
nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "crl-path"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- nc_server_config_del_path(endpt->opts.tls);
- endpt->opts.tls->crl_path = strdup(lyd_get_value(node));
- if (!endpt->opts.tls->crl_path) {
+ nc_server_config_del_path(opts);
+ opts->crl_path = strdup(lyd_get_value(node));
+ if (!opts->crl_path) {
ERRMEM;
ret = 1;
goto cleanup;
}
} else if (op == NC_OP_DELETE) {
- nc_server_config_del_path(endpt->opts.tls);
+ nc_server_config_del_path(opts);
}
cleanup:
@@ -3579,19 +3671,19 @@
nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op)
{
int ret = 0;
- struct nc_endpt *endpt;
+ struct nc_server_tls_opts *opts;
assert(!strcmp(LYD_NAME(node), "crl-cert-ext"));
- if (nc_server_config_get_endpt(node, &endpt, NULL)) {
+ if (nc_server_config_get_tls_opts(node, &opts)) {
ret = 1;
goto cleanup;
}
if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
- endpt->opts.tls->crl_cert_ext = 1;
+ opts->crl_cert_ext = 1;
} else if (op == NC_OP_DELETE) {
- endpt->opts.tls->crl_cert_ext = 0;
+ opts->crl_cert_ext = 0;
}
cleanup:
@@ -3662,6 +3754,8 @@
int ret = 0;
struct nc_ch_endpt *ch_endpt;
+ assert(!strcmp(LYD_NAME(node), "remote-address"));
+
if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
ret = 1;
goto cleanup;
@@ -3690,6 +3784,8 @@
int ret = 0;
struct nc_ch_endpt *ch_endpt;
+ assert(!strcmp(LYD_NAME(node), "remote-port"));
+
if (nc_server_config_get_ch_endpt(node, &ch_endpt)) {
ret = 1;
goto cleanup;
@@ -3708,6 +3804,210 @@
#endif /* NC_ENABLED_SSH_TLS */
static int
+nc_server_config_persistent(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+
+ assert(!strcmp(LYD_NAME(node), "persistent"));
+
+ (void) op;
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ ch_client->conn_type = NC_CH_PERSIST;
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_periodic(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+
+ assert(!strcmp(LYD_NAME(node), "periodic"));
+
+ (void) op;
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ ch_client->conn_type = NC_CH_PERIOD;
+ /* set default values */
+ ch_client->period = 60;
+ ch_client->anchor_time = 0;
+ ch_client->idle_timeout = 180;
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_period(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+
+ assert(!strcmp(LYD_NAME(node), "period"));
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ ch_client->period = strtoul(lyd_get_value(node), NULL, 10);
+ } else if (op == NC_OP_DELETE) {
+ ch_client->period = 60;
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_anchor_time(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+ time_t anchor_time = {0};
+
+ assert(!strcmp(LYD_NAME(node), "anchor-time"));
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ ret = ly_time_str2time(lyd_get_value(node), &anchor_time, NULL);
+ if (ret) {
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ ch_client->anchor_time = anchor_time;
+ } else if (op == NC_OP_DELETE) {
+ ch_client->anchor_time = 0;
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_reconnect_strategy(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+
+ assert(!strcmp(LYD_NAME(node), "reconnect-strategy"));
+
+ (void) op;
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ /* set to default values */
+ ch_client->start_with = NC_CH_FIRST_LISTED;
+ ch_client->max_wait = 5;
+ ch_client->max_attempts = 3;
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_start_with(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+ const char *value;
+
+ assert(!strcmp(LYD_NAME(node), "start-with"));
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if (op == NC_OP_DELETE) {
+ ch_client->start_with = NC_CH_FIRST_LISTED;
+ goto cleanup;
+ }
+
+ value = lyd_get_value(node);
+ if (!strcmp(value, "first-listed")) {
+ ch_client->start_with = NC_CH_FIRST_LISTED;
+ } else if (!strcmp(value, "last-connected")) {
+ ch_client->start_with = NC_CH_LAST_CONNECTED;
+ } else if (!strcmp(value, "random-selection")) {
+ ch_client->start_with = NC_CH_RANDOM;
+ } else {
+ ERR(NULL, "Unexpected start-with value \"%s\".", value);
+ ret = 1;
+ goto cleanup;
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_max_wait(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+
+ assert(!strcmp(LYD_NAME(node), "max-wait"));
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ ch_client->max_wait = strtoul(lyd_get_value(node), NULL, 10);
+ } else {
+ ch_client->max_wait = 5;
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
+nc_server_config_max_attempts(const struct lyd_node *node, NC_OPERATION op)
+{
+ int ret = 0;
+ struct nc_ch_client *ch_client;
+
+ assert(!strcmp(LYD_NAME(node), "max-attempts"));
+
+ if (nc_server_config_get_ch_client(node, &ch_client)) {
+ ret = 1;
+ goto cleanup;
+ }
+
+ if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) {
+ ch_client->max_attempts = strtoul(lyd_get_value(node), NULL, 10);
+ } else {
+ ch_client->max_attempts = 3;
+ }
+
+cleanup:
+ return ret;
+}
+
+static int
nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION op)
{
const char *name = LYD_NAME(node);
@@ -3896,11 +4196,44 @@
}
}
#endif /* NC_ENABLED_SSH_TLS */
+ else if (!strcmp(name, "persistent")) {
+ if (nc_server_config_persistent(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "periodic")) {
+ if (nc_server_config_periodic(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "period")) {
+ if (nc_server_config_period(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "anchor-time")) {
+ if (nc_server_config_anchor_time(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "reconnect-strategy")) {
+ if (nc_server_config_reconnect_strategy(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "start-with")) {
+ if (nc_server_config_start_with(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "max-wait")) {
+ if (nc_server_config_max_wait(node, op)) {
+ goto error;
+ }
+ } else if (!strcmp(name, "max-attempts")) {
+ if (nc_server_config_max_attempts(node, op)) {
+ goto error;
+ }
+ }
return 0;
error:
- ERR(NULL, "Configuring (%s) failed.", LYD_NAME(node));
+ ERR(NULL, "Configuring node \"%s\" failed.", LYD_NAME(node));
return 1;
}
diff --git a/src/server_config.h b/src/server_config.h
index 738e169..103416e 100644
--- a/src/server_config.h
+++ b/src/server_config.h
@@ -588,8 +588,153 @@
int nc_server_config_new_ssh_truststore_reference(const struct ly_ctx *ctx, const char *endpt_name, const char *user_name,
const char *truststore_reference, struct lyd_node **config);
+/**
+ * @brief Creates new YANG configuration data nodes for a call-home server's certificate.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] endpt_name Arbitrary identifier of the call-home client's endpoint.
+ * If a call-home client's endpoint with this identifier already exists, its contents will be changed.
+ * @param[in] pubkey_path Optional path to the server's public key file. If not provided,
+ * it will be generated from the private key.
+ * @param[in] privkey_path Path to the server's private key file.
+ * @param[in] certificate_path Path to the server's certificate file.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_tls_server_certificate(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ const char *pubkey_path, const char *privkey_path, const char *certificate_path, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for a call-home client's (end-entity) certificate.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] endpt_name Arbitrary identifier of the call-home client's endpoint.
+ * If a call-home client's endpoint with this identifier already exists, its contents will be changed.
+ * @param[in] cert_name Arbitrary identifier of the call-home endpoint's end-entity certificate.
+ * If an call-home endpoint's end-entity certificate with this identifier already exists, its contents will be changed.
+ * @param[in] cert_path Path to the certificate file.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_tls_client_certificate(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ const char *cert_name, const char *cert_path, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for a client certificate authority (trust-anchor) certificate.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] endpt_name Arbitrary identifier of the call-home client's endpoint.
+ * If a call-home client's endpoint with this identifier already exists, its contents will be changed.
+ * @param[in] cert_name Arbitrary identifier of the call-home endpoint's certificate authority certificate.
+ * If an call-home endpoint's CA certificate with this identifier already exists, its contents will be changed.
+ * @param[in] cert_path Path to the certificate file.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_tls_client_ca(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ const char *cert_name, const char *cert_path, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for a call-home cert-to-name entry.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] endpt_name Arbitrary identifier of the call-home client's endpoint.
+ * If a call-home client's endpoint with this identifier already exists, its contents will be changed.
+ * @param[in] id ID of the entry. The lower the ID, the higher the priority of the entry (it will be checked earlier).
+ * @param[in] fingerprint Optional fingerprint of the entry. The fingerprint should always be set, however if it is
+ * not set, it will match any certificate. Entry with no fingerprint should therefore be placed only as the last entry.
+ * @param[in] map_type Mapping username to the certificate option.
+ * @param[in] name Username for this cert-to-name entry.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_tls_ctn(const struct ly_ctx *ctx, const char *ch_client_name, const char *endpt_name,
+ uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config);
+
#endif /* NC_ENABLED_SSH_TLS */
+/**
+ * @brief Creates new YANG configuration data nodes for the call-home persistent connection type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for the period parameter of the call-home periodic connection type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] period Duration between periodic connections.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period,
+ struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for the anchor time parameter of the call-home periodic connection type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] anchor_time Timestamp before or after which a series of periodic connections are determined.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name,
+ const char *anchor_time, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for the idle timeout parameter of the call-home periodic connection type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] idle_timeout Specifies the maximum number of seconds that a session may remain idle.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name,
+ uint16_t idle_timeout, struct lyd_node **config);
+
+/**
+ * @brief Creates new YANG configuration data nodes for the call-home reconnect strategy.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ch_client_name Arbitrary identifier of the call-home client.
+ * If a call-home client with this identifier already exists, its contents will be changed.
+ * @param[in] start_with Specifies which endpoint to try if a connection is unsuccessful. Default value is NC_CH_FIRST_LISTED.
+ * @param[in] max_attempts The number of unsuccessful connection attempts before moving to the next endpoint. Default value is 3.
+ * @param[in] max_wait The number of seconds after which a connection to an endpoint is deemed unsuccessful. Default value if 5.
+ * @param config Configuration YANG data tree. If *config is NULL, it will be created.
+ * Otherwise the new YANG data will be added to the previous data and may override it.
+ * @return 0 on success, non-zero otherwise.
+ */
+int nc_server_config_new_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name,
+ NC_CH_START_WITH start_with, uint8_t max_attempts, uint16_t max_wait, struct lyd_node **config);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/session_p.h b/src/session_p.h
index 833e964..3fe3ddd 100644
--- a/src/session_p.h
+++ b/src/session_p.h
@@ -464,6 +464,7 @@
struct nc_session *session;
pthread_t tid;
+ // data * - condition, mutex, thread_running
struct nc_ch_endpt {
char *name;
NC_TRANSPORT_IMPL ti;
@@ -481,17 +482,17 @@
} opts;
} *ch_endpts;
uint16_t ch_endpt_count;
- NC_CH_CONN_TYPE conn_type;
- union {
- struct {
- uint16_t period;
- time_t anchor_time;
- uint16_t idle_timeout;
- } period;
- } conn;
+ NC_CH_CONN_TYPE conn_type;
+ struct {
+ uint16_t period;
+ time_t anchor_time;
+ uint16_t idle_timeout;
+ };
+
NC_CH_START_WITH start_with;
uint8_t max_attempts;
+ uint16_t max_wait;
uint32_t id;
pthread_mutex_t lock;
} *ch_clients;
diff --git a/src/session_server_ch.h b/src/session_server_ch.h
index 42597fe..f1b1b04 100644
--- a/src/session_server_ch.h
+++ b/src/session_server_ch.h
@@ -140,42 +140,6 @@
int max_probes, int probe_interval);
/**
- * @brief Set Call Home client connection type.
- *
- * @param[in] client_name Existing Call Home client name.
- * @param[in] conn_type Call Home connection type.
- * @return 0 on success, -1 on error.
- */
-int nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type);
-
-/**
- * @brief Set Call Home client periodic connection period for reconnecting.
- *
- * @param[in] client_name Existing Call Home client name.
- * @param[in] period Call Home periodic connection period in minutes.
- * @return 0 on success, -1 on error.
- */
-int nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period);
-
-/**
- * @brief Set Call Home client periodic connection period anchor time.
- *
- * @param[in] client_name Existing Call Home client name.
- * @param[in] anchor_time Call Home periodic connection anchor time for the period.
- * @return 0 on success, -1 on error.
- */
-int nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time);
-
-/**
- * @brief Set Call Home client periodic connection idle timeout.
- *
- * @param[in] client_name Existing Call Home client name.
- * @param[in] idle_timeout Call Home periodic idle timeout.
- * @return 0 on success, -1 on error.
- */
-int nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout);
-
-/**
* @brief Set Call Home client start-with policy.
*
* @param[in] client_name Existing Call Home client name.