in nimble/host/mesh/src/friend.c [956:1058]
int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct os_mbuf *buf)
{
struct bt_mesh_ctl_friend_req *msg = (void *)buf->om_data;
struct bt_mesh_friend *frnd = NULL;
uint32_t poll_to;
int32_t delay;
int i, err;
if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
BT_WARN("Ignoring Friend request from local interface");
return 0;
}
if (buf->om_len < sizeof(*msg)) {
BT_WARN("Too short Friend Request");
return -EINVAL;
}
if (msg->recv_delay <= 0x09) {
BT_WARN("Prohibited ReceiveDelay (0x%02x)", msg->recv_delay);
return -EINVAL;
}
poll_to = sys_get_be24(msg->poll_to);
if (poll_to <= 0x000009 || poll_to >= 0x34bc00) {
BT_WARN("Prohibited PollTimeout (0x%06x)", (unsigned) poll_to);
return -EINVAL;
}
if (msg->num_elem == 0x00) {
BT_WARN("Prohibited NumElements value (0x00)");
return -EINVAL;
}
if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr + msg->num_elem - 1)) {
BT_WARN("LPN elements stretch outside of unicast range");
return -EINVAL;
}
if (!MIN_QUEUE_SIZE_LOG(msg->criteria)) {
BT_WARN("Prohibited Minimum Queue Size in Friend Request");
return -EINVAL;
}
if (CONFIG_BT_MESH_FRIEND_QUEUE_SIZE < MIN_QUEUE_SIZE(msg->criteria)) {
BT_WARN("We have a too small Friend Queue size (%u < %u)",
CONFIG_BT_MESH_FRIEND_QUEUE_SIZE,
(unsigned) MIN_QUEUE_SIZE(msg->criteria));
return 0;
}
frnd = bt_mesh_friend_find(rx->sub->net_idx, rx->ctx.addr, true, false);
if (frnd) {
BT_WARN("Existing LPN re-requesting Friendship");
friend_clear(frnd);
goto init_friend;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
if (!bt_mesh.frnd[i].subnet) {
frnd = &bt_mesh.frnd[i];
break;
}
}
if (!frnd) {
BT_WARN("No free Friend contexts for new LPN");
return -ENOMEM;
}
init_friend:
frnd->lpn = rx->ctx.addr;
frnd->num_elem = msg->num_elem;
frnd->subnet = rx->sub;
frnd->recv_delay = msg->recv_delay;
frnd->poll_to = poll_to * 100;
frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr);
err = friend_cred_create(frnd, SUBNET_KEY_TX_IDX(frnd->subnet));
if (err) {
BT_ERR("Failed to create friend credentials");
friend_clear(frnd);
return -EIO;
}
BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums",
frnd->lpn, rx->ctx.recv_rssi, frnd->recv_delay,
(unsigned) frnd->poll_to);
if (BT_MESH_ADDR_IS_UNICAST(frnd->clear.frnd) &&
!bt_mesh_has_addr(frnd->clear.frnd)) {
clear_procedure_start(frnd);
}
delay = offer_delay(frnd, rx->ctx.recv_rssi, msg->criteria);
k_work_reschedule(&frnd->timer, K_MSEC(delay));
enqueue_offer(frnd, rx->ctx.recv_rssi);
return 0;
}