From 23e16013bee15fb395df6b30b5c16994591c2b93 Mon Sep 17 00:00:00 2001 From: suvendhu Date: Mon, 6 Jun 2022 11:35:21 +0530 Subject: [PATCH] T#8134: ACS connectivity issue after bootup --- common.c | 12 +- config.c | 199 +++++++++++++++--- cwmp.c | 25 ++- gitlab-ci/functional-test.sh | 3 - gitlab-ci/setup.sh | 6 + gitlab-ci/shared.sh | 25 ++- heartbeat.c | 13 +- inc/cwmp_uci.h | 5 + test/files/tmp/network.interface.wan.data | 14 ++ .../usr/libexec/rpcd/network.interface.wan | 17 ++ ubus_utils.c | 6 +- 11 files changed, 272 insertions(+), 53 deletions(-) create mode 100644 test/files/tmp/network.interface.wan.data create mode 100755 test/files/usr/libexec/rpcd/network.interface.wan diff --git a/common.c b/common.c index e9cac04..99bea76 100755 --- a/common.c +++ b/common.c @@ -718,17 +718,21 @@ int get_connection_interface() struct blob_buf b = { 0 }; memset(&b, 0, sizeof(struct blob_buf)); blob_buf_init(&b, 0); - bb_add_string(&b, "interface", cwmp_main.conf.default_wan_iface); - int e = icwmp_ubus_invoke("network.interface", "status", b.head, ubus_network_interface_callback, NULL); + char ubus_obj[100] = {0}; + snprintf(ubus_obj, sizeof(ubus_obj), "network.interface.%s", cwmp_main.conf.default_wan_iface); + + FREE(cwmp_main.conf.interface); + + int e = icwmp_ubus_invoke(ubus_obj, "status", b.head, ubus_network_interface_callback, NULL); blob_buf_free(&b); if (e != 0) { - CWMP_LOG(INFO, "Get network interface from network.interface ubus method failed. Ubus err code: %d", e); + CWMP_LOG(INFO, "Get network interface from %s ubus method failed. Ubus err code: %d", ubus_obj, e); return -1; } if (cwmp_main.conf.interface == NULL) { - CWMP_LOG(INFO, "Not able to get the network interface from network.interface ubus method."); + CWMP_LOG(INFO, "Not able to get the network interface from %s ubus method.", ubus_obj); return -1; } return CWMP_OK; diff --git a/config.c b/config.c index 9722344..ceb1c2d 100755 --- a/config.c +++ b/config.c @@ -15,18 +15,153 @@ #include "config.h" #include "log.h" #include "reboot.h" +#include "ubus_utils.h" #include "ssl_utils.h" #include "datamodel_interface.h" #include "heartbeat.h" pthread_mutex_t mutex_config_load = PTHREAD_MUTEX_INITIALIZER; -static int check_global_config(struct config *conf) +void get_dhcp_vend_info_cb(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg) { - if (conf->acsurl == NULL) { - conf->acsurl = strdup(DEFAULT_ACSURL); + if (req == NULL || msg == NULL) + return; + + char **v_info = (char **)req->priv; + if (v_info == NULL) + return; + + struct blob_attr *param; + size_t rem; + + enum { + E_VENDOR_INFO, + __E_MAX + }; + + const struct blobmsg_policy p[__E_MAX] = { + { "vendorspecinf", BLOBMSG_TYPE_STRING }, + }; + + blobmsg_for_each_attr(param, msg, rem) { + if (strcmp(blobmsg_name(param), "data") == 0) { + struct blob_attr *tb[__E_MAX] = {NULL}; + if (blobmsg_parse(p, __E_MAX, tb, blobmsg_data(param), blobmsg_len(param)) != 0) { + return; + } + + if (tb[E_VENDOR_INFO]) { + char *info = blobmsg_get_string(tb[E_VENDOR_INFO]); + int len = strlen(info) + 1; + *v_info = (char *)malloc(len); + if (*v_info == NULL) + return; + + memset(*v_info, 0, len); + snprintf(*v_info, len, "%s", info); + } + + break; + } } - return CWMP_OK; + + return; +} + +bool configure_dhcp_options(char *vendspecinf) +{ + if (vendspecinf == NULL) { + CWMP_LOG(DEBUG, "No vendor specific info found"); + return false; + } + + // extract url from vendor info + int len = CWMP_STRLEN(vendspecinf) + 1; + char vend_info[len]; + memset(vend_info, 0, len); + snprintf(vend_info, len, "%s", vendspecinf); + + if (strncmp(vend_info, "http://", 7) == 0 || strncmp(vend_info, "https://", 8) == 0) { + uci_set_value_by_path(UCI_DHCP_ACS_URL, vend_info, UCI_STANDARD_CONFIG); + CWMP_LOG(DEBUG, "dhcp url: %s", vend_info); + cwmp_commit_package("cwmp", UCI_STANDARD_CONFIG); + return true; + + } + + bool update_uci = false; + char *temp = strtok(vend_info, " "); + while (temp) { + if (strncmp(temp, "1=", 2) == 0) { + char *pos = temp + 2; + if (CWMP_STRLEN(pos)) { + uci_set_value_by_path(UCI_DHCP_ACS_URL, pos, UCI_STANDARD_CONFIG); + CWMP_LOG(DEBUG, "dhcp url: %s", pos); + update_uci = true; + } + } + + if (strncmp(temp, "2=", 2) == 0) { + char *pos = temp + 2; + if (CWMP_STRLEN(pos)) { + uci_set_value_by_path(UCI_DHCP_CPE_PROV_CODE, pos, UCI_STANDARD_CONFIG); + update_uci = true; + } + } + + if (strncmp(temp, "3=", 2) == 0) { + char *pos = temp + 2; + if (CWMP_STRLEN(pos)) { + uci_set_value_by_path(UCI_DHCP_ACS_RETRY_MIN_WAIT_INTERVAL, pos, UCI_STANDARD_CONFIG); + update_uci = true; + } + } + + if (strncmp(temp, "4=", 2) == 0) { + char *pos = temp + 2; + if (CWMP_STRLEN(pos)) { + uci_set_value_by_path(UCI_DHCP_ACS_RETRY_INTERVAL_MULTIPLIER, pos, UCI_STANDARD_CONFIG); + update_uci = true; + } + } + + temp = strtok(NULL, " "); + } + + if (update_uci) + cwmp_commit_package("cwmp", UCI_STANDARD_CONFIG); + + return update_uci; +} + +static void get_dhcp_vendor_info(char *intf) +{ + if (intf == NULL) + return; + + char ubus_obj[100] = {0}; + snprintf(ubus_obj, sizeof(ubus_obj), "network.interface.%s", intf); + + struct blob_buf b; + memset(&b, 0, sizeof(struct blob_buf)); + blob_buf_init(&b, 0); + + for (int i = 0; i < DHCP_OPTION_READ_MAX_RETRY; i++) { + char *vendor_info = NULL; + if (icwmp_ubus_invoke(ubus_obj, "status", b.head, get_dhcp_vend_info_cb, &vendor_info) == 0) { + CWMP_LOG(DEBUG, "vendor info: %s", vendor_info); + if (configure_dhcp_options(vendor_info)) { + FREE(vendor_info); + break; + } + } + + FREE(vendor_info); + CWMP_LOG(INFO, "Failed to read dhcp acs url from ifstatus, retry after %d sec.", DHCP_OPTION_READ_INTERVAL); + sleep(DHCP_OPTION_READ_INTERVAL); + } + + blob_buf_free(&b); } int get_global_config(struct config *conf) @@ -90,22 +225,49 @@ int get_global_config(struct config *conf) return error; } + if ((error = uci_get_value(UCI_CPE_DEFAULT_WAN_IFACE, &value)) == CWMP_OK) { + FREE(conf->default_wan_iface); + if (value != NULL) { + conf->default_wan_iface = strdup(value); + FREE(value); + } else { + conf->default_wan_iface = strdup("wan"); + } + + CWMP_LOG(DEBUG, "CWMP CONFIG - default wan interface: %s", conf->default_wan_iface ? conf->default_wan_iface : ""); + } else { + return error; + } + error = uci_get_value(UCI_DHCP_DISCOVERY_PATH, &value); + + // now read the vendor info from ifstatus before reading the DHCP_ACS_URL from uci + if (error == CWMP_OK && value != NULL) { + if (strcmp(value, "enable") == 0 && conf->default_wan_iface != NULL) { + get_dhcp_vendor_info(conf->default_wan_iface); + } + } + error2 = uci_get_value(UCI_ACS_URL_PATH, &value2); error3 = uci_get_value(UCI_DHCP_ACS_URL, &value3); + FREE(conf->acsurl); if ((((error == CWMP_OK) && (value != NULL) && (strcmp(value, "enable") == 0)) || ((error2 == CWMP_OK) && ((value2 == NULL) || (value2[0] == 0)))) && ((error3 == CWMP_OK) && (value3 != NULL) && (value3[0] != 0))) { - FREE(conf->acsurl); conf->acsurl = strdup(value3); } else if ((error2 == CWMP_OK) && (value2 != NULL) && (value2[0] != 0)) { - FREE(conf->acsurl); conf->acsurl = strdup(value2); } + CWMP_LOG(DEBUG, "CWMP CONFIG - acs url: %s", conf->acsurl ? conf->acsurl : ""); FREE(value); FREE(value2); FREE(value3); + if (conf->acsurl == NULL) { + CWMP_LOG(ERROR, "ACS URL is Null"); + return -1; + } + if ((error = uci_get_value(UCI_ACS_USERID_PATH, &value)) == CWMP_OK) { if (value != NULL) { FREE(conf->acs_userid); @@ -304,20 +466,6 @@ int get_global_config(struct config *conf) return error; } - if ((error = uci_get_value(UCI_CPE_DEFAULT_WAN_IFACE, &value)) == CWMP_OK) { - FREE(conf->default_wan_iface); - if (value != NULL) { - conf->default_wan_iface = strdup(value); - FREE(value); - } else { - conf->default_wan_iface = strdup("wan"); - } - - CWMP_LOG(DEBUG, "CWMP CONFIG - default wan interface: %s", conf->default_wan_iface ? conf->default_wan_iface : ""); - } else { - return error; - } - if ((error = uci_get_value(UCI_CPE_CRPATH_PATH, &value)) == CWMP_OK) { FREE(conf->connection_request_path); if (value == NULL) @@ -651,11 +799,14 @@ int global_conf_init(struct cwmp *cwmp) if ((error = get_global_config(&(cwmp->conf)))) goto end; - if ((error = check_global_config(&(cwmp->conf)))) - goto end; + error = get_connection_interface(); + while (error != CWMP_OK && thread_end != true) { + usleep(500); + error = get_connection_interface(); + } - if ((error = get_connection_interface())) - return -1; + if (error != CWMP_OK) + goto end; /* Launch reboot methods if needed */ launch_reboot_methods(cwmp); diff --git a/cwmp.c b/cwmp.c index 5f5776e..4aa9153 100644 --- a/cwmp.c +++ b/cwmp.c @@ -268,7 +268,11 @@ int run_session_end_func(void) if (end_session_flag & END_SESSION_RELOAD) { CWMP_LOG(INFO, "Config reload: end session request"); cwmp_uci_reinit(); - cwmp_apply_acs_changes(); + if (cwmp_apply_acs_changes() != CWMP_OK) { + // calling exit to avoid race condition + CWMP_LOG(CRITIC, "terminating cwmp service"); + exit(0); + } check_trigger_heartbeat_session(); } @@ -767,8 +771,10 @@ static int cwmp_init(int argc, char **argv, struct cwmp *cwmp) return error; cwmp_uci_init(); - if ((error = global_conf_init(cwmp))) + if ((error = global_conf_init(cwmp))) { + cwmp_uci_exit(); return error; + } cwmp_get_deviceid(cwmp); load_forced_inform_json_file(cwmp); @@ -821,6 +827,8 @@ static void icwmp_signal_handler(int signal_num) if (signal_num == SIGINT || signal_num == SIGTERM) { thread_end = true; + CWMP_LOG(INFO, "Received signal %d", signal_num); + if (cwmp_main.session_status.last_status == SESSION_RUNNING) http_set_timeout(); @@ -833,6 +841,8 @@ static void icwmp_signal_handler(int signal_num) pthread_cond_signal(&threshold_schedule_download); pthread_cond_signal(&threshold_apply_schedule_download); pthread_cond_signal(&threshold_upload); + pthread_cond_signal(&threshold_heartbeat_session); + pthread_cond_signal(&threasheld_retry_session); shutdown(cwmp_main.cr_socket_desc, SHUT_RDWR); } @@ -883,16 +893,16 @@ int main(int argc, char **argv) sigaction(SIGINT, &act, 0); sigaction(SIGTERM, &act, 0); - error = pthread_create(&http_cr_server_thread, NULL, &thread_http_cr_server_listen, NULL); - if (error < 0) { - CWMP_LOG(ERROR, "Error when creating the http connection request server thread!"); - } - error = pthread_create(&ubus_thread, NULL, &thread_uloop_run, NULL); if (error < 0) { CWMP_LOG(ERROR, "Error when creating the ubus thread!"); } + error = pthread_create(&http_cr_server_thread, NULL, &thread_http_cr_server_listen, NULL); + if (error < 0) { + CWMP_LOG(ERROR, "Error when creating the http connection request server thread!"); + } + error = pthread_create(&periodic_event_thread, NULL, &thread_event_periodic, (void *)cwmp); if (error < 0) { CWMP_LOG(ERROR, "Error when creating the periodic event thread!"); @@ -902,6 +912,7 @@ int main(int argc, char **argv) if (error < 0) { CWMP_LOG(ERROR, "Error when creating the periodic check notify thread!"); } + error = pthread_create(&heart_beat_session_thread, NULL, &thread_heartbeat_session, (void *)cwmp); if (error < 0) { CWMP_LOG(ERROR, "Error when creating heartbeat session thread!"); diff --git a/gitlab-ci/functional-test.sh b/gitlab-ci/functional-test.sh index 79adad1..91fa4e2 100755 --- a/gitlab-ci/functional-test.sh +++ b/gitlab-ci/functional-test.sh @@ -21,9 +21,6 @@ supervisorctl status all echo "Configuring genieacs" configure_genieacs -echo "Configuring ACS URL" -configure_acs_url - mkdir -p /var/state/icwmpd echo "Starting icwmpd deamon" diff --git a/gitlab-ci/setup.sh b/gitlab-ci/setup.sh index 3f46e2d..4d0482f 100755 --- a/gitlab-ci/setup.sh +++ b/gitlab-ci/setup.sh @@ -13,6 +13,12 @@ rm -rf /etc/supervisor/conf.d/*.conf cp ./gitlab-ci/iopsys-supervisord.conf /etc/supervisor/conf.d/ cp -rf ./test/files/* / +echo "set acs url in cwmp uci" +url="http://$(hostname -i):7547" +uci set cwmp.acs.url=$url +uci commit cwmp +echo "Current ACS URL=$url" + # copy schema for validation test cp -r ./schemas/ubus/*.json /usr/share/rpcd/schemas/ diff --git a/gitlab-ci/shared.sh b/gitlab-ci/shared.sh index 8d62d1f..f8999e2 100644 --- a/gitlab-ci/shared.sh +++ b/gitlab-ci/shared.sh @@ -74,18 +74,23 @@ function configure_download_firmware() echo "Invalid" > /tmp/firmware/invalid_firmware_v1.0.bin } -function configure_acs_url() -{ - url="http://$(hostname -i):7547" - uci set cwmp.acs.url=$url - uci commit cwmp - echo "Current ACS URL=$url" -} - function check_cwmp_status() { - status=`ubus call tr069 status | jq -r ".cwmp.status"` - if [ $status != "up" ]; then + iter=0 + state=0 + while [ $iter -lt 10 ] + do + status=`ubus call tr069 status | jq -r ".cwmp.status"` + if [ $status == "up" ]; then + state=1 + break + fi + + iter=$(( $iter + 1)) + sleep 2 + done + + if [ $state -eq 0 ]; then echo "icwmpd is not started correctly, (the current status=$status)" exit 1 fi diff --git a/heartbeat.c b/heartbeat.c index 3b5acf6..e5c6e56 100644 --- a/heartbeat.c +++ b/heartbeat.c @@ -51,6 +51,9 @@ void *thread_heartbeat_session(void *v __attribute__((unused))) sleep(2); for (;;) { + if (thread_end) + break; + if (cwmp_main.conf.heart_beat_enable) { heartbeat_interval.tv_sec = time(NULL) + cwmp_main.conf.heartbeat_interval; pthread_mutex_lock(&mutex_heartbeat); @@ -64,6 +67,9 @@ void *thread_heartbeat_session(void *v __attribute__((unused))) //continue; } + if (thread_end) + break; + pthread_mutex_lock(&mutex_heartbeat_session); struct session *heartbeat_session = NULL; heartbeat_session = calloc(1, sizeof(struct session)); @@ -89,6 +95,7 @@ void *thread_heartbeat_session(void *v __attribute__((unused))) pthread_mutex_unlock(&mutex_heartbeat); continue; } + cwmp_uci_init(); if (heart_beat_session_status.last_status == SESSION_FAILURE) reload_networking_config(); @@ -111,7 +118,8 @@ void *thread_heartbeat_session(void *v __attribute__((unused))) cwmp_uci_exit(); pthread_mutex_unlock(&(cwmp_main.mutex_session_send)); pthread_mutex_unlock(&mutex_heartbeat); - break; + // Exiting to avoid race conditions + exit(0); } if (error || heartbeat_session->error == CWMP_RETRY_SESSION) { @@ -141,9 +149,6 @@ void *thread_heartbeat_session(void *v __attribute__((unused))) cwmp_uci_exit(); pthread_mutex_unlock(&mutex_heartbeat_session); pthread_mutex_unlock(&mutex_heartbeat); - - if (thread_end) - break; } else { pthread_mutex_lock(&mutex_heartbeat); pthread_cond_wait(&threshold_heartbeat_session, &mutex_heartbeat); diff --git a/inc/cwmp_uci.h b/inc/cwmp_uci.h index 1d59f54..35dbd69 100644 --- a/inc/cwmp_uci.h +++ b/inc/cwmp_uci.h @@ -61,6 +61,9 @@ #define UCI_ACS_HEARTBEAT_ENABLE "cwmp.acs.heartbeat_enable" #define UCI_ACS_HEARTBEAT_INTERVAL "cwmp.acs.heartbeat_interval" #define UCI_ACS_HEARTBEAT_TIME "cwmp.acs.heartbeat_time" +#define UCI_DHCP_CPE_PROV_CODE "cwmp.cpe.dhcp_provisioning_code" +#define UCI_DHCP_ACS_RETRY_MIN_WAIT_INTERVAL "cwmp.acs.dhcp_retry_min_wait_interval" +#define UCI_DHCP_ACS_RETRY_INTERVAL_MULTIPLIER "cwmp.acs.dhcp_retry_interval_multiplier" #define UCI_CPE_FIREWALL_RESTART_STATE "cwmp.cpe.firewall_restart" @@ -68,6 +71,8 @@ #define LIB_DB_CONFIG "/lib/db/config" #define ETC_DB_CONFIG "/etc/board-db/config" #define VARSTATE_CONFIG "/var/state" +#define DHCP_OPTION_READ_MAX_RETRY 5 +#define DHCP_OPTION_READ_INTERVAL 5 #define section_name(s) s ? (s)->e.name : "" typedef enum uci_config_paths diff --git a/test/files/tmp/network.interface.wan.data b/test/files/tmp/network.interface.wan.data new file mode 100644 index 0000000..bebb987 --- /dev/null +++ b/test/files/tmp/network.interface.wan.data @@ -0,0 +1,14 @@ +{ + "up": true, + "pending": false, + "available": true, + "autostart": true, + "dynamic": false, + "uptime": 0, + "l3_device": "eth0", + "proto": "dhcp", + "device": "eth0", + "data": { + + } +} diff --git a/test/files/usr/libexec/rpcd/network.interface.wan b/test/files/usr/libexec/rpcd/network.interface.wan new file mode 100755 index 0000000..5de00dd --- /dev/null +++ b/test/files/usr/libexec/rpcd/network.interface.wan @@ -0,0 +1,17 @@ +#!/bin/sh + +. /usr/share/libubox/jshn.sh + +case "$1" in + list) + echo '{ "status" : {} }' + ;; + call) + case "$2" in + status) + cat /tmp/network.interface.wan.data 2>/dev/null + ;; + esac + ;; +esac + diff --git a/ubus_utils.c b/ubus_utils.c index b22069e..58c2899 100644 --- a/ubus_utils.c +++ b/ubus_utils.c @@ -40,7 +40,11 @@ static int reload_cmd(struct blob_buf *b) } else { pthread_mutex_lock(&(cwmp_main.mutex_session_queue)); cwmp_uci_reinit(); - cwmp_apply_acs_changes(); + if (cwmp_apply_acs_changes() != CWMP_OK) { + // Exiting to avoid any race condition + CWMP_LOG(CRITIC, "cwmp service terminating"); + exit(0); + } pthread_mutex_unlock(&(cwmp_main.mutex_session_queue)); blobmsg_add_u32(b, "status", 0); blobmsg_add_string(b, "info", "icwmpd config reloaded");