@decentnetwork/lan 0.1.257 → 0.1.259

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,6 +17,12 @@ const RATE_REPORT_INTERVAL_MS = 1000;
17
17
  // pacing a trickle (SSH/keepalives), and it keeps reports sparse. The pacer's
18
18
  // loss window derives its verdict from these reports vs. what it sent.
19
19
  const RATE_REPORT_MIN_BYTES = 32 * 1024; // ~256 kbps sustained
20
+ /** Session keepalive: one zero byte on the data channel.
21
+ *
22
+ * Deliberately not a valid IP packet — the receiver recognises and swallows
23
+ * it. It exists only to keep the Carrier session warm, so it must never be
24
+ * counted as forwarded traffic OR as a drop. */
25
+ const KEEPALIVE_PACKET = new Uint8Array([0]);
20
26
  export class PacketRouter extends EventEmitter {
21
27
  tunDevice;
22
28
  peerManager;
@@ -198,7 +204,7 @@ export class PacketRouter extends EventEmitter {
198
204
  if (!session?.isConnected())
199
205
  continue;
200
206
  try {
201
- await session.send(new Uint8Array([0])); // 1-byte keepalive, dropped at receiver
207
+ await session.send(KEEPALIVE_PACKET);
202
208
  }
203
209
  catch {
204
210
  // ignore — session may have just gone offline
@@ -345,8 +351,30 @@ export class PacketRouter extends EventEmitter {
345
351
  return;
346
352
  const parsed = IpParser.parse(packet);
347
353
  if (!parsed) {
348
- this.stats.packetsDropped++;
349
- this.logger.debug("Dropped invalid incoming IP packet");
354
+ // Classify instead of lumping. IpParser returns null for three very
355
+ // different situations and they need different fixes: a short packet
356
+ // means the transport handed us a truncated frame (MTU / reassembly),
357
+ // while IPv6 means the SENDER is forwarding traffic we never support --
358
+ // one is a bug in us, the other is a filter missing upstream. Recording
359
+ // it per-source also makes it visible in `agentnet diag`, which is how
360
+ // this stayed hidden: the old code bumped the counter and logged at
361
+ // debug, so tens of thousands of drops had neither reason nor owner.
362
+ // OUR OWN keepalive, not a loss. sendKeepalivesToActiveSessions emits a
363
+ // single zero byte on the data channel and the receiver was counting
364
+ // every one of them as a dropped packet: 24015 "drops" on mac-dev with
365
+ // only ~2038 attributable, 16919 on cn against 5. That made the drop
366
+ // metric useless exactly when it was needed to explain real packet loss,
367
+ // and sent this investigation down a wrong path first. Swallow it
368
+ // silently — it did its job by arriving.
369
+ if (packet.length === KEEPALIVE_PACKET.length && packet[0] === KEEPALIVE_PACKET[0]) {
370
+ return;
371
+ }
372
+ const reason = packet.length < 20
373
+ ? "inbound-too-short"
374
+ : ((packet[0] >> 4) & 0x0f) !== 4
375
+ ? "inbound-not-ipv4"
376
+ : "inbound-bad-header";
377
+ this.recordDrop(`from:${srcPubkey.slice(0, 12)}`, reason, `len=${packet.length}`);
350
378
  return;
351
379
  }
352
380
  // Auto-learn (srcPubkey -> srcIp) into IPAM. Without this, ubuntu
@@ -18,7 +18,7 @@ export declare const IP_PROTO_ICMP = 1;
18
18
  * isn't my ping working?" — without forcing them to crank up the
19
19
  * log level.
20
20
  */
21
- export type DropReason = "invalid-ip" | "no-port" | "no-ipam-record" | "friend-offline" | "handshake-failed" | "send-failed" | "tun-write-failed" | "tun-down";
21
+ export type DropReason = "invalid-ip" | "inbound-too-short" | "inbound-not-ipv4" | "inbound-bad-header" | "no-port" | "no-ipam-record" | "friend-offline" | "handshake-failed" | "send-failed" | "tun-write-failed" | "tun-down";
22
22
  export interface DstDropInfo {
23
23
  /** Total drops to this destination. */
24
24
  count: number;
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.257";
1
+ window.__DK_UI_VERSION="0.1.259";
2
2
  const ICON_PATHS = {
3
3
  // ---- tab bar (the four must feel like one set) ----
4
4
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/lan",
3
- "version": "0.1.257",
3
+ "version": "0.1.259",
4
4
  "description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -84,7 +84,7 @@
84
84
  },
85
85
  "dependencies": {
86
86
  "@decentnetwork/dora": "^0.1.14",
87
- "@decentnetwork/peer": "^0.1.123",
87
+ "@decentnetwork/peer": "^0.1.126",
88
88
  "@decentnetwork/peer-webrtc": "^0.2.10",
89
89
  "ink": "^5.2.1",
90
90
  "js-yaml": "^4.1.0",