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