Sfoglia il codice sorgente

fix: Align channel data framing

ref: #1928
Janez T 4 mesi fa
parent
commit
f25d7a882a
5 ha cambiato i file con 63 aggiunte e 56 eliminazioni
  1. 17 25
      docs/companion_protocol.md
  2. 16 17
      examples/companion_radio/MyMesh.cpp
  3. 1 1
      src/MeshCore.h
  4. 1 1
      src/Packet.h
  5. 28 12
      src/helpers/BaseChatMesh.cpp

+ 17 - 25
docs/companion_protocol.md

@@ -303,7 +303,7 @@ Bytes 7+: Binary payload bytes (variable length)
 - Values other than `0xFF` are reserved for official protocol extensions.
 
 **Limits**:
-- Maximum payload length is `163` bytes (`MAX_GROUP_DATA_LENGTH`).
+- Maximum payload length is `160` bytes.
 - Larger payloads are rejected with `PACKET_ERROR` / `ERR_CODE_ILLEGAL_ARG`.
 
 **Response**: `PACKET_OK` (0x00) on success
@@ -326,7 +326,7 @@ Byte 0: 0x0A
 
 **Response**: 
 - `PACKET_CHANNEL_MSG_RECV` (0x08) or `PACKET_CHANNEL_MSG_RECV_V3` (0x11) for channel messages
-- `PACKET_CHANNEL_DATA_RECV` (0x1B) or `PACKET_CHANNEL_DATA_RECV_V3` (0x1C) for channel data
+- `PACKET_CHANNEL_DATA_RECV` (0x1B) for channel data
 - `PACKET_CONTACT_MSG_RECV` (0x07) or `PACKET_CONTACT_MSG_RECV_V3` (0x10) for contact messages
 - `PACKET_NO_MORE_MSGS` (0x0A) if no messages available
 
@@ -397,8 +397,7 @@ Messages are received via the TX characteristic (notifications). The device send
    - `PACKET_CHANNEL_MSG_RECV_V3` (0x11) - Version 3 with SNR
 
 2. **Channel Data**:
-   - `PACKET_CHANNEL_DATA_RECV` (0x1B) - Standard format
-   - `PACKET_CHANNEL_DATA_RECV_V3` (0x1C) - Version 3 with SNR
+   - `PACKET_CHANNEL_DATA_RECV` (0x1B) - Includes SNR and reserved bytes
 
 3. **Contact Messages**:
    - `PACKET_CONTACT_MSG_RECV` (0x07) - Standard format
@@ -502,26 +501,17 @@ Bytes 11+: Payload bytes
 
 ### Channel Data Format
 
-**Standard Format** (`PACKET_CHANNEL_DATA_RECV`, 0x1B):
+**Format** (`PACKET_CHANNEL_DATA_RECV`, 0x1B):
 ```
 Byte 0: 0x1B (packet type)
-Byte 1: Channel Index (0-7)
-Byte 2: Path Length
-Byte 3: Data Type
-Bytes 4-7: Timestamp (32-bit little-endian)
-Bytes 8+: Payload bytes
-```
-
-**V3 Format** (`PACKET_CHANNEL_DATA_RECV_V3`, 0x1C):
-```
-Byte 0: 0x1C (packet type)
 Byte 1: SNR (signed byte, multiplied by 4)
 Bytes 2-3: Reserved
 Byte 4: Channel Index (0-7)
 Byte 5: Path Length
 Byte 6: Data Type
-Bytes 7-10: Timestamp (32-bit little-endian)
-Bytes 11+: Payload bytes
+Byte 7: Data Length
+Bytes 8-11: Timestamp (32-bit little-endian)
+Bytes 12+: Payload bytes
 ```
 
 **Parsing Pseudocode**:
@@ -529,9 +519,10 @@ Bytes 11+: Payload bytes
 def parse_channel_frame(data):
     packet_type = data[0]
     offset = 1
+    snr = None
     
-    # Check for V3 format
-    if packet_type in (0x11, 0x1C):  # V3
+    # Formats with explicit SNR/reserved bytes
+    if packet_type in (0x11, 0x1B):
         snr_byte = data[offset]
         snr = ((snr_byte if snr_byte < 128 else snr_byte - 256) / 4.0)
         offset += 3  # Skip SNR + reserved
@@ -539,8 +530,10 @@ def parse_channel_frame(data):
     channel_idx = data[offset]
     path_len = data[offset + 1]
     item_type = data[offset + 2]
-    timestamp = int.from_bytes(data[offset+3:offset+7], 'little')
-    payload = data[offset+7:]
+    data_len = data[offset + 3] if packet_type == 0x1B else None
+    timestamp = int.from_bytes(data[offset+4:offset+8], 'little') if packet_type == 0x1B else int.from_bytes(data[offset+3:offset+7], 'little')
+    payload_offset = offset + 8 if packet_type == 0x1B else offset + 7
+    payload = data[payload_offset:payload_offset + data_len] if packet_type == 0x1B else data[payload_offset:]
     is_text = packet_type in (0x08, 0x11)
     if is_text and item_type == 0:
         message = payload.decode('utf-8')
@@ -553,7 +546,7 @@ def parse_channel_frame(data):
         'timestamp': timestamp,
         'payload': payload,
         'message': message,
-        'snr': snr if packet_type in (0x11, 0x1C) else None
+        'snr': snr
     }
 ```
 
@@ -590,8 +583,7 @@ Use `CMD_SEND_CHANNEL_TXT_MSG` for plain text, and `CMD_SEND_CHANNEL_DATA` for b
 | 0x10  | PACKET_CONTACT_MSG_RECV_V3 | Contact message (V3 with SNR) |
 | 0x11  | PACKET_CHANNEL_MSG_RECV_V3 | Channel message (V3 with SNR) |
 | 0x12  | PACKET_CHANNEL_INFO        | Channel information           |
-| 0x1B  | PACKET_CHANNEL_DATA_RECV   | Channel data (standard)       |
-| 0x1C  | PACKET_CHANNEL_DATA_RECV_V3| Channel data (V3 with SNR)    |
+| 0x1B  | PACKET_CHANNEL_DATA_RECV   | Channel data (includes SNR)   |
 | 0x80  | PACKET_ADVERTISEMENT       | Advertisement packet          |
 | 0x82  | PACKET_ACK                 | Acknowledgment                |
 | 0x83  | PACKET_MESSAGES_WAITING    | Messages waiting notification |
@@ -892,7 +884,7 @@ def on_notification_received(data):
     packet_type = data[0]
     
     if packet_type in (PACKET_CHANNEL_MSG_RECV, PACKET_CHANNEL_MSG_RECV_V3,
-                       PACKET_CHANNEL_DATA_RECV, PACKET_CHANNEL_DATA_RECV_V3):
+                       PACKET_CHANNEL_DATA_RECV):
         message = parse_channel_frame(data)
         handle_channel_message(message)
     elif packet_type == PACKET_MESSAGES_WAITING:

+ 16 - 17
examples/companion_radio/MyMesh.cpp

@@ -93,7 +93,8 @@
 #define RESP_CODE_AUTOADD_CONFIG      25
 #define RESP_ALLOWED_REPEAT_FREQ      26
 #define RESP_CODE_CHANNEL_DATA_RECV   27
-#define RESP_CODE_CHANNEL_DATA_RECV_V3 28
+
+#define MAX_CHANNEL_DATA_LENGTH       (MAX_FRAME_SIZE - 12)
 
 #define SEND_TIMEOUT_BASE_MILLIS        500
 #define FLOOD_SEND_TIMEOUT_FACTOR       16.0f
@@ -208,7 +209,7 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, co
 
 bool MyMesh::Frame::isChannelMsg() const {
   return buf[0] == RESP_CODE_CHANNEL_MSG_RECV || buf[0] == RESP_CODE_CHANNEL_MSG_RECV_V3 ||
-         buf[0] == RESP_CODE_CHANNEL_DATA_RECV || buf[0] == RESP_CODE_CHANNEL_DATA_RECV_V3;
+         buf[0] == RESP_CODE_CHANNEL_DATA_RECV;
 }
 
 void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) {
@@ -570,28 +571,26 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
 
 void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp, uint8_t data_type,
                                const uint8_t *data, size_t data_len) {
-  int i = 0;
-  if (app_target_ver >= 3) {
-    out_frame[i++] = RESP_CODE_CHANNEL_DATA_RECV_V3;
-    out_frame[i++] = (int8_t)(pkt->getSNR() * 4);
-    out_frame[i++] = 0; // reserved1
-    out_frame[i++] = 0; // reserved2
-  } else {
-    out_frame[i++] = RESP_CODE_CHANNEL_DATA_RECV;
+  if (data_len > MAX_CHANNEL_DATA_LENGTH) {
+    MESH_DEBUG_PRINTLN("onChannelDataRecv: dropping payload_len=%d exceeds frame limit=%d",
+                       (uint32_t)data_len, (uint32_t)MAX_CHANNEL_DATA_LENGTH);
+    return;
   }
 
+  int i = 0;
+  out_frame[i++] = RESP_CODE_CHANNEL_DATA_RECV;
+  out_frame[i++] = (int8_t)(pkt->getSNR() * 4);
+  out_frame[i++] = 0; // reserved1
+  out_frame[i++] = 0; // reserved2
+
   uint8_t channel_idx = findChannelIdx(channel);
   out_frame[i++] = channel_idx;
   out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
   out_frame[i++] = data_type;
+  out_frame[i++] = (uint8_t)data_len;
   memcpy(&out_frame[i], &timestamp, 4);
   i += 4;
 
-  size_t available = MAX_FRAME_SIZE - i;
-  if (data_len > available) {
-    MESH_DEBUG_PRINTLN("onChannelDataRecv(): payload_len=%d exceeds frame space=%d, truncating", (uint32_t)data_len, (uint32_t)available);
-    data_len = available;
-  }
   int copy_len = (int)data_len;
   if (copy_len > 0) {
     memcpy(&out_frame[i], data, copy_len);
@@ -1108,8 +1107,8 @@ void MyMesh::handleCmdFrame(size_t len) {
       writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
     } else if (data_type != DATA_TYPE_CUSTOM) {
       writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
-    } else if (payload_len > MAX_GROUP_DATA_LENGTH) {
-      MESH_DEBUG_PRINTLN("CMD_SEND_CHANNEL_DATA payload too long: %d > %d", payload_len, MAX_GROUP_DATA_LENGTH);
+    } else if (payload_len > MAX_CHANNEL_DATA_LENGTH) {
+      MESH_DEBUG_PRINTLN("CMD_SEND_CHANNEL_DATA payload too long: %d > %d", payload_len, MAX_CHANNEL_DATA_LENGTH);
       writeErrFrame(ERR_CODE_ILLEGAL_ARG);
     } else if (sendGroupData(msg_timestamp, channel.channel, data_type, payload, payload_len)) {
       writeOKFrame();

+ 1 - 1
src/MeshCore.h

@@ -17,7 +17,7 @@
 #define PATH_HASH_SIZE       1
 
 #define MAX_PACKET_PAYLOAD  184
-#define MAX_GROUP_DATA_LENGTH  (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 5)
+#define MAX_GROUP_DATA_LENGTH  (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 6)
 #define MAX_PATH_SIZE        64
 #define MAX_TRANS_UNIT      255
 

+ 1 - 1
src/Packet.h

@@ -22,7 +22,7 @@ namespace mesh {
 #define PAYLOAD_TYPE_ACK         0x03    // a simple ack
 #define PAYLOAD_TYPE_ADVERT      0x04    // a node advertising its Identity
 #define PAYLOAD_TYPE_GRP_TXT     0x05    // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
-#define PAYLOAD_TYPE_GRP_DATA    0x06    // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
+#define PAYLOAD_TYPE_GRP_DATA    0x06    // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, data_type, data_len, blob)
 #define PAYLOAD_TYPE_ANON_REQ    0x07    // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
 #define PAYLOAD_TYPE_PATH        0x08    // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
 #define PAYLOAD_TYPE_TRACE       0x09    // trace a path, collecting SNI for each hop

+ 28 - 12
src/helpers/BaseChatMesh.cpp

@@ -353,15 +353,15 @@ int BaseChatMesh::searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel d
 #endif
 
 void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mesh::GroupChannel& channel, uint8_t* data, size_t len) {
-  if (len < 5) {
-    MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group payload len=%d", (uint32_t)len);
-    return;
-  }
-
-  uint8_t data_type = data[4];
   if (type == PAYLOAD_TYPE_GRP_TXT) {
-    if ((data_type >> 2) != 0) {
-      MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping unsupported group text type=%d", (uint32_t)data_type);
+    if (len < 5) {
+      MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group text payload len=%d", (uint32_t)len);
+      return;
+    }
+
+    uint8_t txt_type = data[4];
+    if ((txt_type >> 2) != 0) {
+      MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping unsupported group text type=%d", (uint32_t)txt_type);
       return;
     }
 
@@ -374,9 +374,24 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes
     // notify UI  of this new message
     onChannelMessageRecv(channel, packet, timestamp, (const char *) &data[5]);  // let UI know
   } else if (type == PAYLOAD_TYPE_GRP_DATA) {
+    if (len < 6) {
+      MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group data payload len=%d", (uint32_t)len);
+      return;
+    }
+
     uint32_t timestamp;
     memcpy(&timestamp, data, 4);
-    onChannelDataRecv(channel, packet, timestamp, data_type, &data[5], len - 5);
+    uint8_t data_type = data[4];
+    uint8_t data_len = data[5];
+    size_t available_len = len - 6;
+
+    if (data_len > available_len) {
+      MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping malformed group data type=%d len=%d available=%d",
+                         (uint32_t)data_type, (uint32_t)data_len, (uint32_t)available_len);
+      return;
+    }
+
+    onChannelDataRecv(channel, packet, timestamp, data_type, &data[6], data_len);
   }
 }
 
@@ -478,12 +493,13 @@ bool BaseChatMesh::sendGroupData(uint32_t timestamp, mesh::GroupChannel& channel
     return false;
   }
 
-  uint8_t temp[5 + MAX_GROUP_DATA_LENGTH];
+  uint8_t temp[6 + MAX_GROUP_DATA_LENGTH];
   memcpy(temp, &timestamp, 4);
   temp[4] = data_type;
-  if (data_len > 0) memcpy(&temp[5], data, data_len);
+  temp[5] = (uint8_t)data_len;
+  if (data_len > 0) memcpy(&temp[6], data, data_len);
 
-  auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_DATA, channel, temp, 5 + data_len);
+  auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_DATA, channel, temp, 6 + data_len);
   if (pkt == NULL) {
     MESH_DEBUG_PRINTLN("sendGroupData: unable to create group datagram, data_len=%d", data_len);
     return false;