blob: 8cd98f4576711fc1d4845ac18a08721816b23e87 [file] [log] [blame]
/* Bluetooth Mesh */
/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2021 Lingao Meng
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <sys/byteorder.h>
#include <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/gatt.h>
#include <bluetooth/mesh.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_PROXY)
#define LOG_MODULE_NAME bt_mesh_proxy
#include "common/log.h"
#include "mesh.h"
#include "adv.h"
#include "net.h"
#include "rpl.h"
#include "transport.h"
#include "prov.h"
#include "beacon.h"
#include "foundation.h"
#include "access.h"
#include "proxy.h"
#include "proxy_msg.h"
#define PDU_SAR(data) (data[0] >> 6)
/* Mesh Profile 1.0 Section 6.6:
* "The timeout for the SAR transfer is 20 seconds. When the timeout
* expires, the Proxy Server shall disconnect."
*/
#define PROXY_SAR_TIMEOUT K_SECONDS(20)
#define SAR_COMPLETE 0x00
#define SAR_FIRST 0x01
#define SAR_CONT 0x02
#define SAR_LAST 0x03
#define PDU_HDR(sar, type) (sar << 6 | (type & BIT_MASK(6)))
static void proxy_sar_timeout(struct k_work *work)
{
struct bt_mesh_proxy_role *role;
BT_WARN("Proxy SAR timeout");
role = CONTAINER_OF(work, struct bt_mesh_proxy_role, sar_timer);
if (role->conn) {
bt_conn_disconnect(role->conn,
BT_HCI_ERR_REMOTE_USER_TERM_CONN);
}
}
#if defined(CONFIG_BT_MESH_PROXY)
static void proxy_cfg(struct bt_mesh_proxy_role *role)
{
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_NET_MAX_PDU_LEN);
struct bt_mesh_net_rx rx;
int err;
err = bt_mesh_net_decode(&role->buf, BT_MESH_NET_IF_PROXY_CFG,
&rx, &buf);
if (err) {
BT_ERR("Failed to decode Proxy Configuration (err %d)", err);
return;
}
rx.local_match = 1U;
if (bt_mesh_rpl_check(&rx, NULL)) {
BT_WARN("Replay: src 0x%04x dst 0x%04x seq 0x%06x",
rx.ctx.addr, rx.ctx.recv_dst, rx.seq);
return;
}
/* Remove network headers */
net_buf_simple_pull(&buf, BT_MESH_NET_HDR_LEN);
BT_DBG("%u bytes: %s", buf.len, bt_hex(buf.data, buf.len));
if (buf.len < 1) {
BT_WARN("Too short proxy configuration PDU");
return;
}
role->cb.recv(role->conn, &rx, &buf);
}
#endif
static void proxy_complete_pdu(struct bt_mesh_proxy_role *role)
{
switch (role->msg_type) {
#if defined(CONFIG_BT_MESH_PROXY)
case BT_MESH_PROXY_NET_PDU:
BT_DBG("Mesh Network PDU");
bt_mesh_net_recv(&role->buf, 0, BT_MESH_NET_IF_PROXY);
break;
case BT_MESH_PROXY_BEACON:
BT_DBG("Mesh Beacon PDU");
bt_mesh_beacon_recv(&role->buf);
break;
case BT_MESH_PROXY_CONFIG:
BT_DBG("Mesh Configuration PDU");
proxy_cfg(role);
break;
#endif
#if defined(CONFIG_BT_MESH_PB_GATT)
case BT_MESH_PROXY_PROV:
BT_DBG("Mesh Provisioning PDU");
bt_mesh_pb_gatt_recv(role->conn, &role->buf);
break;
#endif
default:
BT_WARN("Unhandled Message Type 0x%02x", role->msg_type);
break;
}
net_buf_simple_reset(&role->buf);
}
ssize_t bt_mesh_proxy_msg_recv(struct bt_mesh_proxy_role *role,
const void *buf, uint16_t len)
{
const uint8_t *data = buf;
switch (PDU_SAR(data)) {
case SAR_COMPLETE:
if (role->buf.len) {
BT_WARN("Complete PDU while a pending incomplete one");
return -EINVAL;
}
role->msg_type = PDU_TYPE(data);
net_buf_simple_add_mem(&role->buf, data + 1, len - 1);
proxy_complete_pdu(role);
break;
case SAR_FIRST:
if (role->buf.len) {
BT_WARN("First PDU while a pending incomplete one");
return -EINVAL;
}
k_work_reschedule(&role->sar_timer, PROXY_SAR_TIMEOUT);
role->msg_type = PDU_TYPE(data);
net_buf_simple_add_mem(&role->buf, data + 1, len - 1);
break;
case SAR_CONT:
if (!role->buf.len) {
BT_WARN("Continuation with no prior data");
return -EINVAL;
}
if (role->msg_type != PDU_TYPE(data)) {
BT_WARN("Unexpected message type in continuation");
return -EINVAL;
}
k_work_reschedule(&role->sar_timer, PROXY_SAR_TIMEOUT);
net_buf_simple_add_mem(&role->buf, data + 1, len - 1);
break;
case SAR_LAST:
if (!role->buf.len) {
BT_WARN("Last SAR PDU with no prior data");
return -EINVAL;
}
if (role->msg_type != PDU_TYPE(data)) {
BT_WARN("Unexpected message type in last SAR PDU");
return -EINVAL;
}
/* If this fails, the work handler exits early, as there's no
* active SAR buffer.
*/
(void)k_work_cancel_delayable(&role->sar_timer);
net_buf_simple_add_mem(&role->buf, data + 1, len - 1);
proxy_complete_pdu(role);
break;
}
return len;
}
int bt_mesh_proxy_msg_send(struct bt_mesh_proxy_role *role, uint8_t type,
struct net_buf_simple *msg,
bt_gatt_complete_func_t end, void *user_data)
{
int err;
uint16_t mtu;
struct bt_conn *conn = role->conn;
BT_DBG("conn %p type 0x%02x len %u: %s", (void *)conn, type, msg->len,
bt_hex(msg->data, msg->len));
/* ATT_MTU - OpCode (1 byte) - Handle (2 bytes) */
mtu = bt_gatt_get_mtu(conn) - 3;
if (mtu > msg->len) {
net_buf_simple_push_u8(msg, PDU_HDR(SAR_COMPLETE, type));
return role->cb.send(conn, msg->data, msg->len, end, user_data);
}
net_buf_simple_push_u8(msg, PDU_HDR(SAR_FIRST, type));
err = role->cb.send(conn, msg->data, mtu, NULL, NULL);
if (err) {
return err;
}
net_buf_simple_pull(msg, mtu);
while (msg->len) {
if (msg->len + 1 < mtu) {
net_buf_simple_push_u8(msg, PDU_HDR(SAR_LAST, type));
err = role->cb.send(conn, msg->data, msg->len, end, user_data);
if (err) {
return err;
}
break;
}
net_buf_simple_push_u8(msg, PDU_HDR(SAR_CONT, type));
err = role->cb.send(conn, msg->data, mtu, NULL, NULL);
if (err) {
return err;
}
net_buf_simple_pull(msg, mtu);
}
return 0;
}
void bt_mesh_proxy_msg_init(struct bt_mesh_proxy_role *role)
{
k_work_init_delayable(&role->sar_timer, proxy_sar_timeout);
}