|
| 1 | +# Architecture: how knet, icmp, and packetdumper fit into kanonproxy |
| 2 | + |
| 3 | +This is an onboarding-oriented map of how the three external libraries |
| 4 | +[knet](https://github.com/compscidr/knet), |
| 5 | +[icmp](https://github.com/compscidr/icmp), and |
| 6 | +[packetdumper](https://github.com/compscidr/packetdumper) interact with the |
| 7 | +four kanonproxy modules (`core`, `client`, `server`, `android`). |
| 8 | + |
| 9 | +## 1. Repo relationships at a glance |
| 10 | + |
| 11 | +```mermaid |
| 12 | +flowchart TD |
| 13 | + subgraph kanon["<b>kanonproxy</b>"] |
| 14 | + direction LR |
| 15 | + core["<b>core</b><br/>proxy logic & sessions"] |
| 16 | + client["<b>client</b><br/>TUN/VPN ↔ proxy server"] |
| 17 | + server["<b>server</b><br/>UDP listener for clients"] |
| 18 | + android["<b>android</b><br/>sample VPN app"] |
| 19 | + end |
| 20 | +
|
| 21 | + knet["<b>knet</b><br/>parses raw IP bytes<br/>into Packet objects;<br/>serializes them back"] |
| 22 | + icmp["<b>icmp</b><br/>sends real ICMP echoes;<br/>platform-specific:<br/>IcmpLinux / IcmpAndroid"] |
| 23 | + pd["<b>packetdumper</b><br/>writes pcap-ng / hex-dump<br/>captures to a file or<br/>live TCP server"] |
| 24 | +
|
| 25 | + kanon -- "uses: Packet, IpHeader, TcpHeader,<br/>UdpHeader, IcmpFactory, …" --> knet |
| 26 | + kanon -- "uses: Icmp.ping(),<br/>IcmpV4/V6EchoPacket,<br/>DestinationUnreachable…" --> icmp |
| 27 | + kanon -- "uses: AbstractPacketDumper,<br/>PcapNgTcpServerPacketDumper,<br/>DummyPacketDumper, EtherType" --> pd |
| 28 | +
|
| 29 | + classDef lib fill:#4f6df0,stroke:#1a2a8c,stroke-width:2px,color:#ffffff; |
| 30 | + classDef mod fill:#2da44e,stroke:#1a6b34,stroke-width:2px,color:#ffffff; |
| 31 | + classDef group fill:#1f6feb22,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9; |
| 32 | + class knet,icmp,pd lib; |
| 33 | + class core,client,server,android mod; |
| 34 | + class kanon group; |
| 35 | +``` |
| 36 | + |
| 37 | +knet, icmp, and packetdumper are independent libraries (separate repos, |
| 38 | +separate Maven Central artifacts) that kanonproxy depends on: |
| 39 | + |
| 40 | +- **knet** — "what is this packet?" Parses raw IP bytes into typed `Packet` |
| 41 | + objects and serializes them back. |
| 42 | +- **icmp** — "actually emit a real ping." Provides the platform-specific raw- |
| 43 | + socket implementations (`IcmpLinux`, `IcmpAndroid`). |
| 44 | +- **packetdumper** — "see what's flowing through." An optional observability |
| 45 | + hook injected into `ProxyServer` / `ProxyClient` / `KAnonVpnService` that |
| 46 | + dumps every packet to a pcap-ng stream (live-tailable from Wireshark via |
| 47 | + `wireshark -k -i TCP@127.0.0.1:19000`) or to a file. `DummyPacketDumper` |
| 48 | + is the no-op default so it costs nothing in production. |
| 49 | + |
| 50 | +kanonproxy stitches these together with its own session management, TCP |
| 51 | +state machine, and platform glue. |
| 52 | + |
| 53 | +## 2. Data flow — one packet round-trip |
| 54 | + |
| 55 | +```mermaid |
| 56 | +flowchart TB |
| 57 | + subgraph coreMod["<b>core/</b> — KAnonProxy (pure logic, no I/O of its own)"] |
| 58 | + kHandle["handlePacket(packet)"] |
| 59 | + kSess["Session table<br/>(per clientAddress, per 5-tuple)"] |
| 60 | + kTcpUdp["AnonymousTcpSession / UdpSession<br/>real SocketChannel / DatagramChannel"] |
| 61 | + kIcmp["icmp.ping(destination)<br/>→ Success: IcmpV4/V6EchoPacket reply<br/>→ Failure: DestinationUnreachable"] |
| 62 | + kErr["Session.handleExceptionOnRemoteChannel<br/>→ knet:IcmpFactory.createDestinationUnreachable"] |
| 63 | + kHandle -- "TransportHeader (knet)" --> kSess --> kTcpUdp |
| 64 | + kHandle -- "IcmpNextHeaderWrapper (knet)" --> kIcmp |
| 65 | + kTcpUdp -.->|on connect failure| kErr |
| 66 | + end |
| 67 | +
|
| 68 | + subgraph midRow[" "] |
| 69 | + direction LR |
| 70 | + subgraph clientMod["<b>client/</b> — LinuxProxyClient · AndroidClient extend ProxyClient"] |
| 71 | + cParse["knet: Packet.parseStream"] |
| 72 | + cChan["DatagramChannel<br/>(UDP to server)"] |
| 73 | + cWrite["tunWrite ← packet.toByteArray()"] |
| 74 | + cParse -- "parsed Packets" --> cChan |
| 75 | + cChan -- "from server" --> cWrite |
| 76 | + end |
| 77 | + subgraph serverMod["<b>server/</b> — ProxyServer"] |
| 78 | + sRead["readFromClient()<br/>knet: Packet.parseStream"] |
| 79 | + sHandle["kAnonProxy.handlePackets()"] |
| 80 | + sTake["kAnonProxy.takeResponse()<br/>via ProxySession"] |
| 81 | + sOut["enqueueOutgoing(buffer)"] |
| 82 | + sRead --> sHandle |
| 83 | + sTake --> sOut |
| 84 | + end |
| 85 | + end |
| 86 | +
|
| 87 | + os[("OS / TUN-TAP / Android VPN<br/>raw IP byte stream")] |
| 88 | + inet[("public Internet")] |
| 89 | + pdLib(["<b>packetdumper</b><br/>dumpBuffer(...)"]) |
| 90 | + icmpLib(["<b>icmp</b> lib<br/>IcmpLinux / IcmpAndroid"]) |
| 91 | +
|
| 92 | + %% core ↔ server |
| 93 | + kHandle == "Packet (knet)" ==> sHandle |
| 94 | + sTake -- "responses" --> kTcpUdp |
| 95 | + sTake -- "responses" --> kIcmp |
| 96 | +
|
| 97 | + %% server ↔ client (UDP) |
| 98 | + sRead == "UDP" ==> cChan |
| 99 | + cChan == "UDP" ==> sOut |
| 100 | +
|
| 101 | + %% client ↔ OS |
| 102 | + cParse -- "bytes in" --> os |
| 103 | + os -- "bytes out" --> cWrite |
| 104 | +
|
| 105 | + %% core ↔ external services |
| 106 | + kTcpUdp <--> inet |
| 107 | + kIcmp <-.-> icmpLib |
| 108 | +
|
| 109 | + %% Packetdumper hooks (each packet on read/write) |
| 110 | + cParse -.->|each packet| pdLib |
| 111 | + cWrite -.->|each packet| pdLib |
| 112 | + sRead -.->|each packet| pdLib |
| 113 | + sOut -.->|each packet| pdLib |
| 114 | +
|
| 115 | + classDef ext fill:#d97706,stroke:#7c4a06,stroke-width:2px,color:#ffffff; |
| 116 | + classDef io fill:#6b7280,stroke:#1f2937,stroke-width:2px,color:#ffffff; |
| 117 | + classDef node fill:#1f6feb,stroke:#0d419d,stroke-width:2px,color:#ffffff; |
| 118 | + classDef group fill:#1f6feb22,stroke:#58a6ff,stroke-width:2px,color:#c9d1d9; |
| 119 | + classDef invisGroup fill:transparent,stroke:transparent; |
| 120 | + class pdLib,icmpLib ext; |
| 121 | + class os,inet io; |
| 122 | + class cParse,cChan,cWrite,sRead,sHandle,sTake,sOut,kHandle,kSess,kTcpUdp,kIcmp,kErr node; |
| 123 | + class clientMod,serverMod,coreMod group; |
| 124 | + class midRow invisGroup; |
| 125 | +``` |
| 126 | + |
| 127 | +Note: arrow directions in this diagram describe **rank/layout intent** (core on top, |
| 128 | +server + client below) rather than the actual data direction. Data flows in both |
| 129 | +directions on every edge — read the edge labels for what crosses the boundary. |
| 130 | + |
| 131 | +## 3. Library responsibilities — who owns what |
| 132 | + |
| 133 | +| Concern | Library | What it gives kanonproxy | |
| 134 | +|---|---|---| |
| 135 | +| Parse a raw IP byte stream into objects | **knet** | `Packet.parseStream(ByteBuffer)` | |
| 136 | +| IP / TCP / UDP / ICMP header data classes (read + serialize) | **knet** | `Ipv4Header`, `Ipv6Header`, `TcpHeader`, `UdpHeader`, `IcmpNextHeaderWrapper`, `IcmpFactory` | |
| 137 | +| Serialize a `Packet` back to bytes | **knet** | `packet.toByteArray()` | |
| 138 | +| Actually emit ICMP echo to the real network | **icmp** | `Icmp.ping(InetAddress)` — `IcmpLinux` (raw socket) on JVM, `IcmpAndroid` on Android | |
| 139 | +| ICMP echo / destination-unreachable packet types and codes | **icmp** | `IcmpV4EchoPacket`, `IcmpV6EchoPacket`, `IcmpV4/V6DestinationUnreachablePacket`, `IcmpV4/V6DestinationUnreachableCodes` | |
| 140 | +| Capture a copy of every packet for debugging | **packetdumper** | `AbstractPacketDumper.dumpBuffer(ByteBuffer, EtherType)` | |
| 141 | +| Live pcap-ng stream consumable by Wireshark | **packetdumper** | `PcapNgTcpServerPacketDumper` (default port 19000) | |
| 142 | +| No-op dumper for production / tests | **packetdumper** | `DummyPacketDumper` | |
| 143 | + |
| 144 | +Key point: **knet handles every other protocol end-to-end** (parse + build + serialize). |
| 145 | +For TCP and UDP, kanonproxy itself opens the outbound connections using ordinary |
| 146 | +`SocketChannel` / `DatagramChannel` instances inside `AnonymousTcpSession` / `UdpSession`. |
| 147 | +**ICMP is the exception**: because emitting a real ICMP echo requires privileged raw-socket |
| 148 | +access (and differs between Linux and Android), that emission is delegated to the icmp |
| 149 | +library via `Icmp.ping(...)`, with `IcmpLinux` / `IcmpAndroid` providing the platform |
| 150 | +implementation. |
| 151 | + |
| 152 | +## 4. Where each library is wired in |
| 153 | + |
| 154 | +- **`KAnonProxy(icmp: Icmp, ...)`** — single constructor injection point. Caller passes |
| 155 | + `IcmpLinux` (server `main`) or `IcmpAndroid` (`KAnonVpnService` on Android). |
| 156 | +- **`ProxyServer`** parses inbound UDP from clients with `Packet.parseStream` (knet) and |
| 157 | + forwards into `KAnonProxy`. Also accepts an `AbstractPacketDumper` (packetdumper) and |
| 158 | + calls `dumpBuffer(...)` on every packet in both directions; `ProxyServer.main` boots a |
| 159 | + `PcapNgTcpServerPacketDumper` on port `DEFAULT_PORT + 1`. |
| 160 | +- **`ProxyClient`** parses both directions with knet — TUN→proxy and proxy→TUN — and |
| 161 | + dumps each packet through its injected `AbstractPacketDumper`. |
| 162 | +- **`KAnonVpnService`** (android) wires a `PcapNgTcpServerPacketDumper` into the |
| 163 | + in-process `ProxyServer` / `AndroidClient` so on-device captures can be tailed live |
| 164 | + from Wireshark. |
| 165 | +- **`Session.handleExceptionOnRemoteChannel`** uses knet's `IcmpFactory` to fabricate a |
| 166 | + "host unreachable" reply when a TCP/UDP outbound connect fails — so even non-ICMP |
| 167 | + failures synthesize ICMP responses, and that synthesis lives in knet. |
0 commit comments