Implement reception and response for Proxy Configuration Messages (MshPRT_v1.1, section 6.6). ToDo: Apply configured filters. --- Makefile.mesh | 3 +- mesh/crypto.c | 6 +- mesh/crypto.h | 3 + mesh/net-keys.c | 58 +++++++++++++++++- mesh/net-keys.h | 3 + mesh/net.c | 56 +++++++++++++++++- mesh/proxy-cfg.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++ mesh/proxy-cfg.h | 17 ++++++ 8 files changed, 289 insertions(+), 6 deletions(-) create mode 100644 mesh/proxy-cfg.c create mode 100644 mesh/proxy-cfg.h diff --git a/Makefile.mesh b/Makefile.mesh index 8a190f75de9d..d39bb17eca70 100644 --- a/Makefile.mesh +++ b/Makefile.mesh @@ -41,7 +41,8 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \ mesh/prv-beacon.h mesh/prvbeac-server.c \ mesh/mesh-defs.h \ mesh/gatt-service.h mesh/gatt-service.c \ - mesh/gatt-proxy-svc.h mesh/gatt-proxy-svc.c + mesh/gatt-proxy-svc.h mesh/gatt-proxy-svc.c \ + mesh/proxy-cfg.h mesh/proxy-cfg.c pkglibexec_PROGRAMS += mesh/bluetooth-meshd mesh/mesh.$(OBJEXT): ell/internal diff --git a/mesh/crypto.c b/mesh/crypto.c index ab44baee3427..93c3af751e80 100644 --- a/mesh/crypto.c +++ b/mesh/crypto.c @@ -591,7 +591,7 @@ bool mesh_crypto_packet_build(bool ctl, uint8_t ttl, return true; } -static bool network_header_parse(const uint8_t *packet, uint8_t packet_len, +bool mesh_crypto_network_header_parse(const uint8_t *packet, uint8_t packet_len, bool *ctl, uint8_t *ttl, uint32_t *seq, uint16_t *src, uint16_t *dst) { @@ -631,7 +631,7 @@ bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len, uint16_t this_dst; bool is_segmented; - if (!network_header_parse(packet, packet_len, + if (!mesh_crypto_network_header_parse(packet, packet_len, ctl, ttl, seq, src, &this_dst)) return false; @@ -836,7 +836,7 @@ bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len, uint16_t src; uint16_t dst; - if (!network_header_parse(packet, packet_len, + if (!mesh_crypto_network_header_parse(packet, packet_len, &ctl, &ttl, &seq, &src, &dst)) return false; diff --git a/mesh/crypto.h b/mesh/crypto.h index 55789886eb0e..d66248f84cff 100644 --- a/mesh/crypto.h +++ b/mesh/crypto.h @@ -77,6 +77,9 @@ bool mesh_crypto_packet_parse(const uint8_t *packet, uint8_t packet_len, bool *szmic, bool *relay, uint16_t *seqZero, uint8_t *segO, uint8_t *segN, const uint8_t **payload, uint8_t *payload_len); +bool mesh_crypto_network_header_parse(const uint8_t *packet, uint8_t packet_len, + bool *ctl, uint8_t *ttl, uint32_t *seq, + uint16_t *src, uint16_t *dst); bool mesh_crypto_payload_encrypt(uint8_t *aad, const uint8_t *payload, uint8_t *out, uint16_t payload_len, uint16_t src, uint16_t dst, uint8_t key_aid, diff --git a/mesh/net-keys.c b/mesh/net-keys.c index 62ea4208af98..4b2e14cabb01 100644 --- a/mesh/net-keys.c +++ b/mesh/net-keys.c @@ -76,6 +76,15 @@ struct net_key { bool ivu; }; +struct proxy_cfg_msg { + const uint8_t *data; + uint8_t len; + uint8_t *plain; + uint8_t plain_len; + uint32_t iv_index; + uint32_t key_id; +}; + static struct l_queue *beacons; static struct l_queue *keys; static uint32_t last_flooding_id; @@ -249,13 +258,36 @@ static void decrypt_net_pkt(void *a, void *b) if (result) { cache_id = key->id; - if (cache_plain[1] & 0x80) + if (cache_plain[1] & CTL) cache_plainlen = cache_len - 8; else cache_plainlen = cache_len - 4; } } +static void decrypt_proxy_cfg_msg(void *a, void *b) +{ + const struct net_key *key = a; + struct proxy_cfg_msg *proxy_cfg = b; + bool result; + + if (proxy_cfg->key_id || !key->ref_cnt || + (proxy_cfg->data[0] & 0x7f) != key->nid) + return; + + result = mesh_crypto_packet_decode(proxy_cfg->data, proxy_cfg->len, + true, + proxy_cfg->plain, + proxy_cfg->iv_index, + key->enc_key, + key->prv_key); + + if (result) { + proxy_cfg->key_id = key->id; + proxy_cfg->plain_len = proxy_cfg->len - 8; + } +} + uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len, uint8_t **plain, size_t *plain_len) { @@ -285,6 +317,30 @@ done: return cache_id; } +uint32_t net_key_decrypt_proxy_cfg_msg(uint32_t iv_index, + const uint8_t *pkt, size_t len, + uint8_t *plain, size_t *plain_len) +{ + struct proxy_cfg_msg proxy_cfg = { + .data = pkt, + .len = len, + .plain = plain, + .iv_index = iv_index, + }; + + /* MshPRT_v1.1, section 6.6: Proxy configuration messages have CTL=1 */ + if (!(pkt[1] & CTL)) + return 0; + + /* Try all network keys known to us */ + l_queue_foreach(keys, decrypt_proxy_cfg_msg, &proxy_cfg); + + if (proxy_cfg.key_id) + *plain_len = proxy_cfg.plain_len; + + return proxy_cfg.key_id; +} + bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); diff --git a/mesh/net-keys.h b/mesh/net-keys.h index 5a9d7868ab48..141543feace2 100644 --- a/mesh/net-keys.h +++ b/mesh/net-keys.h @@ -23,6 +23,9 @@ uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd, void net_key_unref(uint32_t id); uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len, uint8_t **plain, size_t *plain_len); +uint32_t net_key_decrypt_proxy_cfg_msg(uint32_t iv_index, + const uint8_t *pkt, size_t len, + uint8_t *plain, size_t *plain_len); bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len); uint32_t net_key_network_id(const uint8_t network[8]); uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi, diff --git a/mesh/net.c b/mesh/net.c index a64cfbde6f4e..141ec4f5acb1 100644 --- a/mesh/net.c +++ b/mesh/net.c @@ -25,6 +25,7 @@ #include "mesh/net-keys.h" #include "mesh/node.h" #include "mesh/net.h" +#include "mesh/proxy-cfg.h" #include "mesh/mesh-io.h" #include "mesh/friend.h" #include "mesh/gatt-service.h" // PROXY_MSG_TYPE_NETWORK_PDU @@ -2352,7 +2353,7 @@ static enum _relay_advice packet_received(struct mesh_net *net, return RELAY_NONE; } - if (net_dst == 0) { + if (net_dst == UNASSIGNED_ADDRESS) { l_error("illegal parms: DST: %4.4x Ctl: %d TTL: %2.2x", net_dst, net_ctl, net_ttl); return RELAY_NONE; @@ -2523,6 +2524,34 @@ static void net_rx(void *net_ptr, void *user_data) } } +static void net_proxy_cfg_msg_rx(void *net_ptr, void *user_data) +{ + struct net_queue_data *data = user_data; + struct mesh_net *net = net_ptr; + uint8_t out[29]; + size_t out_size; + uint32_t net_key_id; + bool ivi_net = !!(net->iv_index & 1); + bool ivi_pkt = !!(data->data[0] & 0x80); + + /* if IVI flag differs, use previous IV Index */ + uint32_t iv_index = net->iv_index - (ivi_pkt ^ ivi_net); + + net_key_id = net_key_decrypt_proxy_cfg_msg(iv_index, + data->data, data->len, + out, &out_size); + + if (!net_key_id) + return; + + if (!data->seen) { + data->seen = true; + print_packet("RX: ProxyCfg [enc] :", data->data, data->len); + } + + proxy_cfg_msg_received(net, net_key_id, iv_index, out, out_size); +} + static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info, const uint8_t *data, uint16_t len) { @@ -2560,6 +2589,26 @@ static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info, } } +static void +net_proxy_cfg_msg_recv(void *user_data, struct mesh_io_recv_info *info, + const uint8_t *data, uint16_t len) +{ + struct net_queue_data net_data = { + .info = info, + .data = data + 1, + .len = len - 1, + .relay_advice = RELAY_NONE, + .seen = false, + }; + + if (len < 9) + return; + + l_queue_foreach(nets, net_proxy_cfg_msg_rx, &net_data); + + /* Proxy configuration messages are not relayed */ +} + static void iv_upd_to(struct l_timeout *upd_timeout, void *user_data) { struct mesh_net *net = user_data; @@ -3074,6 +3123,9 @@ void mesh_net_attach_gatt(struct gatt_proxy_service *gatt_proxy) gatt_proxy_service_register_recv_cb(gatt_proxy, PROXY_MSG_TYPE_NETWORK_PDU, net_msg_recv, NULL); + gatt_proxy_service_register_recv_cb(gatt_proxy, + PROXY_MSG_TYPE_PROXY_CFG, + net_proxy_cfg_msg_recv, NULL); } void mesh_net_detach_gatt(struct gatt_proxy_service *gatt_proxy) @@ -3082,6 +3134,8 @@ void mesh_net_detach_gatt(struct gatt_proxy_service *gatt_proxy) gatt_proxy_service_deregister_recv_cb(gatt_proxy, PROXY_MSG_TYPE_NETWORK_PDU); + gatt_proxy_service_deregister_recv_cb(gatt_proxy, + PROXY_MSG_TYPE_PROXY_CFG); } bool mesh_net_iv_index_update(struct mesh_net *net) diff --git a/mesh/proxy-cfg.c b/mesh/proxy-cfg.c new file mode 100644 index 000000000000..2cb23e0cc05b --- /dev/null +++ b/mesh/proxy-cfg.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2025 ARRI Lighting. All rights reserved. + * + * + */ + +#include <stdbool.h> + +#include <ell/dbus.h> +#include <ell/log.h> +#include <ell/util.h> // l_put_be16() + +#include "mesh/crypto.h" // mesh_crypto_network_header_parse() +#include "mesh/mesh-io.h" // mesh_io_recv_func_t, required by gatt-proxy-svc.h +#include "mesh/gatt-service.h" // PROXY_MSG_TYPE_PROXY_CFG +#include "mesh/gatt-proxy-svc.h" // gatt_proxy_service_send() +#include "mesh/mesh-defs.h" // UNASSIGNED_ADDRESS +#include "mesh/net.h" // mesh_net_next_seq_num(), + // mesh_net_get_address() +#include "mesh/net-keys.h" // net_key_encrypt(), + // net_key_decrypt_proxy_cfg_msg() +#include "mesh/util.h" // print_packet() +#include "mesh/proxy-cfg.h" + +enum proxy_cfg_msg_op { + PROXY_CFG_MSG_OP_FILTER_SET_TYPE = 0x00, + PROXY_CFG_MSG_OP_FILTER_ADDRS_ADD = 0x01, + PROXY_CFG_MSG_OP_FILTER_ADDRS_REM = 0x02, + PROXY_CFG_MSG_OP_FILTER_STATUS = 0x03, +}; + +enum proxy_filter_type { + PROXY_FILTER_TYPE_ACCEPT_LIST = 0x00, + PROXY_FILTER_TYPE_REJECT_LIST = 0x00, +}; + +static enum proxy_filter_type filter_type = PROXY_FILTER_TYPE_ACCEPT_LIST; +static uint8_t num_filters = 0; + +void proxy_cfg_msg_received(struct mesh_net *net, uint32_t net_key_id, + uint32_t iv_index, + const uint8_t *data, uint8_t size) +{ + const uint8_t *msg; + uint8_t cfg_msg_len; + uint8_t net_ttl; + uint32_t net_seq; + uint16_t net_src, net_dst; + bool net_ctl; + uint8_t rsp[10]; + uint8_t rsp_len = 0; + + print_packet("RX: ProxyCfg [clr] :", data, size); + + if (!mesh_crypto_network_header_parse(data, size, &net_ctl, &net_ttl, + &net_seq, &net_src, &net_dst)) { + l_error("Failed to parse packet content"); + return; + } + + /* + * MshPRT_v1.1, section 6.6: + * - The CTL field shall be set to 1. [already checked] + * - The TTL field shall be set to 0. + * - The DST field shall be set to the unassigned address. + */ + if (net_dst != UNASSIGNED_ADDRESS || net_ttl) { + l_error("illegal parms: DST: %4.4x Ctl: %d TTL: %2.2x", + net_dst, net_ctl, net_ttl); + return; + } + + l_debug("RX: ProxyCfg %04x -> %04x : TTL 0x%02x : IV : %8.8x SEQ 0x%06x", + net_src, net_dst, net_ttl, iv_index, net_seq); + + msg = data + 9; + cfg_msg_len = size - 9; + + if (!cfg_msg_len) + return; + + /* process request */ + switch (msg[0]) { + case PROXY_CFG_MSG_OP_FILTER_SET_TYPE: + if (cfg_msg_len > 1) { + filter_type = msg[1]; + num_filters = 0; + } + break; + + case PROXY_CFG_MSG_OP_FILTER_ADDRS_ADD: + if (cfg_msg_len & 0x1) + num_filters += (cfg_msg_len - 1) / 2; + break; + + case PROXY_CFG_MSG_OP_FILTER_ADDRS_REM: + if (cfg_msg_len) + num_filters -= (num_filters >= ((cfg_msg_len - 1) / 2)) ? ((cfg_msg_len - 1) / 2) : num_filters; + break; + + default: + break; + } + + /* prepare response */ + switch (msg[0]) { + case PROXY_CFG_MSG_OP_FILTER_SET_TYPE: + case PROXY_CFG_MSG_OP_FILTER_ADDRS_ADD: + case PROXY_CFG_MSG_OP_FILTER_ADDRS_REM: + rsp[0] = PROXY_CFG_MSG_OP_FILTER_STATUS; + rsp[1] = filter_type; + l_put_be16(num_filters, &rsp[2]); + rsp_len = 4; + break; + + /* + * MshPRT_v1.1, section 6.7: + * Upon receiving a proxy configuration message with the Opcode + * field set to a value that is Reserved for Future Use, the + * Proxy Server shall ignore this message. + */ + default: + break; + } + + if (rsp_len) { + uint8_t pkt[30]; + uint8_t pkt_len; + + net_seq = mesh_net_next_seq_num(net); + if (!mesh_crypto_packet_build(true, 0/*TTL*/, net_seq, + mesh_net_get_address(net)/*src*/, + UNASSIGNED_ADDRESS/*dst*/, rsp[0], + false, 0, false, false, 0, 0, 0, + msg + 1, rsp_len - 1, pkt, &pkt_len)) + return; + + if (!net_key_encrypt(net_key_id, iv_index, pkt, pkt_len)) { + l_error("Failed to encode packet"); + return; + } + + gatt_proxy_service_send(PROXY_MSG_TYPE_PROXY_CFG, pkt, pkt_len); + } +} diff --git a/mesh/proxy-cfg.h b/mesh/proxy-cfg.h new file mode 100644 index 000000000000..658948ae7cbc --- /dev/null +++ b/mesh/proxy-cfg.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2015 ARRI Lighting. All rights reserved. + * + * + */ + +#include <stdint.h> + +struct mesh_net; + +void proxy_cfg_msg_received(struct mesh_net *net, uint32_t net_key_id, + uint32_t iv_index, + const uint8_t *data, uint8_t size); -- 2.43.0