mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-10-23 20:32:24 +00:00
fix: parse TRACE packet path hops from payload instead of header (#277)
Fixes #276 ## Root cause TRACE packets store hop IDs in the payload (bytes 9+) rather than in the header path field. The header path field is overloaded in TRACE packets to carry RSSI values instead of repeater IDs (as noted in the issue comments). This meant `Path.Hops` was always empty for TRACE packets — the raw bytes ended up as an opaque `PathData` hex string with no structure. The hashSize encoded in the header path byte (bits 6–7) is still valid for TRACE and is used to split the payload path bytes into individual hop prefixes. ## Fix After decoding a TRACE payload, if `PathData` is non-empty, parse it into individual hops using `path.HashSize`: ```go if header.PayloadType == PayloadTRACE && payload.PathData != "" { pathBytes, err := hex.DecodeString(payload.PathData) if err == nil && path.HashSize > 0 { for i := 0; i+path.HashSize <= len(pathBytes); i += path.HashSize { path.Hops = append(path.Hops, ...) } } } ``` Applied to both `cmd/ingestor/decoder.go` and `cmd/server/decoder.go`. ## Verification Packet from the issue: `260001807dca00000000007d547d` | | Before | After | |---|---|---| | `Path.Hops` | `[]` | `["7D", "54", "7D"]` | | `Path.HashCount` | `0` | `3` | New test `TestDecodeTracePathParsing` covers this exact packet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
726b041740
commit
8f833f64ae
3 files changed
+55
No files matched your search
@@ -572,6 +572,21 @@ func DecodePacket(hexString string, channelKeys map[string]string) (*DecodedPack
|
||||
payloadBuf := buf[offset:]
|
||||
payload := decodePayload(header.PayloadType, payloadBuf, channelKeys)
|
||||
|
||||
// TRACE packets store hop IDs in the payload (buf[9:]) rather than the header
|
||||
// path field. The header path byte still encodes hashSize in bits 6-7, which
|
||||
// we use to split the payload path data into individual hop prefixes.
|
||||
if header.PayloadType == PayloadTRACE && payload.PathData != "" {
|
||||
pathBytes, err := hex.DecodeString(payload.PathData)
|
||||
if err == nil && path.HashSize > 0 {
|
||||
hops := make([]string, 0, len(pathBytes)/path.HashSize)
|
||||
for i := 0; i+path.HashSize <= len(pathBytes); i += path.HashSize {
|
||||
hops = append(hops, strings.ToUpper(hex.EncodeToString(pathBytes[i:i+path.HashSize])))
|
||||
}
|
||||
path.Hops = hops
|
||||
path.HashCount = len(hops)
|
||||
}
|
||||
}
|
||||
|
||||
return &DecodedPacket{
|
||||
Header: header,
|
||||
TransportCodes: tc,
|
||||
|
||||
@@ -564,6 +564,31 @@ func TestDecodeTraceValid(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeTracePathParsing(t *testing.T) {
|
||||
// Packet from issue #276: 260001807dca00000000007d547d
|
||||
// Path byte 0x00 → hashSize=1, hops in payload at buf[9:] = 7d 54 7d
|
||||
// Expected path: ["7D", "54", "7D"]
|
||||
pkt, err := DecodePacket("260001807dca00000000007d547d", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("DecodePacket error: %v", err)
|
||||
}
|
||||
if pkt.Payload.Type != "TRACE" {
|
||||
t.Errorf("payload type=%s, want TRACE", pkt.Payload.Type)
|
||||
}
|
||||
want := []string{"7D", "54", "7D"}
|
||||
if len(pkt.Path.Hops) != len(want) {
|
||||
t.Fatalf("hops=%v, want %v", pkt.Path.Hops, want)
|
||||
}
|
||||
for i, h := range want {
|
||||
if pkt.Path.Hops[i] != h {
|
||||
t.Errorf("hops[%d]=%s, want %s", i, pkt.Path.Hops[i], h)
|
||||
}
|
||||
}
|
||||
if pkt.Path.HashCount != 3 {
|
||||
t.Errorf("hashCount=%d, want 3", pkt.Path.HashCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeAdvertShort(t *testing.T) {
|
||||
p := decodeAdvert(make([]byte, 50))
|
||||
if p.Error != "too short for advert" {
|
||||
|
||||
@@ -373,6 +373,21 @@ func DecodePacket(hexString string) (*DecodedPacket, error) {
|
||||
payloadBuf := buf[offset:]
|
||||
payload := decodePayload(header.PayloadType, payloadBuf)
|
||||
|
||||
// TRACE packets store hop IDs in the payload (buf[9:]) rather than the header
|
||||
// path field. The header path byte still encodes hashSize in bits 6-7, which
|
||||
// we use to split the payload path data into individual hop prefixes.
|
||||
if header.PayloadType == PayloadTRACE && payload.PathData != "" {
|
||||
pathBytes, err := hex.DecodeString(payload.PathData)
|
||||
if err == nil && path.HashSize > 0 {
|
||||
hops := make([]string, 0, len(pathBytes)/path.HashSize)
|
||||
for i := 0; i+path.HashSize <= len(pathBytes); i += path.HashSize {
|
||||
hops = append(hops, strings.ToUpper(hex.EncodeToString(pathBytes[i:i+path.HashSize])))
|
||||
}
|
||||
path.Hops = hops
|
||||
path.HashCount = len(hops)
|
||||
}
|
||||
}
|
||||
|
||||
return &DecodedPacket{
|
||||
Header: header,
|
||||
TransportCodes: tc,
|
||||
|
||||
Reference in new issue
Block a user