diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index d5bb6ee930e..ffa2336a380 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -186,7 +186,7 @@ config BT_MESH_MSG_CACHE_SIZE config BT_MESH_ADV_BUF_COUNT int "Number of advertising buffers" default 6 - range 3 256 + range 1 256 help Number of advertising buffers available. This should be chosen based on what kind of features the local node should have. E.g. @@ -300,6 +300,13 @@ config BT_MESH_TX_SEG_MAX which leaves 56 bytes for application layer data using a 4-byte MIC and 52 bytes using an 8-byte MIC. +config BT_MESH_LOOPBACK_BUFS + int "Number of loopback buffers" + default 3 + help + The number of buffers allocated for the network loopback mechanism. + Loopback is used when the device sends messages to itself. + config BT_MESH_RELAY bool "Relay support" help diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 5484afa87be..6ace2c51097 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -3535,6 +3535,8 @@ void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store) bt_mesh_friend_clear_net_idx(sub->net_idx); } + bt_mesh_net_loopback_clear(sub->net_idx); + if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) { bt_mesh_clear_subnet(sub); } diff --git a/subsys/bluetooth/mesh/main.c b/subsys/bluetooth/mesh/main.c index bfd963213d5..6acfce2d986 100644 --- a/subsys/bluetooth/mesh/main.c +++ b/subsys/bluetooth/mesh/main.c @@ -178,6 +178,8 @@ void bt_mesh_reset(void) bt_mesh_rx_reset(); bt_mesh_tx_reset(); + bt_mesh_net_loopback_clear(BT_MESH_KEY_ANY); + if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) { if (IS_ENABLED(CONFIG_BT_MESH_LPN_SUB_ALL_NODES_ADDR)) { uint16_t group = BT_MESH_ADDR_ALL_NODES; diff --git a/subsys/bluetooth/mesh/net.c b/subsys/bluetooth/mesh/net.c index 20feabbcf3b..c8f6368e41e 100644 --- a/subsys/bluetooth/mesh/net.c +++ b/subsys/bluetooth/mesh/net.c @@ -45,6 +45,10 @@ */ #define BT_MESH_NET_MIN_PDU_LEN (BT_MESH_NET_HDR_LEN + 1 + 8) +#define LOOPBACK_MAX_PDU_LEN (BT_MESH_NET_HDR_LEN + 16) +#define LOOPBACK_USER_DATA_SIZE sizeof(struct bt_mesh_subnet *) +#define LOOPBACK_BUF_SUB(buf) (*(struct bt_mesh_subnet **)net_buf_user_data(buf)) + /* Seq limit after IV Update is triggered */ #define IV_UPDATE_SEQ_LIMIT 8000000 @@ -88,6 +92,9 @@ struct bt_mesh_net bt_mesh = { }, }; +NET_BUF_POOL_DEFINE(loopback_buf_pool, CONFIG_BT_MESH_LOOPBACK_BUFS, + LOOPBACK_MAX_PDU_LEN, LOOPBACK_USER_DATA_SIZE, NULL); + static uint32_t dup_cache[CONFIG_BT_MESH_MSG_CACHE_SIZE]; static int dup_cache_next; @@ -111,8 +118,7 @@ static bool check_dup(struct net_buf_simple *data) return false; } -static bool msg_cache_match(struct bt_mesh_net_rx *rx, - struct net_buf_simple *pdu) +static bool msg_cache_match(struct net_buf_simple *pdu) { uint16_t i; @@ -733,19 +739,63 @@ static void bt_mesh_net_local(struct k_work *work) struct net_buf *buf; while ((buf = net_buf_slist_get(&bt_mesh.local_queue))) { - BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len)); - bt_mesh_net_recv(&buf->b, 0, BT_MESH_NET_IF_LOCAL); + struct bt_mesh_subnet *sub = LOOPBACK_BUF_SUB(buf); + struct bt_mesh_net_rx rx = { + .ctx = { + .net_idx = sub->net_idx, + /* Initialize AppIdx to a sane value */ + .app_idx = BT_MESH_KEY_UNUSED, + .recv_ttl = TTL(buf->data), + /* TTL=1 only goes to local IF */ + .send_ttl = 1U, + .addr = SRC(buf->data), + .recv_dst = DST(buf->data), + .recv_rssi = 0, + }, + .net_if = BT_MESH_NET_IF_LOCAL, + .sub = sub, + .old_iv = (IVI(buf->data) != (bt_mesh.iv_index & 0x01)), + .ctl = CTL(buf->data), + .seq = SEQ(buf->data), + .new_key = sub->kr_flag, + .local_match = 1U, + .friend_match = 0U, + }; + + BT_DBG("src: 0x%04x dst: 0x%04x seq 0x%06x sub %p", rx.ctx.addr, + rx.ctx.addr, rx.seq, sub); + + (void) bt_mesh_trans_recv(&buf->b, &rx); net_buf_unref(buf); } } -int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf, - bool proxy) +static void net_tx_cred_get(struct bt_mesh_net_tx *tx, uint8_t *nid, + const uint8_t **enc, const uint8_t **priv) +{ + if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) || !tx->friend_cred) { + tx->friend_cred = 0U; + *nid = tx->sub->keys[tx->sub->kr_flag].nid; + *enc = tx->sub->keys[tx->sub->kr_flag].enc; + *priv = tx->sub->keys[tx->sub->kr_flag].privacy; + return; + } + + if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED, nid, enc, priv)) { + BT_WARN("Falling back to master credentials"); + + tx->friend_cred = 0U; + + *nid = tx->sub->keys[tx->sub->kr_flag].nid; + *enc = tx->sub->keys[tx->sub->kr_flag].enc; + *priv = tx->sub->keys[tx->sub->kr_flag].privacy; + } +} + +static int net_header_encode(struct bt_mesh_net_tx *tx, uint8_t nid, + struct net_buf_simple *buf) { const bool ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED); - uint8_t nid; - const uint8_t *enc, *priv; - int err; if (ctl && net_buf_simple_tailroom(buf) < 8) { BT_ERR("Insufficient MIC space for CTL PDU"); @@ -768,26 +818,25 @@ int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf, net_buf_simple_push_u8(buf, tx->ctx->send_ttl); } - if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) && tx->friend_cred) { - if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED, - &nid, &enc, &priv)) { - BT_WARN("Falling back to master credentials"); - - tx->friend_cred = 0U; - - nid = tx->sub->keys[tx->sub->kr_flag].nid; - enc = tx->sub->keys[tx->sub->kr_flag].enc; - priv = tx->sub->keys[tx->sub->kr_flag].privacy; - } - } else { - tx->friend_cred = 0U; - nid = tx->sub->keys[tx->sub->kr_flag].nid; - enc = tx->sub->keys[tx->sub->kr_flag].enc; - priv = tx->sub->keys[tx->sub->kr_flag].privacy; - } - net_buf_simple_push_u8(buf, (nid | (BT_MESH_NET_IVI_TX & 1) << 7)); + return 0; +} + +int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf, + bool proxy) +{ + const uint8_t *enc, *priv; + uint8_t nid; + int err; + + net_tx_cred_get(tx, &nid, &enc, &priv); + + err = net_header_encode(tx, nid, buf); + if (err) { + return err; + } + err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, proxy); if (err) { return err; @@ -796,9 +845,35 @@ int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf, return bt_mesh_net_obfuscate(buf->data, BT_MESH_NET_IVI_TX, priv); } +static int loopback(const struct bt_mesh_net_tx *tx, const uint8_t *data, + size_t len) +{ + struct net_buf *buf; + + buf = net_buf_alloc(&loopback_buf_pool, K_NO_WAIT); + if (!buf) { + BT_WARN("Unable to allocate loopback"); + return -ENOMEM; + } + + BT_DBG(""); + + LOOPBACK_BUF_SUB(buf) = tx->sub; + + net_buf_add_mem(buf, data, len); + + net_buf_slist_put(&bt_mesh.local_queue, buf); + + k_work_submit(&bt_mesh.local_work); + + return 0; +} + int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf, const struct bt_mesh_send_cb *cb, void *cb_data) { + const uint8_t *enc, *priv; + uint8_t nid; int err; BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu", @@ -811,54 +886,89 @@ int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf, tx->ctx->send_ttl = bt_mesh_default_ttl_get(); } - err = bt_mesh_net_encode(tx, &buf->b, false); + net_tx_cred_get(tx, &nid, &enc, &priv); + err = net_header_encode(tx, nid, &buf->b); if (err) { goto done; } - /* Deliver to GATT Proxy Clients if necessary. Mesh spec 3.4.5.2: - * "The output filter of the interface connected to advertising or - * GATT bearers shall drop all messages with TTL value set to 1." - */ - if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && - tx->ctx->send_ttl != 1U) { - if (bt_mesh_proxy_relay(&buf->b, tx->ctx->addr) && - BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { - /* Notify completion if this only went - * through the Mesh Proxy. - */ - send_cb_finalize(cb, cb_data); - - err = 0; - goto done; - } - } - /* Deliver to local network interface if necessary */ if (bt_mesh_fixed_group_match(tx->ctx->addr) || bt_mesh_elem_find(tx->ctx->addr)) { - if (cb && cb->start) { - cb->start(0, 0, cb_data); + err = loopback(tx, buf->data, buf->len); + + /* Local unicast messages should not go out to network */ + if (BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr) || + tx->ctx->send_ttl == 1U) { + if (!err) { + send_cb_finalize(cb, cb_data); + } + + goto done; } - net_buf_slist_put(&bt_mesh.local_queue, net_buf_ref(buf)); - if (cb && cb->end) { - cb->end(0, cb_data); - } - k_work_submit(&bt_mesh.local_work); - } else if (tx->ctx->send_ttl != 1U) { - /* Deliver to to the advertising network interface. Mesh spec - * 3.4.5.2: "The output filter of the interface connected to - * advertising or GATT bearers shall drop all messages with - * TTL value set to 1." - */ - bt_mesh_adv_send(buf, cb, cb_data); } + /* Mesh spec 3.4.5.2: "The output filter of the interface connected to + * advertising or GATT bearers shall drop all messages with TTL value + * set to 1." If a TTL=1 packet wasn't for a local interface, it is + * invalid. + */ + if (tx->ctx->send_ttl == 1U) { + err = -EINVAL; + goto done; + } + + err = bt_mesh_net_encrypt(enc, &buf->b, BT_MESH_NET_IVI_TX, false); + if (err) { + goto done; + } + + err = bt_mesh_net_obfuscate(buf->data, BT_MESH_NET_IVI_TX, priv); + if (err) { + goto done; + } + + /* Deliver to GATT Proxy Clients if necessary. */ + if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && + bt_mesh_proxy_relay(&buf->b, tx->ctx->addr) && + BT_MESH_ADDR_IS_UNICAST(tx->ctx->addr)) { + /* Notify completion if this only went through the Mesh Proxy */ + send_cb_finalize(cb, cb_data); + + err = 0; + goto done; + } + + bt_mesh_adv_send(buf, cb, cb_data); + done: net_buf_unref(buf); return err; } +void bt_mesh_net_loopback_clear(uint16_t net_idx) +{ + sys_slist_t new_list; + struct net_buf *buf; + + BT_DBG("0x%04x", net_idx); + + sys_slist_init(&new_list); + + while ((buf = net_buf_slist_get(&bt_mesh.local_queue))) { + struct bt_mesh_subnet *sub = LOOPBACK_BUF_SUB(buf); + + if (net_idx == BT_MESH_KEY_ANY || net_idx == sub->net_idx) { + BT_DBG("Dropped 0x%06x", SEQ(buf->data)); + net_buf_unref(buf); + } else { + net_buf_slist_put(&new_list, buf); + } + } + + bt_mesh.local_queue = new_list; +} + static bool auth_match(struct bt_mesh_subnet_keys *keys, const uint8_t net_id[8], uint8_t flags, uint32_t iv_index, const uint8_t auth[8]) @@ -935,7 +1045,12 @@ static int net_decrypt(struct bt_mesh_subnet *sub, const uint8_t *enc, return -EINVAL; } - if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(rx, buf)) { + if (bt_mesh_elem_find(rx->ctx.addr)) { + BT_DBG("Dropping locally originated packet"); + return -EBADMSG; + } + + if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(buf)) { BT_DBG("Duplicate found in Network Message Cache"); return -EALREADY; } @@ -1044,8 +1159,6 @@ static bool net_find_and_decrypt(const uint8_t *data, size_t data_len, static bool relay_to_adv(enum bt_mesh_net_if net_if) { switch (net_if) { - case BT_MESH_NET_IF_LOCAL: - return true; case BT_MESH_NET_IF_ADV: return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED); case BT_MESH_NET_IF_PROXY: @@ -1062,20 +1175,8 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, struct net_buf *buf; uint8_t nid, transmit; - if (rx->net_if == BT_MESH_NET_IF_LOCAL) { - /* Locally originated PDUs with TTL=1 will only be delivered - * to local elements as per Mesh Profile 1.0 section 3.4.5.2: - * "The output filter of the interface connected to - * advertising or GATT bearers shall drop all messages with - * TTL value set to 1." - */ - if (rx->ctx.recv_ttl == 1U) { - return; - } - } else { - if (rx->ctx.recv_ttl <= 1U) { - return; - } + if (rx->ctx.recv_ttl <= 1U) { + return; } if (rx->net_if == BT_MESH_NET_IF_ADV && @@ -1103,12 +1204,9 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, return; } - /* Only decrement TTL for non-locally originated packets */ - if (rx->net_if != BT_MESH_NET_IF_LOCAL) { - /* Leave CTL bit intact */ - sbuf->data[1] &= 0x80; - sbuf->data[1] |= rx->ctx.recv_ttl - 1U; - } + /* Leave CTL bit intact */ + sbuf->data[1] &= 0x80; + sbuf->data[1] |= rx->ctx.recv_ttl - 1U; net_buf_add_mem(buf, sbuf->data, sbuf->len); @@ -1139,11 +1237,10 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, } /* Sending to the GATT bearer should only happen if GATT Proxy - * is enabled or the message originates from the local node. + * is enabled. */ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && - (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || - rx->net_if == BT_MESH_NET_IF_LOCAL)) { + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { if (bt_mesh_proxy_relay(&buf->b, rx->ctx.recv_dst) && BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) { goto done; @@ -1215,11 +1312,6 @@ int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if, return -EBADMSG; } - if (net_if != BT_MESH_NET_IF_LOCAL && bt_mesh_elem_find(rx->ctx.addr)) { - BT_DBG("Dropping locally originated packet"); - return -EBADMSG; - } - BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst, rx->ctx.recv_ttl); BT_DBG("PDU: %s", bt_hex(buf->data, buf->len)); diff --git a/subsys/bluetooth/mesh/net.h b/subsys/bluetooth/mesh/net.h index 5294b91ad85..105cf91d948 100644 --- a/subsys/bluetooth/mesh/net.h +++ b/subsys/bluetooth/mesh/net.h @@ -342,6 +342,8 @@ int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if, void bt_mesh_net_recv(struct net_buf_simple *data, int8_t rssi, enum bt_mesh_net_if net_if); +void bt_mesh_net_loopback_clear(uint16_t net_idx); + uint32_t bt_mesh_next_seq(void); void bt_mesh_net_start(void);