mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-23 21:33:53 +00:00
Trace public nRF52 advertising payloads for discovery diagnosis
This commit is contained in:
1 parent
042d5b7f0d
commit
016a69ea10
3 files changed
+106
No files matched your search
@@ -31,6 +31,7 @@ struct MeshBleTraceBuffer {
|
||||
extern "C" { extern MeshBleTraceBuffer mesh_ble_trace; }
|
||||
void meshBleTraceState(char* reply, size_t capacity);
|
||||
void meshBleTraceIdentity(char* reply, size_t capacity);
|
||||
void meshBleTraceAdvertising(char* reply, size_t capacity);
|
||||
static inline void meshBleTraceMax(std::atomic<uint32_t>& value, uint32_t sample) {
|
||||
uint32_t old = value.load(std::memory_order_relaxed);
|
||||
while (sample > old && !value.compare_exchange_weak(old, sample,
|
||||
@@ -78,6 +79,11 @@ static inline void meshBleTraceDispatchExit(uint32_t event, uint32_t handle) {
|
||||
}
|
||||
static inline bool meshBleTraceCommand(const char* command, char* reply,
|
||||
size_t capacity) {
|
||||
if (!strcmp(command, "get bluetooth.trace.advertising")
|
||||
|| !strcmp(command, "get ble.trace.advertising")) {
|
||||
meshBleTraceAdvertising(reply, capacity);
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(command, "get bluetooth.trace.identity")
|
||||
|| !strcmp(command, "get ble.trace.identity")) {
|
||||
meshBleTraceIdentity(reply, capacity);
|
||||
|
||||
@@ -94,6 +94,33 @@ void meshBleTraceIdentity(char* reply, size_t capacity) {
|
||||
name_hex, (unsigned)Bluefruit.Advertising.count(),
|
||||
(unsigned)Bluefruit.ScanResponse.count());
|
||||
}
|
||||
|
||||
void meshBleTraceAdvertising(char* reply, size_t capacity) {
|
||||
if (!instance) {
|
||||
snprintf(reply, capacity, "> Bluetooth not initialized");
|
||||
return;
|
||||
}
|
||||
// Legacy advertising/scan-response bytes are public. Read the configured
|
||||
// payloads themselves: the GAP name getter cannot prove what addName copied.
|
||||
// Retain a hard 31-byte bound even if an invalid count is ever observed.
|
||||
const auto encode = [](BLEAdvertisingData& payload, char* output) -> unsigned {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
const unsigned count = payload.count();
|
||||
const uint8_t* bytes = payload.getData();
|
||||
const unsigned copied = bytes ? (count < 31u ? count : 31u) : 0u;
|
||||
for (unsigned i = 0; i < copied; ++i) {
|
||||
output[2 * i] = hex[bytes[i] >> 4];
|
||||
output[2 * i + 1] = hex[bytes[i] & 15];
|
||||
}
|
||||
output[2 * copied] = 0;
|
||||
return count;
|
||||
};
|
||||
char advertising_hex[63], scan_hex[63];
|
||||
const unsigned advertising_length = encode(Bluefruit.Advertising, advertising_hex);
|
||||
const unsigned scan_length = encode(Bluefruit.ScanResponse, scan_hex);
|
||||
snprintf(reply, capacity, "> adv_len=%u,adv_hex=%s,scan_len=%u,scan_hex=%s",
|
||||
advertising_length, advertising_hex, scan_length, scan_hex);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool formatDeviceName(char* dest, size_t capacity,
|
||||
|
||||
@@ -27,6 +27,9 @@ void meshBleTraceState(char* reply, size_t capacity) {
|
||||
void meshBleTraceIdentity(char* reply, size_t capacity) {
|
||||
snprintf(reply,capacity,"> identity");
|
||||
}
|
||||
void meshBleTraceAdvertising(char* reply, size_t capacity) {
|
||||
snprintf(reply,capacity,"> advertising");
|
||||
}
|
||||
int main() {
|
||||
char reply[160];
|
||||
assert(!meshBleTraceCommand("get radio",reply,sizeof(reply)));
|
||||
@@ -49,6 +52,11 @@ int main() {
|
||||
assert(meshBleTraceCommand(command,reply,sizeof(reply)));
|
||||
assert(!strcmp(reply,"> identity"));
|
||||
}
|
||||
for (const char* command : {"get bluetooth.trace.advertising", "get ble.trace.advertising"}) {
|
||||
assert(meshBleTraceCommand(command,reply,sizeof(reply)));
|
||||
assert(!strcmp(reply,"> advertising"));
|
||||
}
|
||||
assert(!meshBleTraceCommand("get ble.trace.advertising extra",reply,sizeof(reply)));
|
||||
meshBleTraceDispatchEnter(0x10,0);
|
||||
assert(mesh_ble_trace.dispatch_event.load()==0x10);
|
||||
meshBleTraceDispatchExit(0x10,0);
|
||||
@@ -150,6 +158,71 @@ int main() {
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
|
||||
def test_real_advertising_query_reads_public_payloads_with_31_byte_bounds(self):
|
||||
source = (ROOT / "src/helpers/nrf52/SerialBLEInterface.cpp").read_text()
|
||||
start = source.index("void meshBleTraceAdvertising(")
|
||||
implementation = source[start:source.index("\n}\n", start) + 3]
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
work = Path(directory)
|
||||
cpp = work / "advertising.cpp"
|
||||
cpp.write_text(r'''
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
static void* instance;
|
||||
struct BLEAdvertisingData {
|
||||
uint8_t length = 0;
|
||||
uint8_t bytes[31] = {};
|
||||
uint8_t count() { return length; }
|
||||
uint8_t* getData() { return bytes; }
|
||||
};
|
||||
static struct { BLEAdvertisingData Advertising, ScanResponse; } Bluefruit;
|
||||
@IMPLEMENTATION@
|
||||
int main() {
|
||||
char reply[172];
|
||||
meshBleTraceAdvertising(reply, sizeof(reply));
|
||||
assert(!strcmp(reply, "> Bluetooth not initialized"));
|
||||
instance = reply;
|
||||
const uint8_t advertising[] = {2, 1, 6, 2, 10, 4};
|
||||
memcpy(Bluefruit.Advertising.bytes, advertising, sizeof(advertising));
|
||||
Bluefruit.Advertising.length = sizeof(advertising);
|
||||
// This is the copied advertising payload, independent of the GAP getter.
|
||||
const uint8_t scan[] = {5, 9, 'R', 0, 0xc3, 0xa9};
|
||||
memcpy(Bluefruit.ScanResponse.bytes, scan, sizeof(scan));
|
||||
Bluefruit.ScanResponse.length = sizeof(scan);
|
||||
meshBleTraceAdvertising(reply, sizeof(reply));
|
||||
assert(!strcmp(reply, "> adv_len=6,adv_hex=020106020a04,scan_len=6,scan_hex=05095200c3a9"));
|
||||
for (unsigned i = 0; i < 31; ++i) {
|
||||
Bluefruit.Advertising.bytes[i] = i;
|
||||
Bluefruit.ScanResponse.bytes[i] = 255 - i;
|
||||
}
|
||||
Bluefruit.Advertising.length = Bluefruit.ScanResponse.length = 255;
|
||||
meshBleTraceAdvertising(reply, sizeof(reply));
|
||||
assert(strlen(reply) < sizeof(reply) - 1);
|
||||
const char* adv_hex = strstr(reply, "adv_hex=") + 8;
|
||||
const char* scan_hex = strstr(reply, "scan_hex=") + 9;
|
||||
assert(strchr(adv_hex, ',') - adv_hex == 62 && strlen(scan_hex) == 62);
|
||||
assert(!strncmp(reply, "> adv_len=255,", 14));
|
||||
assert(strstr(reply, ",scan_len=255,"));
|
||||
char tiny[3] = {};
|
||||
meshBleTraceAdvertising(tiny, sizeof(tiny));
|
||||
assert(tiny[2] == 0);
|
||||
char untouched = 'X';
|
||||
meshBleTraceAdvertising(&untouched, 0);
|
||||
assert(untouched == 'X');
|
||||
}
|
||||
'''.replace("@IMPLEMENTATION@", implementation))
|
||||
binary = work / "advertising"
|
||||
compiled = subprocess.run([
|
||||
"c++", "-std=c++17", "-O2", "-flto", "-Wall", "-Wextra", "-Werror",
|
||||
"-fsanitize=address,undefined", "-fno-omit-frame-pointer",
|
||||
"-fno-pie", "-no-pie", str(cpp), "-o", str(binary),
|
||||
], capture_output=True, text=True)
|
||||
self.assertEqual(compiled.returncode, 0, compiled.stderr)
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
|
||||
def test_concurrent_reader_never_accepts_mixed_records(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
work = Path(directory)
|
||||
|
||||
Reference in new issue
Block a user