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