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.