1 /*************************************************************************
3 * Copyright 2020 highstreet technologies GmbH and others
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
20 #include "netconf_call_home.h"
21 #include "utils/log_utils.h"
22 #include "utils/sys_utils.h"
23 #include "utils/rand_utils.h"
24 #include "utils/http_client.h"
25 #include "utils/nts_utils.h"
29 #include "core/session.h"
30 #include "core/framework.h"
32 #define NETCONF_CALLHOME_ENABLED_SCHEMA_PATH "/nts-network-function:simulation/network-function/netconf/call-home"
33 #define NETCONF_CALLHOME_CURL_SEND_PAYLOAD_FORMAT "{\"odl-netconf-callhome-server:device\":[{\"odl-netconf-callhome-server:unique-id\":\"%s\",\"odl-netconf-callhome-server:ssh-host-key\":\"%s\",\"odl-netconf-callhome-server:credentials\":{\"odl-netconf-callhome-server:username\":\"netconf\",\"odl-netconf-callhome-server:passwords\":[\"netconf!\"]}}]}"
34 #define SDN_CONTROLLER_DETAILS_SCHEMA_PATH "/nts-network-function:simulation/sdn-controller"
36 static int create_ssh_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node);
37 static int create_tls_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node);
38 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session);
40 static int netconf_call_home_status = 0;
42 int netconf_call_home_feature_get_status(void) {
43 return netconf_call_home_status;
46 int netconf_call_home_feature_start(sr_session_ctx_t *current_session) {
47 assert(current_session);
52 bool callhome_enabled = false;
53 int rc = sr_get_item(current_session, NETCONF_CALLHOME_ENABLED_SCHEMA_PATH, 0, &value);
55 callhome_enabled = value->data.bool_val;
59 // if value is not set yet, feature enable means we want to start call-home
60 callhome_enabled = true;
63 if(callhome_enabled == false) {
64 log_add_verbose(2, "NETCONF CallHome is not enabled, not configuring NETCONF Server.\n");
68 struct lyd_node *netconf_node = 0;
69 netconf_node = lyd_new_path(NULL, session_context, "/ietf-netconf-server:netconf-server", 0, 0, 0);
70 if(netconf_node == 0) {
71 log_error("could not create a new lyd_node\n");
72 return NTS_ERR_FAILED;
75 rc = create_ssh_callhome_endpoint(current_session, netconf_node);
76 if(rc != NTS_ERR_OK) {
77 log_error("could not create SSH CallHome endpoint on the NETCONF Server\n");
78 return NTS_ERR_FAILED;
81 rc = create_tls_callhome_endpoint(current_session, netconf_node);
82 if(rc != NTS_ERR_OK) {
83 log_error("could not create TLS CallHome endpoint on the NETCONF Server\n");
84 return NTS_ERR_FAILED;
87 rc = sr_edit_batch(current_session, netconf_node, "merge");
89 log_error("could not edit batch on datastore\n");
90 return NTS_ERR_FAILED;
93 rc = sr_validate(current_session, "ietf-netconf-server", 0);
95 log_error("sr_validate issues on STARTUP\n");
96 return NTS_ERR_FAILED;
99 rc = sr_apply_changes(current_session, 0, 0);
100 if(rc != SR_ERR_OK) {
101 log_error("could not apply changes on datastore\n");
102 return NTS_ERR_FAILED;
105 lyd_free_withsiblings(netconf_node);
106 netconf_call_home_status = 1;
112 static int create_ssh_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node) {
113 assert(current_session);
114 assert(netconf_node);
116 controller_details_t *controller = controller_details_get(current_session);
117 if(controller == 0) {
118 log_error("controller_details_get failed\n");
119 return NTS_ERR_FAILED;
122 char *controller_ip = strdup(controller->ip);
123 uint16_t controller_callhome_port = controller->nc_callhome_port;
124 controller_details_free(controller);
126 if(controller_ip == 0) {
127 log_error("strdup failed\n");
128 return NTS_ERR_FAILED;
132 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/tcp-client-parameters/keepalives/idle-time");
133 struct lyd_node *rcl = lyd_new_path(netconf_node, 0, xpath, "1", 0, LYD_PATH_OPT_NOPARENTRET);
135 log_error("could not created yang path\n");
137 return NTS_ERR_FAILED;
140 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/tcp-client-parameters/keepalives/max-probes");
141 rcl = lyd_new_path(netconf_node, 0, xpath, "10", 0, LYD_PATH_OPT_NOPARENTRET);
143 log_error("could not created yang path\n");
145 return NTS_ERR_FAILED;
148 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/tcp-client-parameters/keepalives/probe-interval");
149 rcl = lyd_new_path(netconf_node, 0, xpath, "5", 0, LYD_PATH_OPT_NOPARENTRET);
151 log_error("could not created yang path\n");
153 return NTS_ERR_FAILED;
156 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/tcp-client-parameters/remote-address");
157 rcl = lyd_new_path(netconf_node, 0, xpath, controller_ip, 0, LYD_PATH_OPT_NOPARENTRET);
159 log_error("could not created yang path\n");
161 return NTS_ERR_FAILED;
165 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/tcp-client-parameters/remote-port");
167 sprintf(port, "%d", controller_callhome_port);
168 rcl = lyd_new_path(netconf_node, 0, xpath, port, 0, LYD_PATH_OPT_NOPARENTRET);
170 log_error("could not created yang path\n");
171 return NTS_ERR_FAILED;
174 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/ssh-server-parameters/server-identity/host-key[name='default-key']/public-key/keystore-reference");
175 rcl = lyd_new_path(netconf_node, 0, xpath, KS_KEY_NAME, 0, LYD_PATH_OPT_NOPARENTRET);
177 log_error("could not created yang path\n");
178 return NTS_ERR_FAILED;
181 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/ssh-server-parameters/client-authentication/supported-authentication-methods/publickey");
182 rcl = lyd_new_path(netconf_node, 0, xpath, "", 0, LYD_PATH_OPT_NOPARENTRET);
184 log_error("could not created yang path\n");
185 return NTS_ERR_FAILED;
188 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/ssh-server-parameters/client-authentication/supported-authentication-methods/passsword");
189 rcl = lyd_new_path(netconf_node, 0, xpath, "", 0, LYD_PATH_OPT_NOPARENTRET);
191 log_error("could not created yang path\n");
192 return NTS_ERR_FAILED;
195 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/ssh-server-parameters/client-authentication/supported-authentication-methods/other");
196 rcl = lyd_new_path(netconf_node, 0, xpath, "interactive", 0, LYD_PATH_OPT_NOPARENTRET);
198 log_error("could not created yang path\n");
199 return NTS_ERR_FAILED;
202 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/endpoints/endpoint[name='callhome-ssh']/ssh/ssh-server-parameters/client-authentication/users");
203 rcl = lyd_new_path(netconf_node, 0, xpath, "", 0, LYD_PATH_OPT_NOPARENTRET);
205 log_error("could not created yang path\n");
206 return NTS_ERR_FAILED;
209 sprintf(xpath, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='default-client']/connection-type/persistent");
210 rcl = lyd_new_path(netconf_node, 0, xpath, "", 0, LYD_PATH_OPT_NOPARENTRET);
212 log_error("could not created yang path\n");
213 return NTS_ERR_FAILED;
216 int rc = send_odl_callhome_configuration(current_session);
217 if(rc != NTS_ERR_OK) {
218 log_add_verbose(2, "could not send ODL Call Home configuration.\n");
224 static int create_tls_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node) {
225 assert(current_session);
226 assert(netconf_node);
228 // checkAS future usage, TLS endpoint yet supported in ODL
234 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session) {
235 assert(current_session);
237 char *public_ssh_key = read_key(SERVER_PUBLIC_SSH_KEY_PATH);
238 if(public_ssh_key == 0) {
239 log_error("could not read the public ssh key from file %s\n", SERVER_PUBLIC_SSH_KEY_PATH);
240 return NTS_ERR_FAILED;
243 char *ssh_key_string;
244 ssh_key_string = strtok(public_ssh_key, " ");
245 ssh_key_string = strtok(NULL, " ");
246 ssh_key_string[strlen(ssh_key_string) - 1] = 0; // trim the newline character
248 // checkAS we have hardcoded here the username and password of the NETCONF Server
249 char *odl_callhome_payload = 0;
250 asprintf(&odl_callhome_payload, NETCONF_CALLHOME_CURL_SEND_PAYLOAD_FORMAT, framework_environment.settings.hostname, ssh_key_string);
251 free(public_ssh_key);
252 if(odl_callhome_payload == 0) {
253 log_error("bad asprintf\n");
254 return NTS_ERR_FAILED;
257 controller_details_t *controller = controller_details_get(current_session);
258 if(controller == 0) {
259 log_error("controller_details_get failed\n");
260 return NTS_ERR_FAILED;
264 asprintf(&url, "%s/rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=%s", controller->base_url, framework_environment.settings.hostname);
266 log_error("bad asprintf\n");
267 controller_details_free(controller);
268 return NTS_ERR_FAILED;
271 int rc = http_request(url, controller->username, controller->password, "PUT", odl_callhome_payload, 0, 0);
272 if(rc != NTS_ERR_OK) {
273 log_error("http_request failed\n");
277 controller_details_free(controller);
278 free(odl_callhome_payload);