/*
* Copyright 2019 TP-Link Technologies Co., Ltd. All rights reserved.
*
*
*/
#include "ship_module.h"
#include "ship_model_conf.h"
#include "ship_partition.h"
static ship_module_t **sys_modules = NULL;
static SH_INT32 module_num = 0;
static SH_INT32 module_num_max = 0;
static SH_UINT8 dset_buf[SHIP_USER_CONFIG_BUF_SIZE];
static osal_mutex_t mod_dset_lock;
static SH_UINT8 ship_module_aes256_key[32] = {0};
static SH_UINT8 ship_module_aes256_iv[16] = {0};
void ship_module_register(ship_module_t* module)
{
ship_module_t **new_ptr;
if (!sys_modules) {
sys_modules = osal_malloc(sizeof(ship_module_t*) * 10);
module_num_max = 10;
if (!sys_modules)
return;
}
if (module_num >= module_num_max) {
/* space if not enough, need realloc */
new_ptr = osal_malloc(sizeof(ship_module_t*) * (module_num_max + 5));
if (!new_ptr)
return;
OSAL_MEMCPY(new_ptr, sys_modules, sizeof(ship_module_t*) * module_num_max);
osal_free(sys_modules);
sys_modules = new_ptr;
module_num_max += 5;
}
sys_modules[module_num++] = module;
}
void ship_module_run_init_all()
{
SH_INT32 i;
for (i = 0; i < module_num; i++)
{
if (sys_modules[i]->ops->init)
sys_modules[i]->ops->init();
}
}
void ship_module_run_start_all()
{
SH_INT32 i;
for (i = 0; i < module_num; i++)
{
if (sys_modules[i]->ops->start)
sys_modules[i]->ops->start();
}
}
void ship_module_run_erase_wifi_all()
{
SH_INT32 i;
for (i = 0; i < module_num; i++)
{
if (sys_modules[i]->ops->erase_wifi_cb)
sys_modules[i]->ops->erase_wifi_cb();
}
}
SH_BOOL ship_module_reset_check(SH_CHAR *module_name, json_t *filterArr)
{
SH_INT32 array_size, i;
json_t *item;
if (NULL == filterArr)
return TRUE;
if (NULL == module_name)
return FALSE;
array_size = json_get_array_size(filterArr);
for (i = 0; i < array_size; i++) {
item = json_get_array_item(filterArr, i);
if (!OSAL_STRCMP(module_name, item->valuestring))
return TRUE;
}
return FALSE;
}
void ship_module_run_reset_all(json_t *filterArr)
{
SH_UINT32 i;
/* disable factory mode when do factory reset */
if (filterArr == NULL) {
if (ship_model_conf_get_factory_mode())
ship_model_conf_set_factory_mode(FALSE);
}
for (i = 0; i < module_num; i++)
{
if (sys_modules[i]->ops->reset && ship_module_reset_check(sys_modules[i]->name, filterArr)) {
SHIP_D_PRINTF("rst module:%s\n", sys_modules[i]->name);
sys_modules[i]->ops->reset();
}
}
}
static ship_module_t* get_module_by_name(SH_CHAR *name)
{
SH_INT32 i;
for (i = 0; i < module_num; i++)
{
if (!OSAL_STRNCMP(sys_modules[i]->name, name, SHIP_MOD_NAME_LEN)) // hard code a module name length here
return sys_modules[i];
}
return NULL;
}
void ship_module_run_load_config()
{
SH_UINT8 *ptr;
SH_UINT32 size, i, offset = 0, csum = 0, config_len;
ship_module_t* pm;
ship_modconfig_head_t *phead, mhead;
ship_userconfig_head_t *uhead;
/* load header and encrypted data */
OSAL_MEMZERO(dset_buf, sizeof(dset_buf));
ship_partition_read(SHIP_PARTITION_USER_CONFIG, 0, dset_buf, sizeof(dset_buf));
ptr = dset_buf;
uhead = (ship_userconfig_head_t*)ptr;
/* step1: header validation */
ptr += ALIGN_4B(sizeof(ship_userconfig_head_t));
if (uhead->signature != SHIP_USERCONFIG_MAGIC)
{
SHIP_D_PRINTF("magic invalid\n");
uhead->reset_flag = TRUE;
goto reset_all;
}
if (uhead->version >= SHIP_USERCONFIG_MIN_VERSION)
{
for (i = 0; i < uhead->encrypted_length; i++)
csum += ptr[i];
if (csum != uhead->checksum)
{
SHIP_D_PRINTF("checksum invalid\n");
uhead->reset_flag = TRUE;
goto reset_all;
}
}
else
{
SHIP_D_PRINTF("version invalid\n");
uhead->reset_flag = TRUE;
goto reset_all;
}
/* step2: parse encrypted data */
if (uhead->version >= SHIP_USERCONFIG_MIN_ENCRYPT_VERSION)
{
osal_aes256_cbc_dec(ptr, uhead->encrypted_length, ship_module_aes256_key, sizeof(ship_module_aes256_key),
ship_module_aes256_iv, ptr);
}
size = uhead->encrypted_length;
while (size >= sizeof(ship_modconfig_head_t)){
phead = (ship_modconfig_head_t *)ptr;
if (phead->name[0] == 0){ // null name means end of TLV
break;
}
SHIP_D_PRINTF("load mod %s\n", phead->name);
if (phead->len > size) { // illegal size, maybe content is broken
SHIP_D_PRINTF("illegal len:%d\n", phead->len);
break;
}
if (((pm = get_module_by_name(phead->name)) == NULL) || pm->cfg_type != SHIP_USER_CONFIG_JSON){
// skip unknown mod cfg
size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
ptr += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
continue;
}
if (size < sizeof(ship_modconfig_head_t) + phead->len){
break;
}
// make sure null terminate
phead->value[phead->len - 1] = 0;
pm->ops->load_config(phead->value);
pm->cfg_loaded = TRUE;
size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
ptr += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
}
/* step3: parse non-encrypted data */
size = uhead->length - ALIGN_4B(uhead->encrypted_length);
offset = ALIGN_4B(sizeof(ship_userconfig_head_t)) + ALIGN_4B(uhead->encrypted_length);
while (size >= sizeof(ship_modconfig_head_t)){
ship_partition_read(SHIP_PARTITION_USER_CONFIG, offset, (SH_UINT8*)&mhead, sizeof(mhead));
if (mhead.name[0] == 0){ // null name means end of TLV
break;
}
SHIP_D_PRINTF("load mod %s\n", mhead.name);
if (mhead.len > size) { // illegal size, maybe content is broken
SHIP_D_PRINTF("illegal len:%d\n", mhead.len);
break;
}
if (((pm = get_module_by_name(mhead.name)) == NULL) || pm->cfg_type != SHIP_USER_CONFIG_RAW){
// skip unknown mod cfg
SHIP_D_PRINTF("unknown mod %s\n", mhead.name);
size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len);
offset += ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len);
continue;
}
if (size < sizeof(ship_modconfig_head_t) + mhead.len){
break;
}
ptr = pm->ops->get_config_ptr(&config_len);
if (config_len == mhead.len){
ship_partition_read(SHIP_PARTITION_USER_CONFIG, offset + sizeof(ship_modconfig_head_t), ptr, config_len);
pm->cfg_loaded = TRUE;
} else {
SHIP_D_PRINTF("mod %s length invalid %d %d\n", mhead.name, mhead.len, config_len);
}
size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len);
offset += ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len);
}
reset_all:
for (i = 0; i < module_num; i++)
{
if (sys_modules[i]->cfg_type != SHIP_USER_CONFIG_NONE && !sys_modules[i]->cfg_loaded){
SHIP_D_PRINTF("reset %s\n", sys_modules[i]->name);
if (sys_modules[i]->ops->reset)
sys_modules[i]->ops->reset();
sys_modules[i]->cfg_loaded = TRUE;
}
}
}
static void _ship_module_run_save_config(SH_BOOL reset_flag)
{
SH_UINT8 *ptr, *cfg;
SH_UINT32 size, offset, cur_index = 0, encrypted_length = 0, csum = 0, i, config_len;
ship_module_t* pm;
ship_modconfig_head_t *phead, mhead;
ship_userconfig_head_t *uhead;
//SH_UINT64 startTime = osal_time_ms();
//SHIP_D_PRINTF("***********\r\nThe save start time is %lld\r\n", startTime);
/* step1: prepare header, but checksum and length yet to be done */
ptr = dset_buf;
size = sizeof(dset_buf);
uhead = (ship_userconfig_head_t*)ptr;
uhead->version = SHIP_USERCONFIG_CUR_VERSION;
uhead->signature = SHIP_USERCONFIG_MAGIC;
uhead->reset_flag = reset_flag;
/* step2: write json data to buffer */
size -= ALIGN_4B(sizeof(ship_userconfig_head_t));
ptr += ALIGN_4B(sizeof(ship_userconfig_head_t));
while (size >= sizeof(ship_modconfig_head_t)) {
phead = (ship_modconfig_head_t *)ptr;
if (cur_index >= module_num){
phead->name[0] = 0; // end tag
encrypted_length += sizeof(ship_modconfig_head_t);
break;
}
pm = sys_modules[cur_index];
if (NULL == pm || pm->cfg_type != SHIP_USER_CONFIG_JSON)
cfg = NULL;
else
cfg = pm->ops->get_config_ptr(&config_len);
if(!cfg){
cur_index++;
continue;
}
phead->len = config_len + 1;
//OSAL_PRINTF("MODULE:%s\nLENGTH:%d\nDATA:%s\n", pm->name, phead->len, cfg);
OSAL_STRNCPY(phead->name, pm->name, SHIP_MOD_NAME_LEN);
phead->name[SHIP_MOD_NAME_LEN] = '\0';
if (size < sizeof(ship_userconfig_head_t) + config_len + 1)
{
osal_free(cfg);
break;
}
OSAL_STRCPY((SH_CHAR*)phead->value, (SH_CHAR*)cfg);
cur_index++;
size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
encrypted_length += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
ptr += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len);
osal_free(cfg);
}
/* step3: encrypt json data in the buffer */
/* update total len, make it 16 bytes aligned */
if (encrypted_length % 16 != 0) {
OSAL_MEMSET(ptr, 0, 16 - (encrypted_length % 16));
encrypted_length = encrypted_length + 16 - (encrypted_length % 16);
}
SHIP_D_PRINTF("tl=%d, hl=%d, el=%d\n", (SH_INT32)sizeof(dset_buf), (SH_INT32)ALIGN_4B(sizeof(ship_userconfig_head_t)), encrypted_length);
/* aes256 calculation */
ptr = dset_buf + ALIGN_4B(sizeof(ship_userconfig_head_t));
osal_aes256_cbc_enc(ptr, encrypted_length, ship_module_aes256_key, sizeof(ship_module_aes256_key),
ship_module_aes256_iv, ptr);
/* step4: write raw data to flash */
ship_partition_erase(SHIP_PARTITION_USER_CONFIG);
offset = ALIGN_4B(sizeof(ship_userconfig_head_t)) + ALIGN_4B(encrypted_length);
for (i = 0; i < module_num; i++) {
pm = sys_modules[i];
if (NULL == pm || pm->cfg_type != SHIP_USER_CONFIG_RAW)
continue;
cfg = pm->ops->get_config_ptr(&config_len);
OSAL_MEMZERO(&mhead, sizeof(mhead));
mhead.len = config_len;
OSAL_STRNCPY(mhead.name, pm->name, SHIP_MOD_NAME_LEN);
ship_partition_write(SHIP_PARTITION_USER_CONFIG, offset, (SH_UINT8*)&mhead, sizeof(mhead));
ship_partition_write(SHIP_PARTITION_USER_CONFIG, offset + sizeof(ship_modconfig_head_t), cfg, config_len);
offset += ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len);
}
// null TLV
OSAL_MEMZERO(&mhead, sizeof(mhead));
ship_partition_write(SHIP_PARTITION_USER_CONFIG, offset, (SH_UINT8*)&mhead, sizeof(mhead));
offset += ALIGN_4B(sizeof(ship_modconfig_head_t));
/* step5: complete the header and write to flash */
uhead->length = offset - ALIGN_4B(sizeof(ship_userconfig_head_t));
uhead->encrypted_length = encrypted_length;
ptr = dset_buf + ALIGN_4B(sizeof(ship_userconfig_head_t));
for (i = 0; i < encrypted_length; i++)
csum += ptr[i];
uhead->checksum = csum;
ship_partition_write(SHIP_PARTITION_USER_CONFIG, 0, dset_buf, ALIGN_4B(sizeof(ship_userconfig_head_t)) + encrypted_length);
//SH_UINT64 endTime = osal_time_ms();
//SHIP_D_PRINTF("The save end time is %lld\r\n", endTime);
//SHIP_D_PRINTF("The comsume time is %lld\r\n **************\r\n", endTime-startTime);
}
typedef struct _ship_nvram_cache_header_t{
SH_UINT32 magicNum;
SH_INT32 entry_start_addr[SHIP_SLOT_NUM];
SH_INT32 entry_size[SHIP_SLOT_NUM];
}ship_nvram_cache_header_t;
#define SHIP_CONFIG_CACHE_MAGIC 0x43414348 // CACH
static SH_INT32 cache_offset[SHIP_SLOT_NUM];
SH_BOOL ship_module_save_data_to_cache(SH_INT32 index, void *data, SH_INT32 len)
{
ship_nvram_cache_header_t header;
SH_INT32 i, size, part_len;
ship_partition_read(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header));
if (header.magicNum != SHIP_CONFIG_CACHE_MAGIC) {
ship_partition_erase(SHIP_PARTITION_USER_CACHE);
/* check if erase success */
ship_partition_read(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header));
if (header.magicNum != 0xFFFFFFFF){
/* some platform cache is shared with other partition, so can not really erase OSAL_NVRAM_USER_CACHE */
return FALSE;
}
part_len = ship_partition_get_size(SHIP_PARTITION_USER_CACHE);
size = (part_len - sizeof(header)) / SHIP_SLOT_NUM;
for (i = 0; i < SHIP_SLOT_NUM; i++) {
header.entry_start_addr[i] = sizeof(header) + size * i;
header.entry_size[i] = size;
cache_offset[i] = 0;
}
header.magicNum = SHIP_CONFIG_CACHE_MAGIC;
ship_partition_write(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header));
}
if (cache_offset[index] + len > header.entry_size[index]) {
SHIP_D_PRINTF("cache full\n");
return FALSE;
}
ship_partition_write(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index], data, len);
cache_offset[index] += len;
return TRUE;
}
static SH_BOOL cache_data_validate(SH_UINT8 *data, SH_INT32 len)
{
SH_INT32 i;
/* all 0xFF means invalid */
for (i = 0; i < len; i++) {
if (data[i] != 0xFF)
return TRUE;
}
return FALSE;
}
SH_BOOL ship_module_read_data_from_cache(SH_INT32 index, void *data, SH_INT32 len)
{
ship_nvram_cache_header_t header;
cache_offset[index] = 0;
ship_partition_read(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header));
if (header.magicNum != SHIP_CONFIG_CACHE_MAGIC) {
SHIP_D_PRINTF("Read cache fail\n");
return FALSE;
}
while (cache_offset[index] + len <= header.entry_size[index]) {
ship_partition_read(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index], (SH_UINT8*)data, len);
if (!cache_data_validate((SH_UINT8*)data, len)) {
if (cache_offset[index] >= len) {
/* if current block is invalid, return last valid block */
ship_partition_read(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index] - len, (SH_UINT8*)data, len);
return TRUE;
} else {
/* first block is invalid */
return FALSE;
}
}
cache_offset[index] += len;
}
/* cache is full, return the last block */
if (cache_offset[index] + len > header.entry_size[index]) {
ship_partition_read(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index] - len, (SH_UINT8*)data, len);
return TRUE;
}
cache_offset[index] = 0;
return FALSE;
}
void ship_module_run_save_config()
{
_ship_module_run_save_config(FALSE);
}
void ship_module_run_save_config_locked()
{
ship_module_dset_lock();
ship_module_run_save_config();
ship_module_dset_unlock();
}
void ship_module_run_save_rst_config()
{
_ship_module_run_save_config(TRUE);
}
void ship_module_dset_lock_init()
{
if (osal_mutex_create(&(mod_dset_lock)) != OSAL_OK)
{
SHIP_D_PRINTF("mod_dset_lock create fail\n");
}
}
void ship_module_dset_lock()
{
osal_mutex_lock(mod_dset_lock);
}
void ship_module_dset_unlock()
{
osal_mutex_unlock(mod_dset_lock);
}
SH_BOOL ship_module_is_factory()
{
ship_userconfig_head_t uhead;
ship_partition_read(SHIP_PARTITION_USER_CONFIG, 0, (SH_UINT8*)&uhead, sizeof(uhead));
return uhead.reset_flag;
}
void ship_module_aes256_init()
{
SH_CHAR deviceId[41] = {0};
SH_UINT8 mac[6] = {0};
// generate iv according to mac address
ship_model_conf_get_device_mac(mac);
OSAL_MEMSET(ship_module_aes256_iv, 0, sizeof(ship_module_aes256_iv));
OSAL_MEMCPY(ship_module_aes256_iv, mac, sizeof(mac));
// generate aes256 key
ship_model_conf_get_device_id(deviceId, 40);
osal_sha256_digest((SH_UINT8*)deviceId, 40, ship_module_aes256_key);
}
这个呢
最新发布