Bugfix for VES O1 pnfRegistration - both IPv4 and IPv6 were being used.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / features / ves_o1_pnf_registration / ves_o1_pnf_registration.c
1 /*************************************************************************
2 *
3 * Copyright 2022 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 "ves_o1_pnf_registration.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 #include <pthread.h>
29
30 #include "core/session.h"
31 #include "core/framework.h"
32 #include "core/xpath.h"
33
34 static int ves_o1_pnf_sequence_number = 0;
35 static pthread_t ves_o1_pnf_registration_thread;
36 static void* ves_o1_pnf_registration_thread_routine(void *arg);
37 static int ves_o1_pnf_registration_send(sr_session_ctx_t *current_session, const char *nf_ip_v4_address, const char *nf_ip_v6_address, int nf_port, nts_mount_point_addressing_method_t mp, bool is_tls);
38 static cJSON* ves_create_o1_pnf_registration_fields(const char *nf_ip_v4_address, const char *nf_ip_v6_address, int nf_port, bool is_tls);
39
40 static int ves_o1_pnf_registration_status = 0;
41
42 int ves_o1_pnf_registration_feature_get_status(void) {
43     return ves_o1_pnf_registration_status;
44 }
45
46 int ves_o1_pnf_registration_feature_start(sr_session_ctx_t *current_session) {
47     assert(current_session);
48
49     ves_o1_pnf_sequence_number = 0;
50
51     sr_val_t *value = 0;
52     int rc = NTS_ERR_OK;
53     bool o1_pnf_registration_enabled = false;
54     if(strlen(framework_environment.nts.nf_standalone_start_features)) {
55         o1_pnf_registration_enabled = true;
56     }
57     else {
58         rc = sr_get_item(current_session, NTS_NF_VES_O1_PNF_REGISTRATION_SCHEMA_XPATH, 0, &value);
59         if(rc == SR_ERR_OK) {
60             o1_pnf_registration_enabled = value->data.bool_val;
61             sr_free_val(value);
62         }
63         else if(rc != SR_ERR_NOT_FOUND) {
64             log_error("sr_get_item failed\n");
65             return NTS_ERR_FAILED;
66         }
67     }
68
69     if(o1_pnf_registration_enabled == false) {
70         log_add_verbose(2, "O1 PNF registration (stndDefined) is disabled\n");
71         return NTS_ERR_OK;
72     }
73
74     if(pthread_create(&ves_o1_pnf_registration_thread, 0, ves_o1_pnf_registration_thread_routine, current_session)) {
75         log_error("could not create thread for o1 pnf registration\n");
76         return NTS_ERR_FAILED;
77     }
78
79     return NTS_ERR_OK;
80 }
81
82 static void* ves_o1_pnf_registration_thread_routine(void *arg) {
83     sr_session_ctx_t *current_session = arg;
84
85     int ssh_base_port = 0;
86     int tls_base_port = 0;
87     char nf_ip_v4_address[128];
88     char nf_ip_v6_address[128];
89
90     nf_ip_v4_address[0] = 0;
91     nf_ip_v6_address[0] = 0;
92
93     nts_mount_point_addressing_method_t mp = nts_mount_point_addressing_method_get(current_session);
94     if(mp == UNKNOWN_MAPPING) {
95         log_error("mount-point-addressing-method failed\n");
96         return (void*)NTS_ERR_FAILED;
97     }
98     else if(mp == DOCKER_MAPPING) {
99         if (framework_environment.settings.ip_v4 != 0) {
100             strcpy(nf_ip_v4_address, framework_environment.settings.ip_v4);
101         }
102         if (framework_environment.settings.ip_v6 && framework_environment.settings.ip_v6_enabled) {
103             strcpy(nf_ip_v6_address, framework_environment.settings.ip_v6);
104         }
105
106         ssh_base_port = STANDARD_NETCONF_PORT;
107         tls_base_port = ssh_base_port + framework_environment.settings.ssh_connections;
108     }
109     else {
110         if(framework_environment.settings.ip_v6_enabled) {
111             strcpy(nf_ip_v6_address, framework_environment.host.ip);
112         }
113         else {
114             strcpy(nf_ip_v4_address, framework_environment.host.ip);
115         }
116
117         ssh_base_port = framework_environment.host.ssh_base_port;
118         tls_base_port = framework_environment.host.tls_base_port;
119     }
120
121     uint32_t total_regs = 0;
122     struct regs_s {
123         bool sent;
124         uint16_t port;
125         bool is_tls;
126     } *regs;
127
128     regs = (struct regs_s *)malloc(sizeof(struct regs_s) * (1 + framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections));
129     if(regs == 0) {
130         log_error("malloc failed\n");
131         return (void*)NTS_ERR_FAILED;
132     }
133
134
135     if((framework_environment.settings.ssh_connections + framework_environment.settings.tls_connections) > 1) {
136         for(int port = ssh_base_port; port < ssh_base_port + framework_environment.settings.ssh_connections; port++) {
137             regs[total_regs].sent = false;
138             regs[total_regs].port = port;
139             regs[total_regs].is_tls = false;
140             total_regs++;
141         }
142
143         for(int port = tls_base_port; port < tls_base_port + framework_environment.settings.tls_connections; port++) {
144             regs[total_regs].sent = false;
145             regs[total_regs].port = port;
146             regs[total_regs].is_tls = true;
147             total_regs++;
148         }
149     }
150     else {
151         bool tls;
152         if(framework_environment.settings.tls_connections == 0) {
153             tls = false;
154         }
155         else {
156             tls = true;
157         }
158
159         regs[total_regs].sent = false;
160         regs[total_regs].port = 0;
161         regs[total_regs].is_tls = tls;
162         total_regs++;
163     }
164
165     uint32_t remaining = total_regs;
166     while(remaining) {
167         for(int i = 0; i < total_regs; i++) {
168             if(regs[i].sent == false) {
169                 uint16_t port = regs[i].port;
170                 bool is_tls = regs[i].is_tls;
171                 int rc = ves_o1_pnf_registration_send(current_session, nf_ip_v4_address, nf_ip_v6_address, port, mp, is_tls);
172                 if(rc == NTS_ERR_OK) {
173                     remaining--;
174                     regs[i].sent = true;
175                 }
176                 else {
177                     log_error("O1 pnfRegistration (stndDefined) failed for ipv4=%s ipv6=%s port=%d is_tls=%d\n", nf_ip_v4_address, nf_ip_v6_address, port, is_tls);
178                 }
179             }
180         }
181         if(remaining) {
182             log_error("O1 pnfRegistration (stndDefined) could not register all ports; retrying in 5 seconds...\n");
183             sleep(5);
184         }
185     }
186     free(regs);
187     log_add_verbose(2, "O1 PNF registration (stndDefined) finished\n");
188     ves_o1_pnf_registration_status = 1;
189
190     return NTS_ERR_OK;
191 }
192
193 static int ves_o1_pnf_registration_send(sr_session_ctx_t *current_session, const char *nf_ip_v4_address, const char *nf_ip_v6_address, int nf_port, nts_mount_point_addressing_method_t mp, bool is_tls) {
194     assert(current_session);
195
196     cJSON *post_data_json = cJSON_CreateObject();
197     if(post_data_json == 0) {
198         log_error("could not create cJSON object\n");
199         return NTS_ERR_FAILED;
200     }
201
202     cJSON *event = cJSON_CreateObject();
203     if(event == 0) {
204         log_error("could not create cJSON object\n");
205         cJSON_Delete(post_data_json);
206         return NTS_ERR_FAILED;
207     }
208
209     if(cJSON_AddItemToObject(post_data_json, "event", event) == 0) {
210         log_error("cJSON_AddItemToObject failed\n");
211         cJSON_Delete(post_data_json);
212         return NTS_ERR_FAILED;
213     }
214
215     char *hostname_string = framework_environment.settings.hostname;
216     cJSON *common_event_header = ves_create_common_event_header_721("stndDefined", "o1-notify-pnf-registration", hostname_string, nf_port, "Low", ves_o1_pnf_sequence_number++, "o1-notify-pnf-registration");
217     if(common_event_header == 0) {
218         log_error("could not create cJSON object\n");
219         cJSON_Delete(post_data_json);
220         return NTS_ERR_FAILED;
221     }
222
223     if(nf_port == 0) {
224         if(mp == DOCKER_MAPPING) {
225             nf_port = STANDARD_NETCONF_PORT;
226         }
227         else {
228             if(is_tls) {
229                 nf_port = framework_environment.host.tls_base_port;
230             }
231             else {
232                 nf_port = framework_environment.host.ssh_base_port;
233             }
234         }
235     }
236
237     if(cJSON_AddItemToObject(event, "commonEventHeader", common_event_header) == 0) {
238         log_error("cJSON_AddItemToObject failed\n");
239         cJSON_Delete(post_data_json);
240         return NTS_ERR_FAILED;
241     }
242
243         cJSON *o1_pnf_registration_fields = ves_create_o1_pnf_registration_fields(nf_ip_v4_address, nf_ip_v6_address, nf_port, is_tls);
244     if(o1_pnf_registration_fields == 0) {
245         log_error("could not create cJSON object\n");
246         cJSON_Delete(post_data_json);
247         return NTS_ERR_FAILED;
248     }
249
250     if(cJSON_AddItemToObject(event, "stndDefinedFields", o1_pnf_registration_fields) == 0) {
251         log_error("cJSON_AddItemToObject failed\n");
252         cJSON_Delete(post_data_json);
253         return NTS_ERR_FAILED;
254     }
255
256     char *post_data = cJSON_PrintUnformatted(post_data_json);
257     cJSON_Delete(post_data_json);
258     if(post_data == 0) {
259         log_error("cJSON_PrintUnformatted failed\n");
260         return NTS_ERR_FAILED;
261     }
262
263
264     ves_details_t *ves_details = ves_endpoint_details_get(current_session, 0);
265     if(!ves_details) {
266         log_error("ves_endpoint_details_get failed\n");
267         free(post_data);
268         return NTS_ERR_FAILED;
269     }
270
271     int rc = http_request(ves_details->url, ves_details->username, ves_details->password, "POST", post_data, 0, 0);
272     ves_details_free(ves_details);
273     free(post_data);
274
275     if(rc != NTS_ERR_OK) {
276         log_error("http_request failed\n");
277         return NTS_ERR_FAILED;
278     }
279
280     return NTS_ERR_OK;
281 }
282
283 static cJSON* ves_create_o1_pnf_registration_fields(const char *nf_ip_v4_address, const char *nf_ip_v6_address, int nf_port, bool is_tls) {
284
285     cJSON *stnd_defined_fields = cJSON_CreateObject();
286     if(stnd_defined_fields == 0) {
287         log_error("could not create JSON object\n");
288         return 0;
289     }
290
291     if(cJSON_AddStringToObject(stnd_defined_fields, "stndDefinedFieldsVersion", "1.0") == 0) {
292         log_error("cJSON_AddItemToObject failed\n");
293         cJSON_Delete(stnd_defined_fields);
294         return 0;
295     }
296
297     if(cJSON_AddStringToObject(stnd_defined_fields, "schemaReference", "https://gerrit.o-ran-sc.org/r/gitweb?p=scp/oam/modeling.git;a=blob;f=data-model/yang/working/o-ran-sc/template/yes-o1-notify-pnf-registration.yang#/components/schemas/o1-notify-pnf-registration") == 0) {
298         log_error("cJSON_AddItemToObject failed\n");
299         cJSON_Delete(stnd_defined_fields);
300         return 0;
301     }
302
303     cJSON *data = cJSON_CreateObject();
304     if(data == 0) {
305         log_error("could not create JSON object\n");
306         cJSON_Delete(stnd_defined_fields);
307         return 0;
308     }
309     cJSON_AddItemToObject(stnd_defined_fields, "data", data);
310
311     if(cJSON_AddStringToObject(data, "object-class", "manged-function") == 0) {
312         log_error("cJSON_AddItemToObject failed\n");
313         cJSON_Delete(stnd_defined_fields);
314         return 0;
315     }
316
317     if(cJSON_AddStringToObject(data, "object-instance", "YES-API-PROVIDER") == 0) {
318         log_error("cJSON_AddItemToObject failed\n");
319         cJSON_Delete(stnd_defined_fields);
320         return 0;
321     }
322
323     if(cJSON_AddNumberToObject(data, "notification-identifier", (double)(0)) == 0) {
324         log_error("cJSON_AddItemToObject failed\n");
325         cJSON_Delete(stnd_defined_fields);
326         return 0;
327     }
328
329     if(cJSON_AddStringToObject(data, "notification-type", "o1-notify-pnf-registration") == 0) {
330         log_error("cJSON_AddItemToObject failed\n");
331         cJSON_Delete(stnd_defined_fields);
332         return 0;
333     }
334
335     char *current_date_and_time = get_current_date_and_time();
336     if(current_date_and_time == 0) {
337         log_error("get_current_date_and_time failed\n");
338         cJSON_Delete(stnd_defined_fields);
339         return 0;
340     }
341
342     if(cJSON_AddStringToObject(data, "event-time", current_date_and_time) == 0) {
343         log_error("cJSON_AddItemToObject failed\n");
344         cJSON_Delete(stnd_defined_fields);
345         free(current_date_and_time);
346         return 0;
347     }
348     free(current_date_and_time);
349
350     if(cJSON_AddStringToObject(data, "system-distinguished-name", "DN:managed-element=YES-API-PROVIDER") == 0) {
351         log_error("cJSON_AddItemToObject failed\n");
352         cJSON_Delete(stnd_defined_fields);
353         return 0;
354     }
355
356     if(cJSON_AddStringToObject(data, "o1-specification-version", "v07.00") == 0) {
357         log_error("cJSON_AddItemToObject failed\n");
358         cJSON_Delete(stnd_defined_fields);
359         return 0;
360     }
361
362     char serial_number[512];
363     sprintf(serial_number, "%s-%s-%d-Simulated Device Melacon", framework_environment.settings.hostname, nf_ip_v4_address, nf_port);
364
365     if(cJSON_AddStringToObject(data, "serial-number", serial_number) == 0) {
366         log_error("cJSON_AddItemToObject failed\n");
367         cJSON_Delete(stnd_defined_fields);
368         return 0;
369     }
370
371     if(cJSON_AddNumberToObject(data, "vendor-pen", (double)(57272)) == 0) {
372         log_error("cJSON_AddItemToObject failed\n");
373         cJSON_Delete(stnd_defined_fields);
374         return 0;
375     }
376
377     if (!framework_environment.settings.ip_v6_enabled) {
378         if (nf_ip_v4_address != 0 && strlen(nf_ip_v4_address) > 0) {
379             if(cJSON_AddStringToObject(data, "oam-host", nf_ip_v4_address) == 0) {
380                 log_error("cJSON_AddItemToObject failed\n");
381                 cJSON_Delete(stnd_defined_fields);
382                 return 0;
383             }
384         }
385     }
386     else {
387         if (nf_ip_v6_address != 0 && strlen(nf_ip_v6_address) > 0) {
388             if(cJSON_AddStringToObject(data, "oam-host", nf_ip_v6_address) == 0) {
389                 log_error("cJSON_AddItemToObject failed\n");
390                 cJSON_Delete(stnd_defined_fields);
391                 return 0;
392             }
393         }
394     }
395
396     if(cJSON_AddNumberToObject(data, "oam-port", (double)(nf_port)) == 0) {
397         log_error("cJSON_AddItemToObject failed\n");
398         cJSON_Delete(stnd_defined_fields);
399         return 0;
400     }
401
402     char *mac_addr = rand_mac_address();
403     if(mac_addr == 0) {
404         log_error("rand_mac_address failed\n")
405         cJSON_Delete(stnd_defined_fields);
406         return 0;
407     }
408
409     if(cJSON_AddStringToObject(data, "mac-address", mac_addr) == 0) {
410         log_error("cJSON_AddItemToObject failed\n");
411         cJSON_Delete(stnd_defined_fields);
412         free(mac_addr);
413         return 0;
414     }
415     free(mac_addr);
416
417     if(cJSON_AddStringToObject(data, "unit-family", "O-RAN-SC SIM") == 0) {
418         log_error("cJSON_AddItemToObject failed\n");
419         cJSON_Delete(stnd_defined_fields);
420         return 0;
421     }
422
423     if(cJSON_AddStringToObject(data, "unit-type", "O1-Interface") == 0) {
424         log_error("cJSON_AddItemToObject failed\n");
425         cJSON_Delete(stnd_defined_fields);
426         return 0;
427     }
428
429     if(cJSON_AddStringToObject(data, "model-number", "1859a6ea-2520-11ed-861d-0242ac120002") == 0) {
430         log_error("cJSON_AddItemToObject failed\n");
431         cJSON_Delete(stnd_defined_fields);
432         return 0;
433     }
434
435     if(cJSON_AddStringToObject(data, "software-version", "G-Release") == 0) {
436         log_error("cJSON_AddItemToObject failed\n");
437         cJSON_Delete(stnd_defined_fields);
438         return 0;
439     }
440
441     if(cJSON_AddStringToObject(data, "restart-reason", "External trigger") == 0) {
442         log_error("cJSON_AddItemToObject failed\n");
443         cJSON_Delete(stnd_defined_fields);
444         return 0;
445     }
446
447     if(cJSON_AddStringToObject(data, "manufacture-date", "2022-08-26Z") == 0) {
448         log_error("cJSON_AddItemToObject failed\n");
449         cJSON_Delete(stnd_defined_fields);
450         return 0;
451     }
452
453     if(cJSON_AddStringToObject(data, "last-service-date", "2022-08-26Z") == 0) {
454         log_error("cJSON_AddItemToObject failed\n");
455         cJSON_Delete(stnd_defined_fields);
456         return 0;
457     }
458
459     if(cJSON_AddStringToObject(data, "username", "netconf") == 0) {
460         log_error("cJSON_AddItemToObject failed\n");
461         cJSON_Delete(stnd_defined_fields);
462         return 0;
463     }
464
465     if(is_tls) {
466         //TLS specific configuration
467         if(cJSON_AddStringToObject(data, "transport-protocol", "tls") == 0) {
468             log_error("cJSON_AddItemToObject failed\n");
469             cJSON_Delete(stnd_defined_fields);
470             return 0;
471         }
472
473         if(cJSON_AddStringToObject(data, "key-reference", KS_KEY_NAME) == 0) {
474             log_error("cJSON_AddItemToObject failed\n");
475             cJSON_Delete(stnd_defined_fields);
476             return 0;
477         }
478     }
479     else {
480         //SSH specific configuration
481         if(cJSON_AddStringToObject(data, "transport-protocol", "ssh") == 0) {
482             log_error("cJSON_AddItemToObject failed\n");
483             cJSON_Delete(stnd_defined_fields);
484             return 0;
485         }
486
487         // hardcoded password here
488         if(cJSON_AddStringToObject(data, "password", "netconf!") == 0) {
489             log_error("cJSON_AddItemToObject failed\n");
490             cJSON_Delete(stnd_defined_fields);
491             return 0;
492         }
493     }
494
495     if(cJSON_AddFalseToObject(data, "reconnect-on-changed-schema") == 0) {
496         log_error("cJSON_AddItemToObject failed\n");
497         cJSON_Delete(stnd_defined_fields);
498         return 0;
499     }
500
501     if(cJSON_AddNumberToObject(data, "connection-timeout", (double)(20000)) == 0) {
502         log_error("cJSON_AddItemToObject failed\n");
503         cJSON_Delete(stnd_defined_fields);
504         return 0;
505     }
506
507     if(cJSON_AddNumberToObject(data, "max-connection-attempts", (double)(100)) == 0) {
508         log_error("cJSON_AddItemToObject failed\n");
509         cJSON_Delete(stnd_defined_fields);
510         return 0;
511     }
512
513     if(cJSON_AddNumberToObject(data, "between-attempts-timeout", (double)(2000)) == 0) {
514         log_error("cJSON_AddItemToObject failed\n");
515         cJSON_Delete(stnd_defined_fields);
516         return 0;
517     }
518
519     if(cJSON_AddNumberToObject(data, "sleep-factor", (double)(1.5)) == 0) {
520         log_error("cJSON_AddItemToObject failed\n");
521         cJSON_Delete(stnd_defined_fields);
522         return 0;
523     }
524
525     if(cJSON_AddNumberToObject(data, "keepalive-delay", (double)(120)) == 0) {
526         log_error("cJSON_AddItemToObject failed\n");
527         cJSON_Delete(stnd_defined_fields);
528         return 0;
529     }
530
531     return stnd_defined_fields;
532 }