a56e45b42467a62249735b6672624e8de59b6c51
[sim/o1-interface.git] / ntsimulator / ntsim-ng / features / netconf_call_home / netconf_call_home.c
1 /*************************************************************************
2 *
3 * Copyright 2020 highstreet technologies GmbH and others
4 *
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
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
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 ***************************************************************************/
17
18 #define _GNU_SOURCE
19
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"
26 #include <stdio.h>
27 #include <assert.h>
28
29 #include "core/session.h"
30 #include "core/framework.h"
31 #include "core/xpath.h"
32
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
35 static int create_ssh_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node);
36 static int create_tls_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node);
37 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session);
38
39 static int netconf_call_home_status = 0;
40
41 int netconf_call_home_feature_get_status(void) {
42     return netconf_call_home_status;
43 }
44
45 int netconf_call_home_feature_start(sr_session_ctx_t *current_session) {
46     assert(current_session);
47     assert_session();
48
49     sr_val_t *value = 0;
50
51     bool callhome_enabled = false;
52     int rc = sr_get_item(current_session, NTS_NF_NETCONF_CALLHOME_ENABLED_SCHEMA_PATH, 0, &value);
53     if(rc == SR_ERR_OK) {
54         callhome_enabled = value->data.bool_val;
55         sr_free_val(value);
56     }
57     else {
58         // if value is not set yet, feature enable means we want to start call-home
59         if(strlen(framework_environment.nts.nf_standalone_start_features)) {
60             callhome_enabled = true;
61         }
62     }
63
64     if(callhome_enabled == false) {
65         log_add_verbose(2, "NETCONF CallHome is not enabled, not configuring NETCONF Server.\n");
66         return NTS_ERR_OK;
67     }
68
69     struct lyd_node *netconf_node = 0;
70     netconf_node = lyd_new_path(NULL, session_context, IETF_NETCONF_SERVER_SCHEMA_XPATH, 0, 0, 0);
71     if(netconf_node == 0) {
72         log_error("could not create a new lyd_node\n");
73         return NTS_ERR_FAILED;
74     }
75
76     rc = create_ssh_callhome_endpoint(current_session, netconf_node);
77     if(rc != NTS_ERR_OK) {
78         log_error("could not create SSH CallHome endpoint on the NETCONF Server\n");
79         return NTS_ERR_FAILED;
80     }
81
82     rc = create_tls_callhome_endpoint(current_session, netconf_node);
83     if(rc != NTS_ERR_OK) {
84         log_error("could not create TLS CallHome endpoint on the NETCONF Server\n");
85         return NTS_ERR_FAILED;
86     }
87
88     rc = sr_edit_batch(current_session, netconf_node, "merge");
89     if(rc != SR_ERR_OK) {
90         log_error("could not edit batch on datastore\n");
91         return NTS_ERR_FAILED;
92     }
93
94     rc = sr_validate(current_session, IETF_NETCONF_SERVER_MODULE, 0);
95     if(rc != SR_ERR_OK) {
96         log_error("sr_validate issues on STARTUP\n");
97         return NTS_ERR_FAILED;
98     }
99
100     rc = sr_apply_changes(current_session, 0, 0);
101     if(rc != SR_ERR_OK) {
102         log_error("could not apply changes on datastore\n");
103         return NTS_ERR_FAILED;
104     }
105
106     lyd_free_withsiblings(netconf_node);
107     netconf_call_home_status = 1;
108
109     return NTS_ERR_OK;
110 }
111
112
113 static int create_ssh_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node) {
114     assert(current_session);
115     assert(netconf_node);
116
117     controller_details_t *controller = controller_details_get(current_session);
118     if(controller == 0) {
119         log_error("controller_details_get failed\n");
120         return NTS_ERR_FAILED;
121     }
122
123     char *controller_ip = strdup(controller->ip);
124     uint16_t controller_callhome_port = controller->nc_callhome_port;
125     controller_details_free(controller);
126
127     if(controller_ip == 0) {
128         log_error("strdup failed\n");
129         return NTS_ERR_FAILED;
130     }
131
132     
133     struct lyd_node *rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/keepalives/idle-time", "1", 0, LYD_PATH_OPT_NOPARENTRET);
134     if(rcl == 0) {
135         log_error("could not created yang path\n");
136         free(controller_ip);
137         return NTS_ERR_FAILED;
138     }
139
140     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/keepalives/max-probes", "10", 0, LYD_PATH_OPT_NOPARENTRET);
141     if(rcl == 0) {
142         log_error("could not created yang path\n");
143         free(controller_ip);
144         return NTS_ERR_FAILED;
145     }
146
147     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/keepalives/probe-interval", "5", 0, LYD_PATH_OPT_NOPARENTRET);
148     if(rcl == 0) {
149         log_error("could not created yang path\n");
150         free(controller_ip);
151         return NTS_ERR_FAILED;
152     }
153
154     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/remote-address", controller_ip, 0, LYD_PATH_OPT_NOPARENTRET);
155     if(rcl == 0) {
156         log_error("could not created yang path\n");
157         free(controller_ip);
158         return NTS_ERR_FAILED;
159     }
160     free(controller_ip);
161
162     char port[20];
163     sprintf(port, "%d", controller_callhome_port);
164     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_TCP_CLIENT_SCHEMA_XPATH"/remote-port", port, 0, LYD_PATH_OPT_NOPARENTRET);
165     if(rcl == 0) {
166         log_error("could not created yang path\n");
167         return NTS_ERR_FAILED;
168     }
169
170     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/server-identity/host-key[name='default-key']/public-key/keystore-reference", KS_KEY_NAME, 0, LYD_PATH_OPT_NOPARENTRET);
171     if(rcl == 0) {
172         log_error("could not created yang path\n");
173         return NTS_ERR_FAILED;
174     }
175
176     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/supported-authentication-methods/publickey", "", 0, LYD_PATH_OPT_NOPARENTRET);
177     if(rcl == 0) {
178         log_error("could not created yang path\n");
179         return NTS_ERR_FAILED;
180     }
181
182     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/supported-authentication-methods/passsword", "", 0, LYD_PATH_OPT_NOPARENTRET);
183     if(rcl == 0) {
184         log_error("could not created yang path\n");
185         return NTS_ERR_FAILED;
186     }
187
188     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/supported-authentication-methods/other", "interactive", 0, LYD_PATH_OPT_NOPARENTRET);
189     if(rcl == 0) {
190         log_error("could not created yang path\n");
191         return NTS_ERR_FAILED;
192     }
193
194     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_SSH_SERVER_PARAMS_SCEHMA_XPATH"/client-authentication/users", "", 0, LYD_PATH_OPT_NOPARENTRET);
195     if(rcl == 0) {
196         log_error("could not created yang path\n");
197         return NTS_ERR_FAILED;
198     }
199     rcl = lyd_new_path(netconf_node, 0, IETF_NETCONF_SERVER_CH_CONN_PERSISTENT_SCHEMA_XPATH, "", 0, LYD_PATH_OPT_NOPARENTRET);
200     if(rcl == 0) {
201         log_error("could not created yang path\n");
202         return NTS_ERR_FAILED;
203     }
204
205     int rc = send_odl_callhome_configuration(current_session);
206     if(rc != NTS_ERR_OK) {
207         log_add_verbose(2, "could not send ODL Call Home configuration.\n");
208     }
209
210     return NTS_ERR_OK;
211 }
212
213 static int create_tls_callhome_endpoint(sr_session_ctx_t *current_session, struct lyd_node *netconf_node) {
214     assert(current_session);
215     assert(netconf_node);
216
217     // checkAS future usage, TLS endpoint yet supported in ODL
218     
219     return NTS_ERR_OK;
220 }
221
222
223 static int send_odl_callhome_configuration(sr_session_ctx_t *current_session) {
224     assert(current_session);
225
226     char *public_ssh_key = read_key(SERVER_PUBLIC_SSH_KEY_PATH);
227     if(public_ssh_key == 0) {
228         log_error("could not read the public ssh key from file %s\n", SERVER_PUBLIC_SSH_KEY_PATH);
229         return NTS_ERR_FAILED;
230     }
231
232     char *ssh_key_string;
233     ssh_key_string = strtok(public_ssh_key, " ");
234     ssh_key_string = strtok(NULL, " ");
235     ssh_key_string[strlen(ssh_key_string) - 1] = 0; // trim the newline character
236
237     // checkAS we have hardcoded here the username and password of the NETCONF Server
238     char *odl_callhome_payload = 0;
239     asprintf(&odl_callhome_payload, NETCONF_CALLHOME_CURL_SEND_PAYLOAD_FORMAT, framework_environment.settings.hostname, ssh_key_string);
240     free(public_ssh_key);
241     if(odl_callhome_payload == 0) {
242         log_error("bad asprintf\n");
243         return NTS_ERR_FAILED;
244     }
245
246     controller_details_t *controller = controller_details_get(current_session);
247     if(controller == 0) {
248         log_error("controller_details_get failed\n");
249         return NTS_ERR_FAILED;
250     }
251     
252     char *url = 0;
253     asprintf(&url, "%s/rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=%s", controller->base_url, framework_environment.settings.hostname);
254     if(url == 0) {
255         log_error("bad asprintf\n");
256         controller_details_free(controller);
257         return NTS_ERR_FAILED;
258     }
259
260     int rc = http_request(url, controller->username, controller->password, "PUT", odl_callhome_payload, 0, 0);
261     if(rc != NTS_ERR_OK) {
262         log_error("http_request failed\n");
263     }
264     
265     free(url);
266     controller_details_free(controller);
267     free(odl_callhome_payload);
268
269     return rc;
270 }