/* * notifications.c - Manage CWMP Notifications * * Copyright (C) 2022, IOPSYS Software Solutions AB. * * Author Omar Kallel * * See LICENSE file for license related information. * */ #include #include #include #include "notifications.h" #include "uci_utils.h" #include "datamodel_interface.h" #include "ssl_utils.h" #include "log.h" #include "event.h" #include "xml.h" #include "cwmp_event.h" #define UPSTREAM_STABILITY_CHECK_TIMESPAN 5 // In seconds #define MANAGEABLE_DEVICES_NBRE "Device.ManagementServer.ManageableDeviceNumberOfEntries" LIST_HEAD(list_value_change); LIST_HEAD(list_lw_value_change); LIST_HEAD(list_param_obj_notify); static int cr_url_retry = 3; static void update_notify_file_line(FILE *notify_file, char *param_name, char *param_type, char *param_value, int notification); static void create_list_param_leaf_notify(struct list_head *, void (*fp)(FILE *, char *, char *, char *, int), FILE*); static void send_active_value_change(void); static void add_list_value_change(const char *param_name, const char *param_data, const char *param_type); static void add_lw_list_value_change(char *param_name, char *param_data, char *param_type); static void cwmp_lwnotification(void); static void periodic_check_notifiy(struct uloop_timeout *timeout __attribute__((unused))); struct uloop_timeout check_notify_timer = { .cb = periodic_check_notifiy }; char *supported_notification_types[7] = {"disabled" , "passive", "active", "passive_lw", "passive_passive_lw", "active_lw", "passive_active_lw"}; char *default_active_notifications_parameters[] = { "Device.ManagementServer.ConnectionRequestURL", "Device.ManagementServer.ConnReqJabberID", "Device.GatewayInfo.ManufacturerOUI", "Device.GatewayInfo.ProductClass", "Device.GatewayInfo.SerialNumber", "Device.SoftwareModules.ExecutionUnit.*.Status", }; char *forced_notifications_parameters[] = { "Device.DeviceInfo.SoftwareVersion", "Device.DeviceInfo.ProvisioningCode" }; /* * Common functions */ static bool parameter_is_subobject_of_parameter(const char *parent, const char *child) { if (child == NULL) { CWMP_LOG(WARNING, "notifications %s: child is null", __FUNCTION__); return false; } if (parent == NULL) parent = "Device."; if (strcmp(parent, child) == 0) return false; if (strncmp(parent, child, strlen(parent)) == 0) return true; return false; } int check_parameter_forced_notification(const char *parameter) { int i; if (parameter == NULL) { CWMP_LOG(WARNING, "notifications %s: parameter is null", __FUNCTION__); return 0; } for (i = 0; i < (int)ARRAY_SIZE(forced_notifications_parameters); i++) { if (strcmp(forced_notifications_parameters[i], parameter) == 0) return 2; } return 0; } static char *check_valid_parameter_path(const char *parameter_name) { char *error = NULL; LIST_HEAD(parameters_list); /*check if parameter name is valid parameter path*/ error = cwmp_validate_parameter_name(parameter_name, false, ¶meters_list); if (error && CWMP_STRCMP(error, "9003") == 0) error = cwmp_get_parameter_values(parameter_name, ¶meters_list); cwmp_free_all_dm_parameter_list(¶meters_list); return error; } /* * SetParameterAttributes */ // Add parameter_name to the suitable notifications list static int add_uci_option_notification(const char *parameter_name, int type) { char notif_path[BUF_SIZE_256] = {0}; int ret; set_uci_path_value(ICWMPD_CONFIG, "cwmp_notifications.notifications", "notifications"); snprintf(notif_path, BUF_SIZE_256, "cwmp_notifications.notifications.%s", supported_notification_types[type]); ret = set_uci_list_value(ICWMPD_CONFIG, notif_path, parameter_name); if (ret != UCI_OK) return -1; return ret; } static bool check_parent_with_different_notification(const char *parameter_name, int notification) { int i; bool ret = false; for (i = 0; i < 7; i++) { char notif_path[BUF_SIZE_256] = {0}; struct cwmp_dm_parameter *param_iter = NULL; LIST_HEAD(local_notify_list); snprintf(notif_path, BUF_SIZE_256, "cwmp_notifications.notifications.%s", supported_notification_types[i]); get_uci_dm_list(ICWMPD_CONFIG, notif_path, &local_notify_list, i); list_for_each_entry(param_iter, &local_notify_list, list) { if (CWMP_STRLEN(param_iter->name) == 0) continue; if (parameter_is_subobject_of_parameter(param_iter->name, parameter_name)) { ret = true; break; } } cwmp_free_all_dm_parameter_list(&local_notify_list); if (ret == true) { break; } } return ret; } static bool update_notifications_list(const char *parameter_name, int notification) { int i; bool update_ret = true; if (parameter_name == NULL) parameter_name = "Device."; // Parse all possible lists of notifications one by one for (i = 0; i < 7; i++) { char notif_path[BUF_SIZE_256] = {0}; struct cwmp_dm_parameter *param_iter = NULL; LIST_HEAD(local_notify_list); snprintf(notif_path, BUF_SIZE_256, "cwmp_notifications.notifications.%s", supported_notification_types[i]); get_uci_dm_list(ICWMPD_CONFIG, notif_path, &local_notify_list, i); list_for_each_entry(param_iter, &local_notify_list, list) { if (CWMP_STRLEN(param_iter->name) == 0) continue; if ((CWMP_STRCMP(parameter_name, param_iter->name) == 0 && (i != notification)) || parameter_is_subobject_of_parameter(parameter_name, param_iter->name)) del_uci_list_value(ICWMPD_CONFIG, notif_path, param_iter->name); if ((CWMP_STRCMP(parameter_name, param_iter->name) == 0 || parameter_is_subobject_of_parameter(param_iter->name, parameter_name) ) && (i == notification)) update_ret = false; } cwmp_free_all_dm_parameter_list(&local_notify_list); } if (update_ret && notification == 0 && !check_parent_with_different_notification(parameter_name, 0)) update_ret = false; return update_ret; } char *cwmp_set_parameter_attributes(const char *parameter_name, int notification) { char *error = NULL; if (parameter_name == NULL) parameter_name = "Device."; /*Check if the parameter name is present in TR-181 datamodel*/ error = check_valid_parameter_path(parameter_name); if (error != NULL) return error; /*Mustn't set notifications for forced notifications parameter*/ if (check_parameter_forced_notification(parameter_name)) return "9009"; /*checks if the notifications lists need to be updated*/ if (update_notifications_list(parameter_name, notification) == true) add_uci_option_notification(parameter_name, notification); return NULL; } int cwmp_set_parameter_attributes_list(struct list_head *parameters_list) { struct cwmp_dm_parameter *param_iter = NULL; int ret = CWMP_OK; list_for_each_entry (param_iter, parameters_list, list) { char *parameter_name = param_iter->name; int notif_type = param_iter->notification; char *error = NULL; error = cwmp_set_parameter_attributes(parameter_name, notif_type); if (error != NULL) { CWMP_LOG(ERROR, "Invalid/forced parameter %s, skipped %s", parameter_name, error); continue; } } return ret; } /* * GetPrameterAttributes */ static int get_parameter_family_notifications(const char *parameter_name, struct list_head *childs_notifications) { int i, notif_ret = 0; if (parameter_name == NULL) parameter_name = "Device."; for (i = 0; i < 7; i++) { char *parent_param = NULL; char notif_path[BUF_SIZE_256] = {0}; LIST_HEAD(local_notify_list); struct cwmp_dm_parameter *param_iter = NULL; snprintf(notif_path, BUF_SIZE_256, "cwmp_notifications.notifications.%s", supported_notification_types[i]); get_uci_dm_list(ICWMPD_CONFIG, notif_path, &local_notify_list, i); list_for_each_entry(param_iter, &local_notify_list, list) { if (CWMP_STRLEN(param_iter->name) == 0) continue; if (parameter_is_subobject_of_parameter(parameter_name, param_iter->name)) { add_dm_parameter_to_list(childs_notifications, param_iter->name, "", "", i, false); } // cppcheck-suppress knownConditionTrueFalse if (parameter_is_subobject_of_parameter(param_iter->name, parameter_name) && (parent_param == NULL || parameter_is_subobject_of_parameter(parent_param, param_iter->name))) { parent_param = CWMP_STRDUP(param_iter->name); notif_ret = i; } if (CWMP_STRCMP(parameter_name, param_iter->name) == 0) notif_ret = i; } FREE(parent_param); cwmp_free_all_dm_parameter_list(&local_notify_list); } return notif_ret; } int get_parameter_leaf_notification_from_childs_list(char *parameter_name, struct list_head *childs_list) { char *parent = NULL; int ret_notif = -1; struct cwmp_dm_parameter *param_value = NULL; if (childs_list == NULL) return -1; list_for_each_entry (param_value, childs_list, list) { if (CWMP_STRCMP(param_value->name, parameter_name) == 0) { ret_notif = param_value->notification; break; } if (parameter_is_subobject_of_parameter(param_value->name, parameter_name) && ( parent == NULL || parameter_is_subobject_of_parameter(parent, param_value->name))) { parent = param_value->name; ret_notif = param_value->notification; } } return ret_notif; } char *cwmp_get_parameter_attributes(const char *parameter_name, struct list_head *parameters_list) { char *error = NULL; if (parameter_name == NULL || parameters_list == NULL) { CWMP_LOG(ERROR, "notifications %s: childs_list is null", __FUNCTION__); return NULL; } error = check_valid_parameter_path(parameter_name); if (error != NULL) return error; LIST_HEAD(childs_notifs); int notification = get_parameter_family_notifications(parameter_name, &childs_notifs); LIST_HEAD(params_list); error = cwmp_get_parameter_values(parameter_name, ¶ms_list); if (error != NULL) { cwmp_free_all_dm_parameter_list(&childs_notifs); return error; } struct cwmp_dm_parameter *param_value = NULL; list_for_each_entry (param_value, ¶ms_list, list) { int notif_leaf; notif_leaf = check_parameter_forced_notification(param_value->name); if (notif_leaf > 0) { add_dm_parameter_to_list(parameters_list, param_value->name, "", "", notif_leaf, false); continue; } notif_leaf = get_parameter_leaf_notification_from_childs_list(param_value->name, &childs_notifs); if (notif_leaf == -1) { //param_value is not among childs_notifs add_dm_parameter_to_list(parameters_list, param_value->name, "", "", notification, false); } else { //param_value is among childs_notifs add_dm_parameter_to_list(parameters_list, param_value->name, "", "", notif_leaf, false); } } cwmp_free_all_dm_parameter_list(&childs_notifs); cwmp_free_all_dm_parameter_list(¶ms_list); return NULL; } /* * Update notify file */ static bool parameter_is_other_notif_object_child(const char *parent, const char *parameter) { struct list_head list_iter, *list_ptr; list_iter.next = list_param_obj_notify.next; list_iter.prev = list_param_obj_notify.prev; if (parent == NULL) parent = "Device."; if (parameter == NULL) parameter = "Device."; while (list_iter.prev != &list_param_obj_notify) { struct cwmp_dm_parameter *dm_parameter; if (list_iter.prev == NULL) continue; dm_parameter = list_entry(list_iter.prev, struct cwmp_dm_parameter, list); list_ptr = list_iter.prev; list_iter.prev = list_ptr->prev; list_iter.next = list_ptr->next; if (dm_parameter->name == NULL) continue; if (CWMP_STRCMP(parent, dm_parameter->name) == 0) continue; if (CWMP_STRNCMP(parent, dm_parameter->name, strlen(parent)) == 0 && CWMP_STRNCMP(parameter, dm_parameter->name, strlen(dm_parameter->name)) == 0) return true; } return false; } char* update_list_param_leaf_notify_with_sub_parameter_list(struct list_head *list_param_leaf_notify, char* parent_parameter, int parent_notification, bool parent_forced_notif, void (*update_notify_file_line_arg)(FILE *notify_file, char *param_name, char *param_type, char *param_value, int notification), FILE* notify_file_arg) { struct cwmp_dm_parameter *param_iter = NULL; LIST_HEAD(params_list); char *err = cwmp_get_parameter_values(parent_parameter, ¶ms_list); if (err) return err; list_for_each_entry (param_iter, ¶ms_list, list) { if (parent_forced_notif || (!parameter_is_other_notif_object_child(parent_parameter, param_iter->name) && !check_parameter_forced_notification(param_iter->name))) { if (list_param_leaf_notify != NULL) add_dm_parameter_to_list(list_param_leaf_notify, param_iter->name, param_iter->value, "", parent_notification, false); if (notify_file_arg != NULL && update_notify_file_line_arg != NULL) update_notify_file_line_arg(notify_file_arg, param_iter->name, param_iter->type, param_iter->value, parent_notification); } } cwmp_free_all_dm_parameter_list(¶ms_list); return NULL; } void create_list_param_leaf_notify(struct list_head *list_param_leaf_notify, void (*update_notify_file_line_arg)(FILE *notify_file, char *param_name, char *param_type, char *param_value, int notification), FILE* notify_file_arg) { struct cwmp_dm_parameter *param_iter = NULL; int i; for (i = 0; i < (int)ARRAY_SIZE(forced_notifications_parameters); i++) update_list_param_leaf_notify_with_sub_parameter_list(list_param_leaf_notify, forced_notifications_parameters[i], 2, true, update_notify_file_line_arg, notify_file_arg); list_for_each_entry (param_iter, &list_param_obj_notify, list) { if (param_iter->notification == 0) continue; update_list_param_leaf_notify_with_sub_parameter_list(list_param_leaf_notify, param_iter->name, param_iter->notification, false, update_notify_file_line_arg, notify_file_arg); } } static void load_notify_values(struct list_head *notify_list) { if (notify_list == NULL) return; // cppcheck-suppress cert-MSC24-C FILE *fp = fopen(DM_ENABLED_NOTIFY, "r"); if (fp == NULL) return; char line[2048] = {0}; while (fgets(line, sizeof(line), fp)) { const char *p_name = NULL, *p_val = NULL, *p_type = NULL; int p_notif = 0; json_object *jobj = json_tokener_parse(line); if (jobj == NULL) continue; json_object *p_obj = json_object_object_get(jobj, "parameter"); json_object *v_obj = json_object_object_get(jobj, "value"); json_object *n_obj = json_object_object_get(jobj, "notification"); json_object *t_obj = json_object_object_get(jobj, "type"); if (p_obj == NULL || v_obj == NULL || n_obj == NULL || t_obj == NULL) { json_object_put(jobj); jobj = NULL; continue; } p_name = json_object_get_string(p_obj); p_val = json_object_get_string(v_obj); p_notif = json_object_get_int(n_obj); p_type = json_object_get_string(t_obj); add_dm_parameter_to_list(notify_list, p_name, p_val, p_type, p_notif, 0); json_object_put(jobj); jobj = NULL; } fclose(fp); } static void apply_notify_values(struct list_head *notify_list) { if (notify_list == NULL) return; if (list_empty(notify_list)) return; // cppcheck-suppress cert-MSC24-C FILE *fp = fopen(DM_ENABLED_NOTIFY, "w"); if (fp == NULL) return; struct cwmp_dm_parameter *notif_value = NULL; list_for_each_entry(notif_value, notify_list, list) { update_notify_file_line(fp, notif_value->name, notif_value->type, notif_value->value, notif_value->notification); } fclose(fp); } void cwmp_update_notify_values(struct list_head *parameter_values_list) { struct cwmp_dm_parameter *param_value = NULL, *notif_value = NULL; bool value_changed = false; LIST_HEAD(notify_list); load_notify_values(¬ify_list); if (list_empty(¬ify_list)) return; list_for_each_entry(param_value, parameter_values_list, list) { if (CWMP_STRLEN(param_value->name) == 0) continue; char *inst_path = NULL; if (CWMP_OK != instantiate_param_name(param_value->name, &inst_path)) continue; list_for_each_entry(notif_value, ¬ify_list, list) { if (CWMP_STRCMP(inst_path, notif_value->name) == 0) { if (CWMP_STRCMP(param_value->value, notif_value->value) != 0) { FREE(notif_value->value); notif_value->value = strdup(param_value->value); value_changed = true; } break; } } FREE(inst_path); } if (value_changed == true) { apply_notify_values(¬ify_list); } cwmp_free_all_dm_parameter_list(¬ify_list); } void init_list_param_notify() { int i; for (i = 0; i < 7; i++) { char notif_path[BUF_SIZE_256] = {0}; snprintf(notif_path, BUF_SIZE_256, "cwmp_notifications.notifications.%s", supported_notification_types[i]); get_uci_dm_list(ICWMPD_CONFIG, notif_path, &list_param_obj_notify, i); } } void clean_list_param_notify() { cwmp_free_all_dm_parameter_list(&list_param_obj_notify); } void reinit_list_param_notify() { clean_list_param_notify(); init_list_param_notify(); } void update_notify_file_line(FILE *notify_file, char *param_name, char *param_type, char *param_value, int notification) { if (notify_file == NULL) return; if (param_name == NULL) return; struct blob_buf bbuf; CWMP_MEMSET(&bbuf, 0, sizeof(struct blob_buf)); blob_buf_init(&bbuf, 0); blobmsg_add_string(&bbuf, "parameter", param_name); blobmsg_add_u32(&bbuf, "notification", notification); blobmsg_add_string(&bbuf, "type", param_type ? param_type : "xsd:string"); blobmsg_add_string(&bbuf, "value", param_value ? param_value : ""); char *notification_line = blobmsg_format_json(bbuf.head, true); if (notification_line != NULL) { fprintf(notify_file, "%s\n", notification_line); FREE(notification_line); } blob_buf_free(&bbuf); } void cwmp_update_enabled_notify_file(void) { FILE *fp = NULL; LIST_HEAD(list_notify_params); remove(DM_ENABLED_NOTIFY); // cppcheck-suppress cert-MSC24-C fp = fopen(DM_ENABLED_NOTIFY, "a"); if (fp == NULL) return; create_list_param_leaf_notify(NULL, update_notify_file_line, fp); fclose(fp); } /* * Load custom notify json file */ void load_custom_notify_json(void) { struct blob_buf bbuf = {0}; struct blob_attr *cur = NULL; struct blob_attr *custom_notify_list = NULL; int rem = 0; cwmp_ctx.custom_notify_active = false; if (!file_exists(cwmp_ctx.conf.custom_notify_json)) return; // Check for custom notification success import marker if (file_exists(NOTIFY_MARKER) == true) return; CWMP_MEMSET(&bbuf, 0, sizeof(struct blob_buf)); blob_buf_init(&bbuf, 0); // Create success marker in temp area, so that it can be in sync with backup script if (blobmsg_add_json_from_file(&bbuf, cwmp_ctx.conf.custom_notify_json) == false) { CWMP_LOG(WARNING, "The file %s is not a valid JSON file", cwmp_ctx.conf.custom_notify_json); blob_buf_free(&bbuf); creat(RUN_NOTIFY_MARKER, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); return; } struct blob_attr *tb_notif[1] = {0}; const struct blobmsg_policy p_notif[1] = { { "custom_notification", BLOBMSG_TYPE_ARRAY } }; blobmsg_parse(p_notif, 1, tb_notif, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); if (tb_notif[0] == NULL) { CWMP_LOG(WARNING, "The JSON file %s doesn't contain a notify parameters list", cwmp_ctx.conf.custom_notify_json); blob_buf_free(&bbuf); creat(RUN_NOTIFY_MARKER, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); return; } custom_notify_list = tb_notif[0]; const struct blobmsg_policy p[2] = { { "parameter", BLOBMSG_TYPE_STRING }, { "notify_type", BLOBMSG_TYPE_STRING } }; LIST_HEAD(notification_list_head); blobmsg_for_each_attr(cur, custom_notify_list, rem) { struct blob_attr *tb[2] = { 0, 0 }; blobmsg_parse(p, 2, tb, blobmsg_data(cur), blobmsg_len(cur)); if (!tb[0] || !tb[1]) continue; if (!icwmp_validate_int_in_range(blobmsg_get_string(tb[1]), 0, 6)) { CWMP_LOG(WARNING, "Wrong notification value: %s", blobmsg_get_string(tb[1])); continue; } add_dm_parameter_to_list(¬ification_list_head, blobmsg_get_string(tb[0]), "", "", (int)strtol(blobmsg_get_string(tb[1]), NULL, 10), false); } blob_buf_free(&bbuf); cwmp_set_parameter_attributes_list(¬ification_list_head); cwmp_free_all_dm_parameter_list(¬ification_list_head); creat(RUN_NOTIFY_MARKER, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); cwmp_ctx.custom_notify_active = true; } void set_default_forced_active_parameters_notifications() { LIST_HEAD(forced_list_head); for (size_t i = 0; i < ARRAY_SIZE(default_active_notifications_parameters); i++) { add_dm_parameter_to_list(&forced_list_head, default_active_notifications_parameters[i], "", "", 2, false); } cwmp_set_parameter_attributes_list(&forced_list_head); cwmp_free_all_dm_parameter_list(&forced_list_head); } /* * Check value change */ static void get_parameter_value_from_parameters_list(struct list_head *params_list, const char *parameter_name, char **value, char **type) { struct cwmp_dm_parameter *param_value = NULL; if (params_list == NULL) { CWMP_LOG(ERROR, "notifications %s: params_list is null", __FUNCTION__); return; } if (parameter_name == NULL) parameter_name = "Device."; list_for_each_entry (param_value, params_list, list) { if (param_value->name == NULL) continue; if (strcmp(parameter_name, param_value->name) != 0) continue; *value = strdup(param_value->value ? param_value->value : ""); *type = strdup(param_value->type ? param_value->type : ""); } } int check_value_change(void) { FILE *fp; char buf[1280]; char *dm_value = NULL, *dm_type = NULL; int notif_ret = 0; struct blob_buf bbuf; char *parameter = NULL, *value = NULL; int notification = 0; // cppcheck-suppress cert-MSC24-C fp = fopen(DM_ENABLED_NOTIFY, "r"); if (fp == NULL) return notif_ret; LIST_HEAD(list_notify_params); create_list_param_leaf_notify(&list_notify_params, NULL, NULL); while (fgets(buf, 1280, fp) != NULL) { int len = strlen(buf); if (len) buf[len - 1] = '\0'; CWMP_MEMSET(&bbuf, 0, sizeof(struct blob_buf)); blob_buf_init(&bbuf, 0); if (blobmsg_add_json_from_string(&bbuf, buf) == false) { blob_buf_free(&bbuf); continue; } const struct blobmsg_policy p[4] = { { "parameter", BLOBMSG_TYPE_STRING }, { "notification", BLOBMSG_TYPE_INT32 }, { "type", BLOBMSG_TYPE_STRING }, { "value", BLOBMSG_TYPE_STRING } }; struct blob_attr *tb[4] = { NULL, NULL, NULL, NULL }; blobmsg_parse(p, 4, tb, blobmsg_data(bbuf.head), blobmsg_len(bbuf.head)); parameter = blobmsg_get_string(tb[0]); notification = blobmsg_get_u32(tb[1]); //type = blobmsg_get_string(tb[2]); value = blobmsg_get_string(tb[3]); get_parameter_value_from_parameters_list(&list_notify_params, parameter, &dm_value, &dm_type); if (dm_value == NULL && dm_type == NULL){ blob_buf_free(&bbuf); parameter = NULL; notification = 0; //type = NULL; value = NULL; continue; } if ((notification >= 1) && (dm_value != NULL) && value && (strcmp(dm_value, value) != 0)) { if (cwmp_ctx.conf.md_notif_limit > 0 && CWMP_STRCMP(parameter, MANAGEABLE_DEVICES_NBRE) == 0 && notification == 2) { unsigned int time_from_last_vc = time(NULL) - cwmp_ctx.md_value_change_last_time; if ((cwmp_ctx.md_value_change_last_time <= 0) || (time_from_last_vc >= cwmp_ctx.conf.md_notif_limit)) { cwmp_ctx.md_value_change_last_time = time(NULL); add_list_value_change(MANAGEABLE_DEVICES_NBRE, dm_value, dm_type); notif_ret |= NOTIF_ACTIVE; } } else if (notification == 1 || notification == 2) { add_list_value_change(parameter, dm_value, dm_type); if (notification == 1) notif_ret |= NOTIF_PASSIVE; if (notification == 2) notif_ret |= NOTIF_ACTIVE; } else { add_lw_list_value_change(parameter, dm_value, dm_type); notif_ret |= NOTIF_LW_ACTIVE; } } FREE(dm_value); FREE(dm_type); parameter = NULL; notification = 0; //type = NULL; value = NULL; blob_buf_free(&bbuf); } fclose(fp); cwmp_free_all_dm_parameter_list(&list_notify_params); return notif_ret; } void cwmp_prepare_value_change(void) { struct event_container *event_container; if (list_value_change.next == &(list_value_change)) return; event_container = cwmp_add_event_container(EVENT_IDX_4VALUE_CHANGE, ""); if (!event_container) return; list_splice_init(&(list_value_change), &(event_container->head_dm_parameter)); cwmp_save_event_container(event_container); } void sotfware_version_value_change(struct transfer_complete *p) { char *current_software_version = NULL; if (p == NULL) { CWMP_LOG(ERROR, "notifications %s: p is null", __FUNCTION__); return; } if (!p->old_software_version || p->old_software_version[0] == 0) return; current_software_version = cwmp_ctx.deviceid.softwareversion; if (p->old_software_version && current_software_version && strcmp(p->old_software_version, current_software_version) != 0) cwmp_add_event_container(EVENT_IDX_4VALUE_CHANGE, ""); } void periodic_check_notifiy(struct uloop_timeout *timeout __attribute__((unused))) { int is_notify = 0; if (cwmp_stop) return; /* If ConnectionRequestURL is empty then reschedule the timer after 5 second for * maximum of 3 times to check the upstream connection is stable, before enqueing * for notification. This can be a case of DHCP lease renewal phase for e.g * renew of old address is NACKED by the server (In this case interface releases its * current IP and waits for a new IP from server) */ // An empty connection url cause CDR test to break if (cr_url_retry) { struct cwmp_dm_parameter cwmp_dm_param = {0}; if (!cwmp_get_parameter_value("Device.ManagementServer.ConnectionRequestURL", &cwmp_dm_param)) { uloop_timeout_set(&check_notify_timer, UPSTREAM_STABILITY_CHECK_TIMESPAN * 1000); cr_url_retry = cr_url_retry - 1; return; } if (CWMP_STRLEN(cwmp_dm_param.value) == 0) { uloop_timeout_set(&check_notify_timer, UPSTREAM_STABILITY_CHECK_TIMESPAN * 1000); cr_url_retry = cr_url_retry - 1; return; } } // restore the retry count cr_url_retry = 3; is_notify = check_value_change(); if (is_notify > 0) cwmp_update_enabled_notify_file(); if (is_notify & NOTIF_ACTIVE) { send_active_value_change(); int last_session_interval = time(NULL) - cwmp_ctx.session->session_status.last_end_time; if (!cwmp_ctx.throttle_session_triggered && (cwmp_ctx.session->session_status.last_status == SESSION_SUCCESS) && (cwmp_ctx.conf.active_notif_throttle > 0)) { cwmp_ctx.throttle_session_triggered = true; if (last_session_interval < cwmp_ctx.conf.active_notif_throttle) trigger_cwmp_throttle_session_timer(cwmp_ctx.conf.active_notif_throttle - last_session_interval); else trigger_cwmp_throttle_session_timer(0); } else if (cwmp_ctx.conf.active_notif_throttle == 0) trigger_cwmp_session_timer(); } if (is_notify & NOTIF_LW_ACTIVE) cwmp_lwnotification(); uloop_timeout_set(&check_notify_timer, cwmp_ctx.conf.periodic_notify_interval * 1000); } void trigger_periodic_notify_check() { uloop_timeout_set(&check_notify_timer, 10); } void add_list_value_change(const char *param_name, const char *param_data, const char *param_type) { add_dm_parameter_to_list(&list_value_change, param_name, param_data, param_type, 0, false); } void clean_list_value_change() { cwmp_free_all_dm_parameter_list(&list_value_change); } void send_active_value_change(void) { struct event_container *event_container; event_container = cwmp_add_event_container(EVENT_IDX_4VALUE_CHANGE, ""); if (event_container == NULL) return; cwmp_save_event_container(event_container); return; } /* * Light Weight Notifications */ void add_lw_list_value_change(char *param_name, char *param_data, char *param_type) { add_dm_parameter_to_list(&list_lw_value_change, param_name, param_data, param_type, 0, false); } static void udplw_server_param(struct addrinfo **res) { struct addrinfo hints = { 0 }; struct config *conf = &(cwmp_ctx.conf); char port[32]; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; snprintf(port, sizeof(port), "%d", conf->lwn_port); getaddrinfo(conf->lwn_hostname, port, &hints, res); } char *calculate_lwnotification_cnonce() { char *cnonce = calloc(33, sizeof(char)); if (cnonce == NULL) return NULL; char *rand = generate_random_string(16); if (rand == NULL) { free(cnonce); return NULL; } snprintf(cnonce, 33, "%s", rand); free(rand); return cnonce; } static void send_udp_message(struct addrinfo *servaddr, char *msg) { int fd; if (msg == NULL) { CWMP_LOG(ERROR, "notifications %s: msg is null", __FUNCTION__); return; } fd = socket(servaddr->ai_family, SOCK_DGRAM, 0); if (fd >= 0) { sendto(fd, msg, strlen(msg), 0, servaddr->ai_addr, servaddr->ai_addrlen); close(fd); } } void del_list_lw_notify(struct cwmp_dm_parameter *dm_parameter) { list_del(&dm_parameter->list); free(dm_parameter->name); free(dm_parameter); } static void free_all_list_lw_notify() { struct cwmp_dm_parameter *dm_parameter = NULL, *node; list_for_each_entry_safe(dm_parameter, node, &list_lw_value_change, list) { del_list_lw_notify(dm_parameter); } } void cwmp_lwnotification() { char msg[1024], *msg_out = NULL; char signature[41]; struct addrinfo *servaddr; struct config *conf; conf = &(cwmp_ctx.conf); udplw_server_param(&servaddr); xml_prepare_lwnotification_message(&msg_out); if (msg_out == NULL) { CWMP_LOG(ERROR, "%s: msg_out is null", __FUNCTION__); return; } message_compute_signature(msg_out, signature, sizeof(signature)); snprintf(msg, sizeof(msg), "%s \n %s: %s \n %s: %s \n %s: %zu\n %s: %s\n\n%s", "POST /HTTPS/1.1", "HOST", conf->lwn_hostname, "Content-Type", "test/xml; charset=utf-8", "Content-Lenght", strlen(msg_out), "Signature", signature, msg_out); send_udp_message(servaddr, msg); free_all_list_lw_notify(); //freeaddrinfo(servaddr); //To check FREE(msg_out); }