// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "mesh/mesh-defs.h" #include "mesh/node.h" #include "mesh/net.h" #include "mesh/appkey.h" #include "mesh/model.h" #include "mesh/mesh-config.h" #include "mesh/cfgmod.h" #define CREDFLAG_MASK 0x1000 #define CFG_GET_ID(vendor, pkt) ((vendor) ? \ (SET_ID(l_get_le16((pkt)), l_get_le16((pkt) + 2))) : \ (SET_ID(SIG_VENDOR, l_get_le16(pkt)))) /* Supported composition pages, sorted high to low */ static const uint8_t supported_pages[] = { 128, 0 }; static uint8_t msg[MAX_MSG_LEN]; static uint16_t set_pub_status(uint8_t status, uint16_t ele_addr, uint32_t id, uint16_t pub_addr, uint16_t idx, bool cred_flag, uint8_t ttl, uint8_t period, uint8_t rtx) { size_t n; n = mesh_model_opcode_set(OP_CONFIG_MODEL_PUB_STATUS, msg); msg[n++] = status; l_put_le16(ele_addr, msg + n); l_put_le16(pub_addr, msg + n + 2); idx |= cred_flag ? CREDFLAG_MASK : 0; l_put_le16(idx, msg + n + 4); n += 6; msg[n++] = ttl; msg[n++] = period; msg[n++] = rtx; if (!IS_VENDOR(id)) { l_put_le16(MODEL_ID(id), msg + n); n += 2; } else { l_put_le16(VENDOR_ID(id), msg + n); l_put_le16(MODEL_ID(id), msg + n + 2); n += 4; } return n; } static uint16_t config_pub_get(struct mesh_node *node, const uint8_t *pkt, bool vendor) { uint32_t id; uint16_t ele_addr; uint8_t rtx; struct mesh_model_pub *pub; int status; ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 2); pub = mesh_model_pub_get(node, ele_addr, id, &status); if (pub && status == MESH_STATUS_SUCCESS) { rtx = pub->rtx.cnt + (((pub->rtx.interval / 50) - 1) << 3); return set_pub_status(status, ele_addr, id, pub->addr, pub->idx, pub->credential, pub->ttl, pub->period, rtx); } else return set_pub_status(status, ele_addr, id, 0, 0, 0, 0, 0, 0); } static uint16_t config_pub_set(struct mesh_node *node, const uint8_t *pkt, bool virt, bool vendor) { uint32_t id; uint16_t ele_addr, idx, pub_dst; const uint8_t *pub_addr; uint8_t ttl, period, rtx, cnt, interval; int status; bool cred_flag; ele_addr = l_get_le16(pkt); pub_addr = pkt + 2; pub_dst = l_get_le16(pub_addr); if (!virt && IS_VIRTUAL(pub_dst)) return 0; pkt += (virt ? 14 : 0); idx = l_get_le16(pkt + 4); cred_flag = !!(CREDFLAG_MASK & idx); idx &= APP_IDX_MASK; ttl = pkt[6]; period = pkt[7]; rtx = pkt[8]; id = CFG_GET_ID(vendor, pkt + 9); cnt = rtx & 0x7; interval = ((rtx >> 3) + 1) * 50; status = mesh_model_pub_set(node, ele_addr, id, pub_addr, idx, cred_flag, ttl, period, cnt, interval, virt, &pub_dst); l_debug("pub_set: status %d, ea %4.4x, ota: %4.4x, id: %x, idx: %3.3x", status, ele_addr, pub_dst, id, idx); if (status != MESH_STATUS_SUCCESS) return set_pub_status(status, ele_addr, id, 0, 0, 0, 0, 0, 0); if (IS_UNASSIGNED(pub_dst) && !virt) { ttl = period = idx = 0; /* Remove model publication from config file */ if (!mesh_config_model_pub_del(node_config_get(node), ele_addr, vendor ? id : MODEL_ID(id), vendor)) status = MESH_STATUS_STORAGE_FAIL; } else { struct mesh_config_pub db_pub = { .virt = virt, .addr = pub_dst, .idx = idx, .ttl = ttl, .credential = cred_flag, .period = period, .cnt = cnt, .interval = interval }; if (virt) memcpy(db_pub.virt_addr, pub_addr, 16); /* Save model publication to config file */ if (!mesh_config_model_pub_add(node_config_get(node), ele_addr, vendor ? id : MODEL_ID(id), vendor, &db_pub)) status = MESH_STATUS_STORAGE_FAIL; } return set_pub_status(status, ele_addr, id, pub_dst, idx, cred_flag, ttl, period, rtx); } static uint16_t cfg_sub_get_msg(struct mesh_node *node, const uint8_t *pkt, uint16_t size) { uint16_t ele_addr, n, sub_len; uint32_t id; int opcode; bool vendor = (size == 6); ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 2); opcode = vendor ? OP_CONFIG_VEND_MODEL_SUB_LIST : OP_CONFIG_MODEL_SUB_LIST; n = mesh_model_opcode_set(opcode, msg); memcpy(msg + n + 1, pkt, size); msg[n] = mesh_model_sub_get(node, ele_addr, id, msg + n + 1 + size, MAX_MSG_LEN - (n + 1 + size), &sub_len); if (msg[n] == MESH_STATUS_SUCCESS) n += sub_len; n += (size + 1); return n; } static bool save_cfg_sub(struct mesh_node *node, uint16_t ele_addr, uint32_t id, bool vendor, const uint8_t *label, bool virt, uint16_t grp, uint32_t opcode) { struct mesh_config *cfg = node_config_get(node); struct mesh_config_sub db_sub = { .virt = virt, .addr.grp = grp }; id = (vendor) ? id : MODEL_ID(id); if (virt) memcpy(db_sub.addr.label, label, 16); if (opcode == OP_CONFIG_MODEL_SUB_VIRT_DELETE || opcode == OP_CONFIG_MODEL_SUB_DELETE) return mesh_config_model_sub_del(cfg, ele_addr, id, vendor, &db_sub); if (opcode == OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE || opcode == OP_CONFIG_MODEL_SUB_OVERWRITE) if (!mesh_config_model_sub_del_all(cfg, ele_addr, id, vendor)) return false; return mesh_config_model_sub_add(cfg, ele_addr, id, vendor, &db_sub); } static uint16_t cfg_sub_add_msg(struct mesh_node *node, const uint8_t *pkt, bool vendor, uint32_t opcode) { uint16_t addr, ele_addr, n; uint32_t id; addr = l_get_le16(pkt + 2); if (!IS_GROUP(addr)) return 0; ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 4); n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg); if (opcode == OP_CONFIG_MODEL_SUB_OVERWRITE) msg[n] = mesh_model_sub_ovrt(node, ele_addr, id, addr); else if (opcode == OP_CONFIG_MODEL_SUB_ADD) msg[n] = mesh_model_sub_add(node, ele_addr, id, addr); else msg[n] = mesh_model_sub_del(node, ele_addr, id, addr); if (msg[n] == MESH_STATUS_SUCCESS && !save_cfg_sub(node, ele_addr, id, vendor, NULL, false, addr, opcode)) msg[n] = MESH_STATUS_STORAGE_FAIL; if (vendor) { memcpy(msg + n + 1, pkt, 8); n += 9; } else { memcpy(msg + n + 1, pkt, 6); n += 7; } return n; } static uint16_t cfg_virt_sub_add_msg(struct mesh_node *node, const uint8_t *pkt, bool vendor, uint32_t opcode) { uint16_t addr, ele_addr, n; uint32_t id; const uint8_t *label; n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg); ele_addr = l_get_le16(pkt); label = pkt + 2; id = CFG_GET_ID(vendor, pkt + 18); if (opcode == OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE) msg[n] = mesh_model_virt_sub_ovrt(node, ele_addr, id, label, &addr); else if (opcode == OP_CONFIG_MODEL_SUB_VIRT_ADD) msg[n] = mesh_model_virt_sub_add(node, ele_addr, id, label, &addr); else msg[n] = mesh_model_virt_sub_del(node, ele_addr, id, label, &addr); if (msg[n] == MESH_STATUS_SUCCESS && !save_cfg_sub(node, ele_addr, id, vendor, label, true, addr, opcode)) msg[n] = MESH_STATUS_STORAGE_FAIL; /* If processing failed, set addr field to zero in reply */ if (msg[n] != MESH_STATUS_SUCCESS) addr = UNASSIGNED_ADDRESS; l_put_le16(ele_addr, msg + n + 1); l_put_le16(addr, msg + n + 3); if (vendor) { l_put_le16(VENDOR_ID(id), msg + n + 5); l_put_le16(MODEL_ID(id), msg + n + 7); n += 9; } else { l_put_le16(MODEL_ID(id), msg + n + 5); n += 7; } return n; } static uint16_t config_sub_del_all(struct mesh_node *node, const uint8_t *pkt, bool vendor) { uint16_t ele_addr, n, grp = UNASSIGNED_ADDRESS; uint32_t id; n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg); ele_addr = l_get_le16(pkt); id = CFG_GET_ID(vendor, pkt + 2); msg[n] = mesh_model_sub_del_all(node, ele_addr, id); if (msg[n] == MESH_STATUS_SUCCESS) { struct mesh_config *cfg = node_config_get(node); if (!mesh_config_model_sub_del_all(cfg, ele_addr, vendor ? id : MODEL_ID(id), vendor)) msg[n] = MESH_STATUS_STORAGE_FAIL; } l_put_le16(ele_addr, msg + n + 1); l_put_le16(grp, msg + n + 3); if (vendor) { l_put_le16(VENDOR_ID(id), msg + n + 5); l_put_le16(MODEL_ID(id), msg + n + 7); n += 9; } else { l_put_le16(MODEL_ID(id), msg + n + 5); n += 7; } return n; } static uint16_t model_app_list(struct mesh_node *node, const uint8_t *pkt, uint16_t size) { uint16_t ele_addr, n, bnd_len; uint32_t id; int opcode; opcode = (size == 4) ? OP_MODEL_APP_LIST : OP_VEND_MODEL_APP_LIST; ele_addr = l_get_le16(pkt); n = mesh_model_opcode_set(opcode, msg); memcpy(msg + n + 1, pkt, size); id = CFG_GET_ID(size == 6, pkt + 2); msg[n] = mesh_model_get_bindings(node, ele_addr, id, msg + n + 1 + size, MAX_MSG_LEN - (n + 1 + size), &bnd_len); if (msg[n] == MESH_STATUS_SUCCESS) n += bnd_len; n += (size + 1); return n; } static uint16_t model_app_bind(struct mesh_node *node, const uint8_t *pkt, uint16_t size, bool unbind) { uint16_t ele_addr, idx, n; uint32_t id; idx = l_get_le16(pkt + 2); if (idx > APP_IDX_MAX) return 0; ele_addr = l_get_le16(pkt); id = CFG_GET_ID(size == 8, pkt + 4); n = mesh_model_opcode_set(OP_MODEL_APP_STATUS, msg); if (unbind) msg[n] = mesh_model_binding_del(node, ele_addr, id, idx); else msg[n] = mesh_model_binding_add(node, ele_addr, id, idx); memcpy(msg + n + 1, pkt, size); n += (size + 1); return n; } static uint16_t cfg_relay_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint8_t count; uint16_t interval; uint16_t n; if (opcode == OP_CONFIG_RELAY_SET) { count = (pkt[1] & 0x7) + 1; interval = ((pkt[1] >> 3) + 1) * 10; node_relay_mode_set(node, !!pkt[0], count, interval); } n = mesh_model_opcode_set(OP_CONFIG_RELAY_STATUS, msg); msg[n++] = node_relay_mode_get(node, &count, &interval); msg[n++] = (count - 1) + ((interval/10 - 1) << 3); return n; } static uint16_t cfg_key_refresh_phase(struct mesh_node *node, const uint8_t *pkt, int opcode) { struct mesh_net *net = node_get_net(node); uint16_t n, idx = l_get_le16(pkt); uint8_t phase; int status; n = mesh_model_opcode_set(OP_CONFIG_KEY_REFRESH_PHASE_STATUS, msg); status = mesh_net_key_refresh_phase_get(net, idx, &phase); if (status == MESH_STATUS_SUCCESS && opcode == OP_CONFIG_KEY_REFRESH_PHASE_SET) { if (pkt[2] == KEY_REFRESH_TRANS_TWO) { if (phase == KEY_REFRESH_PHASE_TWO) goto done; else if (phase != KEY_REFRESH_PHASE_ONE) return 0; } if (pkt[2] == KEY_REFRESH_TRANS_THREE && phase == KEY_REFRESH_PHASE_NONE) goto done; status = mesh_net_key_refresh_phase_set(net, idx, pkt[2]); l_debug("Set KR Phase: net=%3.3x transition=%d", idx, pkt[2]); if (status == MESH_STATUS_SUCCESS) mesh_net_key_refresh_phase_get(net, idx, &phase); } done: msg[n] = status; l_put_le16(idx, &msg[n + 1]); msg[n + 3] = (status != MESH_STATUS_SUCCESS) ? KEY_REFRESH_PHASE_NONE : phase; return n + 4; } static uint8_t uint32_to_log(uint32_t value) { uint32_t val = 1; uint8_t ret = 0; if (!value) return 0; else if (value > 0x10000) return 0xff; while (val <= value) { val <<= 1; ret++; } return ret; } static uint16_t hb_subscription_status(struct mesh_node *node, int status) { struct mesh_net *net = node_get_net(node); struct mesh_net_heartbeat_sub *sub = mesh_net_get_heartbeat_sub(net); struct timeval time_now; uint16_t n; gettimeofday(&time_now, NULL); time_now.tv_sec -= sub->start; if (time_now.tv_sec >= (long) sub->period) time_now.tv_sec = 0; else time_now.tv_sec = sub->period - time_now.tv_sec; l_debug("Sub Period (Log %2.2x) %d sec", uint32_to_log(time_now.tv_sec), (int) time_now.tv_sec); n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_SUB_STATUS, msg); msg[n++] = status; l_put_le16(sub->src, msg + n); n += 2; l_put_le16(sub->dst, msg + n); n += 2; msg[n++] = uint32_to_log(time_now.tv_sec); msg[n++] = sub->count != 0xffff ? uint32_to_log(sub->count) : 0xff; msg[n++] = sub->min_hops; msg[n++] = sub->max_hops; return n; } static uint16_t hb_subscription_get(struct mesh_node *node, int status) { struct mesh_net *net = node_get_net(node); struct mesh_net_heartbeat_sub *sub = mesh_net_get_heartbeat_sub(net); /* * MshPRFv1.0.1 section 4.4.1.2.16, Heartbeat Subscription state: * If this is a GET request and the source or destination is unassigned, * all fields shall be set to zero in the status reply. */ if (IS_UNASSIGNED(sub->src) || IS_UNASSIGNED(sub->dst)) { uint16_t n; n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_SUB_STATUS, msg); memset(msg + n, 0, 9); n += 9; return n; } return hb_subscription_status(node, status); } static uint16_t hb_subscription_set(struct mesh_node *node, const uint8_t *pkt) { uint16_t src, dst; uint8_t period_log; struct mesh_net *net; int status; src = l_get_le16(pkt); dst = l_get_le16(pkt + 2); /* SRC must be Unicast, DST can be any legal address except Virtual */ if ((!IS_UNASSIGNED(src) && !IS_UNICAST(src)) || IS_VIRTUAL(dst)) return 0; period_log = pkt[4]; if (period_log > 0x11) return 0; net = node_get_net(node); status = mesh_net_set_heartbeat_sub(net, src, dst, period_log); return hb_subscription_status(node, status); } static uint16_t hb_publication_get(struct mesh_node *node, int status) { struct mesh_net *net = node_get_net(node); struct mesh_net_heartbeat_pub *pub = mesh_net_get_heartbeat_pub(net); uint16_t n; n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_PUB_STATUS, msg); msg[n++] = status; l_put_le16(pub->dst, msg + n); n += 2; msg[n++] = pub->count != 0xffff ? uint32_to_log(pub->count) : 0xff; msg[n++] = uint32_to_log(pub->period); msg[n++] = pub->ttl; l_put_le16(pub->features, msg + n); n += 2; l_put_le16(pub->net_idx, msg + n); n += 2; return n; } static uint16_t hb_publication_set(struct mesh_node *node, const uint8_t *pkt) { uint16_t dst, features, net_idx; uint8_t period_log, count_log, ttl; struct mesh_net *net; int status; dst = l_get_le16(pkt); count_log = pkt[2]; period_log = pkt[3]; ttl = pkt[4]; if (count_log > 0x11 && count_log != 0xff) return 0; if (period_log > 0x11 || ttl > TTL_MASK || IS_VIRTUAL(dst)) return 0; features = l_get_le16(pkt + 5) & 0xf; net_idx = l_get_le16(pkt + 7); net = node_get_net(node); status = mesh_net_set_heartbeat_pub(net, dst, features, net_idx, ttl, count_log, period_log); if (status != MESH_STATUS_SUCCESS) { uint16_t n; n = mesh_model_opcode_set(OP_CONFIG_HEARTBEAT_PUB_STATUS, msg); msg[n++] = status; memcpy(msg + n, pkt, 9); n += 9; return n; } else return hb_publication_get(node, status); } static void node_reset(void *user_data) { struct mesh_node *node = user_data; l_debug("Node Reset"); node_remove(node); } static uint16_t cfg_appkey_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint16_t n_idx, a_idx, n; struct mesh_net *net = node_get_net(node); n_idx = l_get_le16(pkt) & 0xfff; a_idx = l_get_le16(pkt + 1) >> 4; n = mesh_model_opcode_set(OP_APPKEY_STATUS, msg); if (opcode == OP_APPKEY_ADD) msg[n] = appkey_key_add(net, n_idx, a_idx, pkt + 3); else if (opcode == OP_APPKEY_UPDATE) msg[n] = appkey_key_update(net, n_idx, a_idx, pkt + 3); else msg[n] = appkey_key_delete(net, n_idx, a_idx); l_debug("AppKey Command %s: Net_Idx %3.3x, App_Idx %3.3x", (msg[n] == MESH_STATUS_SUCCESS) ? "success" : "fail", n_idx, a_idx); memcpy(msg + n + 1, &pkt[0], 3); return n + 4; } static uint16_t cfg_netkey_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint16_t n_idx, n; struct mesh_net *net = node_get_net(node); n_idx = l_get_le16(pkt); if (n_idx > NET_IDX_MAX) return 0; n = mesh_model_opcode_set(OP_NETKEY_STATUS, msg); if (opcode == OP_NETKEY_ADD) msg[n] = mesh_net_add_key(net, n_idx, pkt + 2); else if (opcode == OP_NETKEY_UPDATE) msg[n] = mesh_net_update_key(net, n_idx, pkt + 2); else msg[n] = mesh_net_del_key(net, n_idx); l_debug("NetKey Command %s: Net_Idx %3.3x", (msg[n] == MESH_STATUS_SUCCESS) ? "success" : "fail", n_idx); memcpy(msg + n + 1, &pkt[0], 2); return n + 3; } static uint16_t cfg_get_appkeys_msg(struct mesh_node *node, const uint8_t *pkt) { uint16_t n_idx, sz, n; n_idx = l_get_le16(pkt); n = mesh_model_opcode_set(OP_APPKEY_LIST, msg); l_put_le16(n_idx, msg + n + 1); msg[n] = appkey_list(node_get_net(node), n_idx, msg + n + 3, MAX_MSG_LEN - (n + 3), &sz); return n + 3 + sz; } static uint16_t cfg_poll_timeout_msg(struct mesh_node *node, const uint8_t *pkt) { uint16_t n, addr = l_get_le16(pkt); uint32_t poll_to; if (!IS_UNICAST(addr)) return 0; n = mesh_model_opcode_set(OP_CONFIG_POLL_TIMEOUT_STATUS, msg); l_put_le16(addr, msg + n); n += 2; poll_to = mesh_net_friend_timeout(node_get_net(node), addr); msg[n++] = poll_to; msg[n++] = poll_to >> 8; msg[n++] = poll_to >> 16; return n; } static uint16_t cfg_net_tx_msg(struct mesh_node *node, const uint8_t *pkt, int opcode) { uint8_t cnt; uint16_t interval, n; struct mesh_net *net = node_get_net(node); cnt = (pkt[0] & 0x7) + 1; interval = ((pkt[0] >> 3) + 1) * 10; if (opcode == OP_CONFIG_NETWORK_TRANSMIT_SET && mesh_config_write_net_transmit(node_config_get(node), cnt, interval)) mesh_net_transmit_params_set(net, cnt, interval); n = mesh_model_opcode_set(OP_CONFIG_NETWORK_TRANSMIT_STATUS, msg); mesh_net_transmit_params_get(net, &cnt, &interval); msg[n++] = (cnt - 1) + ((interval/10 - 1) << 3); return n; } static uint16_t get_composition(struct mesh_node *node, uint8_t page, uint8_t *buf) { const uint8_t *comp; uint16_t len = 0; size_t i; for (i = 0; i < sizeof(supported_pages); i++) { if (page < supported_pages[i]) continue; page = supported_pages[i]; comp = node_get_comp(node, page, &len); if (!page || len) break; } if (!len) return 0; *buf++ = page; memcpy(buf, comp, len); return len + 1; } static bool cfg_srv_pkt(uint16_t src, uint16_t dst, uint16_t app_idx, uint16_t net_idx, const uint8_t *data, uint16_t size, const void *user_data) { struct mesh_node *node = (struct mesh_node *) user_data; struct mesh_net *net; const uint8_t *pkt = data; uint32_t opcode; uint16_t n_idx; uint8_t state; bool virt = false; uint16_t n; if (app_idx != APP_IDX_DEV_LOCAL) return false; if (mesh_model_opcode_get(pkt, size, &opcode, &n)) { size -= n; pkt += n; } else return false; net = node_get_net(node); l_debug("CONFIG-SRV-opcode 0x%x size %u idx %3.3x", opcode, size, net_idx); n = 0; switch (opcode) { default: return false; case OP_DEV_COMP_GET: if (size != 1) return true; n = mesh_model_opcode_set(OP_DEV_COMP_STATUS, msg); n += get_composition(node, pkt[0], msg + n); break; case OP_CONFIG_DEFAULT_TTL_SET: if (size != 1 || pkt[0] > TTL_MASK || pkt[0] == 1) return true; node_default_ttl_set(node, pkt[0]); /* Fall Through */ case OP_CONFIG_DEFAULT_TTL_GET: if (opcode == OP_CONFIG_DEFAULT_TTL_GET && size != 0) return true; l_debug("Get/Set Default TTL"); n = mesh_model_opcode_set(OP_CONFIG_DEFAULT_TTL_STATUS, msg); msg[n++] = node_default_ttl_get(node); break; case OP_CONFIG_MODEL_PUB_VIRT_SET: if (size != 25 && size != 27) return true; virt = true; /* Fall Through */ case OP_CONFIG_MODEL_PUB_SET: if (!virt && (size != 11 && size != 13)) return true; n = config_pub_set(node, pkt, virt, size == 13 || size == 27); break; case OP_CONFIG_MODEL_PUB_GET: if (size != 4 && size != 6) return true; n = config_pub_get(node, pkt, size == 6); break; case OP_CONFIG_VEND_MODEL_SUB_GET: if (size != 6) return true; /* Fall Through */ case OP_CONFIG_MODEL_SUB_GET: if (size != 4 && opcode == OP_CONFIG_MODEL_SUB_GET) return true; n = cfg_sub_get_msg(node, pkt, size); break; case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE: case OP_CONFIG_MODEL_SUB_VIRT_DELETE: case OP_CONFIG_MODEL_SUB_VIRT_ADD: if (size != 20 && size != 22) return true; n = cfg_virt_sub_add_msg(node, pkt, size == 22, opcode); break; case OP_CONFIG_MODEL_SUB_OVERWRITE: case OP_CONFIG_MODEL_SUB_DELETE: case OP_CONFIG_MODEL_SUB_ADD: if (size != 6 && size != 8) return true; n = cfg_sub_add_msg(node, pkt, size == 8, opcode); break; case OP_CONFIG_MODEL_SUB_DELETE_ALL: if (size != 4 && size != 6) return true; n = config_sub_del_all(node, pkt, size == 6); break; case OP_CONFIG_RELAY_SET: if (size != 2 || pkt[0] > 0x01) return true; /* Fall Through */ case OP_CONFIG_RELAY_GET: if (opcode == OP_CONFIG_RELAY_GET && size != 0) return true; n = cfg_relay_msg(node, pkt, opcode); break; case OP_CONFIG_NETWORK_TRANSMIT_SET: if (size != 1) return true; /* Fall Through */ case OP_CONFIG_NETWORK_TRANSMIT_GET: if (opcode == OP_CONFIG_NETWORK_TRANSMIT_GET && size != 0) return true; n = cfg_net_tx_msg(node, pkt, opcode); break; case OP_CONFIG_PROXY_SET: if (size != 1 || pkt[0] > 0x01) return true; node_proxy_mode_set(node, !!pkt[0]); /* Fall Through */ case OP_CONFIG_PROXY_GET: if (opcode == OP_CONFIG_PROXY_GET && size != 0) return true; n = mesh_model_opcode_set(OP_CONFIG_PROXY_STATUS, msg); msg[n++] = node_proxy_mode_get(node); l_debug("Get/Set Config Proxy (%d)", msg[n-1]); break; case OP_NODE_IDENTITY_SET: if (size != 3) return true; /* Currently setting node identity not supported */ /* Fall Through */ case OP_NODE_IDENTITY_GET: if (opcode == OP_NODE_IDENTITY_GET && size != 2) return true; n_idx = l_get_le16(pkt); if (n_idx > NET_IDX_MAX) return true; n = mesh_model_opcode_set(OP_NODE_IDENTITY_STATUS, msg); msg[n++] = mesh_net_get_identity_mode(net, n_idx, &state); l_put_le16(n_idx, msg + n); n += 2; msg[n++] = state; l_debug("Get/Set Config Identity (%d)", state); break; case OP_CONFIG_BEACON_SET: if (size != 1 || pkt[0] > 0x01) return true; node_beacon_mode_set(node, !!pkt[0]); /* Fall Through */ case OP_CONFIG_BEACON_GET: if (opcode == OP_CONFIG_BEACON_GET && size != 0) return true; n = mesh_model_opcode_set(OP_CONFIG_BEACON_STATUS, msg); msg[n++] = node_beacon_mode_get(node); l_debug("Get/Set Config Beacon (%d)", msg[n-1]); break; case OP_CONFIG_FRIEND_SET: if (size != 1 || pkt[0] > 0x01) return true; node_friend_mode_set(node, !!pkt[0]); /* Fall Through */ case OP_CONFIG_FRIEND_GET: if (opcode == OP_CONFIG_FRIEND_GET && size != 0) return true; n = mesh_model_opcode_set(OP_CONFIG_FRIEND_STATUS, msg); msg[n++] = node_friend_mode_get(node); l_debug("Get/Set Friend (%d)", msg[n-1]); break; case OP_CONFIG_KEY_REFRESH_PHASE_SET: if (size != 3 || (pkt[2] != KEY_REFRESH_TRANS_THREE && pkt[2] != KEY_REFRESH_TRANS_TWO)) return true; /* Fall Through */ case OP_CONFIG_KEY_REFRESH_PHASE_GET: if (size != 2 && opcode == OP_CONFIG_KEY_REFRESH_PHASE_GET) return true; n = cfg_key_refresh_phase(node, pkt, opcode); break; case OP_APPKEY_ADD: case OP_APPKEY_UPDATE: if (size != 19) return true; /* Fall Through */ case OP_APPKEY_DELETE: if (opcode == OP_APPKEY_DELETE && size != 3) return true; n = cfg_appkey_msg(node, pkt, opcode); break; case OP_APPKEY_GET: if (size != 2) return true; n = cfg_get_appkeys_msg(node, pkt); break; case OP_NETKEY_ADD: case OP_NETKEY_UPDATE: if (size != 18) return true; /* Fall Through */ case OP_NETKEY_DELETE: if (opcode == OP_NETKEY_DELETE && size != 2) return true; n = cfg_netkey_msg(node, pkt, opcode); break; case OP_NETKEY_GET: if (size != 0) return true; n = mesh_model_opcode_set(OP_NETKEY_LIST, msg); size = MAX_MSG_LEN - n; if (mesh_net_key_list_get(net, msg + n, &size)) n += size; break; case OP_MODEL_APP_BIND: case OP_MODEL_APP_UNBIND: if (size != 6 && size != 8) return true; n = model_app_bind(node, pkt, size, opcode != OP_MODEL_APP_BIND); break; case OP_VEND_MODEL_APP_GET: if (size != 6) return true; n = model_app_list(node, pkt, size); break; case OP_MODEL_APP_GET: if (size != 4) return true; n = model_app_list(node, pkt, size); break; case OP_CONFIG_HEARTBEAT_PUB_SET: l_debug("Config Heartbeat Publication Set"); if (size != 9) return true; n = hb_publication_set(node, pkt); break; case OP_CONFIG_HEARTBEAT_PUB_GET: if (size != 0) return true; n = hb_publication_get(node, MESH_STATUS_SUCCESS); break; case OP_CONFIG_HEARTBEAT_SUB_SET: if (size != 5) return true; l_debug("Set HB Sub Period Log %2.2x", pkt[4]); n = hb_subscription_set(node, pkt); break; case OP_CONFIG_HEARTBEAT_SUB_GET: if (size != 0) return true; n = hb_subscription_get(node, MESH_STATUS_SUCCESS); break; case OP_CONFIG_POLL_TIMEOUT_GET: if (size != 2) return true; n = cfg_poll_timeout_msg(node, pkt); break; case OP_NODE_RESET: if (size != 0) return true; n = mesh_model_opcode_set(OP_NODE_RESET_STATUS, msg); /* Delay node removal to give it a chance to send the status */ l_idle_oneshot(node_reset, node, NULL); break; } if (n) mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx, DEFAULT_TTL, false, n, msg); return true; } static void cfgmod_srv_unregister(void *user_data) { } static const struct mesh_model_ops ops = { .unregister = cfgmod_srv_unregister, .recv = cfg_srv_pkt, .bind = NULL, .sub = NULL, .pub = NULL }; void cfgmod_server_init(struct mesh_node *node, uint8_t ele_idx) { l_debug("%2.2x", ele_idx); mesh_model_register(node, ele_idx, CONFIG_SRV_MODEL, &ops, node); }