Explorar o código

* new Packet type: PAYLOAD_TYPE_TRACE

Scott Powell hai 1 ano
pai
achega
0db15db625

+ 4 - 0
examples/companion_radio/main.cpp

@@ -448,6 +448,10 @@ protected:
     // TODO: check for Get Stats response
   }
 
+  void onContactTraceRecv(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t hash[], int8_t snr[], uint8_t path_len) override {
+    // TODO: write an out_frame
+  }
+
   uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override {
     return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis);
   }

+ 4 - 0
examples/simple_secure_chat/main.cpp

@@ -268,6 +268,10 @@ protected:
     // not supported
   }
 
+  void onContactTraceRecv(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t hash[], int8_t snr[], uint8_t path_len) override {
+    // TODO: write an out_frame
+  }
+
   uint32_t calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const override {
     return SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * pkt_airtime_millis);
   }

+ 3 - 2
src/Dispatcher.cpp

@@ -105,7 +105,7 @@ void Dispatcher::checkRecv() {
           pkt->payload_len = len - i;  // payload is remainder
           memcpy(pkt->payload, &raw[i], pkt->payload_len);
 
-          score = _radio->packetScore(_radio->getLastSNR(), len);
+          score = _radio->packetScore(pkt->_snr = _radio->getLastSNR(), len);
           air_time = _radio->getEstAirtimeFor(len);
         }
       }
@@ -117,7 +117,7 @@ void Dispatcher::checkRecv() {
     #if MESH_PACKET_LOGGING      
       Serial.printf("PACKET: recv, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d\n", 
             2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
-            (int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
+            (int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
     #endif
 
     if (pkt->isRouteFlood()) {
@@ -198,6 +198,7 @@ Packet* Dispatcher::obtainNewPacket() {
     n_full_events++;
   } else {
     pkt->payload_len = pkt->path_len = 0;
+    pkt->_snr = 0;
   }
   return pkt;
 }

+ 39 - 4
src/Mesh.cpp

@@ -41,6 +41,16 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
       // remove our hash from 'path', then re-broadcast
       pkt->path_len -= PATH_HASH_SIZE;
       memcpy(pkt->path, &pkt->path[PATH_HASH_SIZE], pkt->path_len);
+
+      if (pkt->getPayloadType() == PAYLOAD_TYPE_TRACE && pkt->payload_len + PATH_HASH_SIZE+1 < MAX_PACKET_PAYLOAD) {
+        if ((pkt->payload[0] & 3) == 0) {
+          // append our hash + SNR
+          pkt->payload_len += self_id.copyHashTo(&pkt->payload[pkt->payload_len]);
+          pkt->payload[pkt->payload_len++] = (int8_t) (pkt->getSNR()*4);
+        } else {
+          // unknown flags:type, don't append any info
+        }
+      }
       return ACTION_RETRANSMIT(0);   // Routed traffic is HIGHEST priority (and NO per-hop delay)
     }
     return ACTION_RELEASE;   // this node is NOT the next hop (OR this packet has already been forwarded), so discard.
@@ -61,16 +71,21 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
       }
       break;
     }
+    case PAYLOAD_TYPE_TRACE:
     case PAYLOAD_TYPE_PATH:
     case PAYLOAD_TYPE_REQ:
     case PAYLOAD_TYPE_RESPONSE:
     case PAYLOAD_TYPE_TXT_MSG: {
       int i = 0;
+      if (pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) {
+        //uint8_t flags = pkt->payload[i];    // reserved for now
+        i++;   // skip over
+      }
       uint8_t dest_hash = pkt->payload[i++];
       uint8_t src_hash = pkt->payload[i++];
 
       uint8_t* macAndData = &pkt->payload[i];   // MAC + encrypted data 
-      if (i + 2 >= pkt->payload_len) {
+      if (i + CIPHER_MAC_SIZE >= pkt->payload_len) {
         MESH_DEBUG_PRINTLN("Mesh::onRecvPacket(): incomplete data packet");
       } else if (!_tables->hasSeen(pkt)) {
         // NOTE: this is a 'first packet wins' impl. When receiving from multiple paths, the first to arrive wins.
@@ -88,7 +103,12 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
 
             // decrypt, checking MAC is valid
             uint8_t data[MAX_PACKET_PAYLOAD];
-            int len = Utils::MACThenDecrypt(secret, data, macAndData, pkt->payload_len - i);
+            int len;
+            if (pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) {
+              len = Utils::MACThenDecrypt(secret, data, macAndData, CIPHER_MAC_SIZE+CIPHER_BLOCK_SIZE);  // encrypted part is fixed-len
+            } else {
+              len = Utils::MACThenDecrypt(secret, data, macAndData, pkt->payload_len - i);
+            }
             if (len > 0) {  // success!
               if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH) {
                 int k = 0;
@@ -104,6 +124,8 @@ DispatcherAction Mesh::onRecvPacket(Packet* pkt) {
                     if (rpath) sendDirect(rpath, path, path_len);
                   }
                 }
+              } else if (pkt->getPayloadType() == PAYLOAD_TYPE_TRACE) {
+                onPeerTraceRecv(pkt, j, secret, data);
               } else {
                 onPeerDataRecv(pkt, pkt->getPayloadType(), j, secret, data, len);
               }
@@ -227,6 +249,16 @@ DispatcherAction Mesh::routeRecvPacket(Packet* packet) {
     // append this node's hash to 'path'
     packet->path_len += self_id.copyHashTo(&packet->path[packet->path_len]);
 
+    if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE && (packet->payload[0] & 3) != 0) {
+      // flags:type must be zero (otherwise, some future/unknown sub-type)
+      return ACTION_RELEASE;  // don't forward, just discard
+    }
+
+    if (packet->getPayloadType() == PAYLOAD_TYPE_TRACE && packet->payload_len + 1 < MAX_PACKET_PAYLOAD) {
+      // append packet SNR
+      packet->payload[packet->payload_len++] = (int8_t) (packet->getSNR()*4);
+    }
+
     uint32_t d = getRetransmitDelay(packet);
     // as this propagates outwards, give it lower and lower priority
     return ACTION_RETRANSMIT_DELAYED(packet->path_len, d);   // give priority to closer sources, than ones further away
@@ -316,8 +348,8 @@ Packet* Mesh::createPathReturn(const uint8_t* dest_hash, const uint8_t* secret,
 }
 
 Packet* Mesh::createDatagram(uint8_t type, const Identity& dest, const uint8_t* secret, const uint8_t* data, size_t data_len) {
-  if (type == PAYLOAD_TYPE_TXT_MSG || type == PAYLOAD_TYPE_REQ || type == PAYLOAD_TYPE_RESPONSE) {
-    if (data_len + 2 + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL;
+  if (type == PAYLOAD_TYPE_TXT_MSG || type == PAYLOAD_TYPE_REQ || type == PAYLOAD_TYPE_RESPONSE || type == PAYLOAD_TYPE_TRACE) {
+    if (data_len + CIPHER_MAC_SIZE + CIPHER_BLOCK_SIZE-1 > MAX_PACKET_PAYLOAD) return NULL;
   } else {
     return NULL;  // invalid type
   }
@@ -330,6 +362,9 @@ Packet* Mesh::createDatagram(uint8_t type, const Identity& dest, const uint8_t*
   packet->header = (type << PH_TYPE_SHIFT);  // ROUTE_TYPE_* set later
 
   int len = 0;
+  if (type == PAYLOAD_TYPE_TRACE) {
+    packet->payload[len++] = 0;  // reserved: flags:type
+  }
   len += dest.copyHashTo(&packet->payload[len]);  // dest hash
   len += self_id.copyHashTo(&packet->payload[len]);  // src hash
   len += Utils::encryptThenMAC(secret, &packet->payload[len], data, data_len);

+ 10 - 0
src/Mesh.h

@@ -81,9 +81,19 @@ protected:
    * \param  type  one of: PAYLOAD_TYPE_TXT_MSG, PAYLOAD_TYPE_REQ, PAYLOAD_TYPE_RESPONSE
    * \param  sender_idx  index of peer, [0..n) where n is what searchPeersByHash() returned
    * \param  secret   the pre-calculated shared-secret (handy for sending response packet)
+   * \param  data   decrypted data from payload
   */
   virtual void onPeerDataRecv(Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) { }
 
+  /**
+   * \brief  A (now verified) TRACE packet has been received (by a known peer).
+   *         NOTE: these can be received multiple times (per sender/msg-id), via different routes
+   * \param  sender_idx  index of peer, [0..n) where n is what searchPeersByHash() returned
+   * \param  secret   the pre-calculated shared-secret (handy for sending response packet)
+   * \param  data   decrypted data from payload (fixed length, one CIPHER_BLOCK)
+  */
+  virtual void onPeerTraceRecv(Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* data) { }
+
   /**
    * \brief  A path TO peer (sender_idx) has been received. (also with optional 'extra' data encoded)
    *         NOTE: these can be received multiple times (per sender), via differen routes

+ 5 - 2
src/Packet.cpp

@@ -10,12 +10,15 @@ Packet::Packet() {
   payload_len = 0;
 }
 
-
 void Packet::calculatePacketHash(uint8_t* hash) const {
   SHA256 sha;
   uint8_t t = getPayloadType();
   sha.update(&t, 1);
-  sha.update(payload, payload_len);
+  if (t == PAYLOAD_TYPE_TRACE) {
+    sha.update(payload, 3+CIPHER_MAC_SIZE+CIPHER_BLOCK_SIZE);   // the 'content' part of TRACE packets is just the fixed-len encrypted part
+  } else {
+    sha.update(payload, payload_len);
+  }
   sha.finalize(hash, MAX_HASH_SIZE);
 }
 

+ 4 - 0
src/Packet.h

@@ -25,6 +25,7 @@ namespace mesh {
 #define PAYLOAD_TYPE_GRP_DATA    0x06    // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, 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
 //...
 #define PAYLOAD_TYPE_RESERVEDM   0x0F    // FUTURE
 
@@ -44,6 +45,7 @@ public:
   uint16_t payload_len, path_len;
   uint8_t path[MAX_PATH_SIZE];
   uint8_t payload[MAX_PACKET_PAYLOAD];
+  float _snr;
 
   /**
    * \brief calculate the hash of payload + type
@@ -72,6 +74,8 @@ public:
   void markDoNotRetransmit() { header = 0xFF; }
   bool isMarkedDoNotRetransmit() const { return header == 0xFF; }
 
+  float getSNR() const { return _snr; }
+
   /**
    * \brief  save entire packet as a blob
    * \param dest  (OUT) destination buffer (assumed to be MAX_MTU_SIZE)

+ 63 - 0
src/helpers/BaseChatMesh.cpp

@@ -135,6 +135,50 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
   }
 }
 
+void BaseChatMesh::onPeerTraceRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* data) {
+  int i = matching_peer_indexes[sender_idx];
+  if (i < 0 || i >= num_contacts) {
+    MESH_DEBUG_PRINTLN("onPeerTraceRecv: Invalid sender idx: %d", i);
+    return;
+  }
+
+  if ((packet->payload[0] & 3) != 0) {
+    MESH_DEBUG_PRINTLN("onPeerTraceRecv: unknown TRACE sub-type: %u", (uint32_t)packet->payload[0]);
+    return;
+  }
+
+  ContactInfo& from = contacts[i];
+
+  uint32_t timestamp;
+  memcpy(&timestamp, data, 4);  // timestamp (by sender's RTC clock - which could be wrong)
+  uint8_t sender_flags = data[4];
+
+  int j = 3 + CIPHER_MAC_SIZE + CIPHER_BLOCK_SIZE;  // skip fixed-len part
+
+  uint8_t path_hashes[64];
+  int8_t  path_snr[65];
+  uint16_t len;
+  if (packet->isRouteFlood()) {
+    memcpy(path_hashes, packet->path, len = packet->path_len);
+    memset(path_snr, 0, sizeof(path_snr));
+    memcpy(path_snr, &packet->payload[j], packet->payload_len - j);  // 'track' should just contain SNRs
+  } else {
+    len = 0;
+    while (j + 1 < packet->payload_len) {
+      path_hashes[len] = packet->payload[j++];   // pairs of Hash + SNR
+      path_snr[len] = packet->payload[j++];
+      len++;
+    }
+  }
+  path_snr[len] = (int8_t)(packet->getSNR()*4);  // also include last hop (to this node)
+
+  onContactTraceRecv(from, timestamp, path_hashes, path_snr, len);
+
+  if (sender_flags & 1) {   // the 'wants reply' flag
+    // TODO: send a TRACE packet back
+  }
+}
+
 bool BaseChatMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) {
   int i = matching_peer_indexes[sender_idx];
   if (i < 0 || i >= num_contacts) {
@@ -229,6 +273,25 @@ int  BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp,
   return rc;
 }
 
+bool BaseChatMesh::sendContactTraceDirect(const ContactInfo& recipient, bool wantReply) {
+  if (recipient.out_path_len < 0) return false;  // Error: no known path
+
+  uint8_t temp[CIPHER_BLOCK_SIZE];
+  memset(temp, 0, sizeof(temp));
+
+  uint32_t timestamp = getRTCClock()->getCurrentTime();
+  memcpy(temp, &timestamp, 4);
+  temp[4] = wantReply ? 1 : 0;
+  // TODO: any other data to encrypt??
+
+  auto pkt = createDatagram(PAYLOAD_TYPE_TRACE, recipient.id, recipient.shared_secret, temp, sizeof(temp));
+  if (pkt) {
+    sendDirect(pkt, recipient.out_path, recipient.out_path_len);
+    return true;  // success
+  }
+  return false;  // error
+}
+
 bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len) {
   uint8_t temp[5+MAX_TEXT_LEN+32];
   memcpy(temp, &timestamp, 4);   // mostly an extra blob to help make packet_hash unique

+ 3 - 0
src/helpers/BaseChatMesh.h

@@ -87,6 +87,7 @@ protected:
   virtual void onSendTimeout() = 0;
   virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) = 0;
   virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0;
+  virtual void onContactTraceRecv(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t hash[], int8_t snr[], uint8_t path_len) = 0;
 
   // storage concepts, for sub-classes to override/implement
   virtual int  getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) { return 0; }  // not implemented
@@ -98,6 +99,7 @@ protected:
   void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
   void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
   bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
+  void onPeerTraceRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* data) override;
   void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override;
 #ifdef MAX_GROUP_CHANNELS
   int searchChannelsByHash(const uint8_t* hash, mesh::GroupChannel channels[], int max_matches) override;
@@ -112,6 +114,7 @@ public:
   bool shareContactZeroHop(const ContactInfo& contact);
   uint8_t exportContact(const ContactInfo& contact, uint8_t dest_buf[]);
   bool importContact(const uint8_t src_buf[], uint8_t len);
+  bool sendContactTraceDirect(const ContactInfo& recipient, bool wantReply);
   void resetPathTo(ContactInfo& recipient);
   void scanRecentContacts(int last_n, ContactVisitor* visitor);
   ContactInfo* searchContactsByPrefix(const char* name_prefix);