DF-2362 / analyze2362b.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #!/usr/bin/env python3 -u # analyze2362b.py -- properly reconstruct packet round-trips from the raw log. import re, sys LOG = sys.argv[1] if len(sys.argv) > 1 else "/tmp/opencode/df2362-raw.log" raw = open(LOG, "rb").read().decode(errors="replace") events = [] for m in re.finditer(r"<< |>> ", raw): events.append((m.start(), m.end())) chunks = [] for i, (s, e) in enumerate(events): end = events[i + 1][0] if i + 1 < len(events) else len(raw) prefix = raw[s:e] chunks.append(("in" if prefix == "<< " else "out", raw[e:end])) def frames(buf): out = [] while True: i = buf.find("$") if i < 0: return out, buf m = re.match(r"\$([^#]*)#([0-9a-fA-F]{2})", buf[i:]) if not m or len(m.group(0)) > len(buf[i:]): return out, buf[i:] out.append(m.group(1)) buf = buf[i + len(m.group(0)):] # consume a leading '+'/'-' in remainder lazily rounds = [] recvbuf = "" pending = None for d, t in chunks: if d == "out": if t.startswith("$"): m = re.match(r"\$([^#]*)#([0-9a-fA-F]{2})", t) if m: pending = m.group(1) elif t.startswith("+") and pending is not None: pass else: recvbuf += t # strip acks while recvbuf[:1] in ("+", "-"): recvbuf = recvbuf[1:] fs, recvbuf = frames(recvbuf) for f in fs: rounds.append((pending, f)) pending = None print("round trips: %d" % len(rounds)) def hexv(c): ch = chr(c) if ch in "0123456789": return ord(ch) - 48 if ch in "abcdef": return ord(ch) - 87 if ch in "ABCDEF": return ord(ch) - 55 return -1 ROUT = 0xffffffff817a6f20 RIN = 0xffffffff817a70c0 # locate the big m reads and verify contents for idx, (s, r) in enumerate(rounds): if s and s.startswith("m") and "," in s: addr = int(s[1:].split(",")[0], 16) n = int(s.split(",")[1], 16) if r and len(r) == 2 * n: b = bytes.fromhex(r) # is this a read of the ROUT tail? if addr == ROUT + 400 and n == 0xc0: print("[%02d] verify-read of gap: %s" % (idx, b[:40].decode(errors="replace"))) # ---- T1 forensic: find the m<src>,108 overflow for idx, (s, r) in enumerate(rounds): if s and s.endswith(",108") and s.startswith("m") and r and len(r) == 528: ov = r # the next read(s) print("\nT1 overflow reply: len %d, chars[400:416]=%r" % (len(ov), ov[400:416])) for j in range(idx + 1, min(idx + 3, len(rounds))): s2, r2 = rounds[j] if s2 and s2.startswith("m") and r2: a2 = int(s2[1:].split(",")[0], 16) n2 = int(s2.split(",")[1], 16) b2 = bytes.fromhex(r2) if a2 == ROUT + 400: print(" first verify read [%d] (%d bytes):" % (j, n2)) print(" bytes 0..15 (gap): %r" % b2[:16]) print(" expected ov[400:416]: %r" % ov[400:416].encode()) print(" match: %s" % (b2[:16] == ov[400:416].encode())) print(" bytes 38..111 (Rin22..):%r" % b2[38:112][:20]) print(" expected ov[438:458]: %r" % ov[438:458].encode()) print(" match40: %s" % (b2[38:112] == ov[438:528].encode()[:74])) print(" byte@128 (Rout+528): %02x" % b2[128]) break # ---- T3 forensic print("\n=== T3 ===") for idx, (s, r) in enumerate(rounds): if s and s.startswith("M") and ",fa:" in s and r == "OK": data = s.split(":", 1)[1] datb = bytes.fromhex(data) # following reads reads = [] for j in range(idx + 1, min(idx + 8, len(rounds))): s2, r2 = rounds[j] if s2 and s2.startswith("m") and r2 and "," in s2: a2 = int(s2[1:].split(",")[0], 16) n2 = int(s2.split(",")[1], 16) if len(r2) == 2 * n2: reads.append((a2, n2, bytes.fromhex(r2))) tgt = [x for x in reads if x[0] == 0xffffffff817a6fa0] rin = [x for x in reads if x[0] == RIN + 0x180] if tgt and rin: got = tgt[0][2] post = rin[0][2] src = bytearray([0]) + post[14:16] + post[16:] pred = bytearray(datb) i = 0 while len(pred) < 250 and 2 * i + 1 < len(src): pred.append(((hexv(src[2*i]) << 4) + hexv(src[2*i+1])) & 0xff) i += 1 pred = bytes(pred[:250]) print("pkt3 data %dB, target got %dB" % (len(datb), len(got))) print("src head: %s" % src[:12].hex()) ok = True for k in range(min(len(got), len(pred))): if got[k] != pred[k]: print("diverge at %d: got %02x pred %02x" % (k, got[k], pred[k])) print(" got : %s" % got[max(0,k-6):k+10].hex()) print(" pred : %s" % pred[max(0,k-6):k+10].hex()) kb = k - 188 # index into src bytes print(" src around byte %d: %s" % (kb, src[max(0,kb-2):kb+6].hex())) ok = False break if ok and len(got) == len(pred): print("FULL MATCH") break |