Mirage
Mirage
Reference: https://ocaml.org/p/mirage/4.10.3
Install Mirage OS
Reference: https://mirage.io/docs/install
Create new switch for Mirage OS
opam switch create mirage.5.3.0 ocaml 5.3.0
eval $(opam env --switch=mirage.5.3.0)$ opam list
# Packages matching: installed
# Name # Installed # Synopsis
base-bigarray base
base-domains base
base-effects base
base-nnp base Naked pointers prohibited in the OCaml heap
base-threads base
base-unix base
ocaml 5.3.0 The OCaml compiler (virtual package)
ocaml-base-compiler 5.3.0 Official release of OCaml 5.3.0
ocaml-compiler 5.3.0 Official release of OCaml 5.3.0
ocaml-config 3 OCaml Switch Configuration
ocaml-options-vanilla 1 Ensure that OCaml is compiled with no special options enabledopam update
opam upgradeInstall mirage os.
opam install mirageVerify its installed.
mirage --helpCreate unikernal with no jobs
Reference: https://mirage.io/docs/hello-world
git clone https://github.com/mirage/mirage-skeleton.git
cd mirage-skeletonCreate make file
cd tutorial/noop/
mirage configure -t unixSuccessfully configured the unikernel. Now run 'make' (or more fine-grained steps: 'make all', 'make depends', or 'make lock').Install dependencies
make dependsusing overlay repository mirage: [opam-overlays, mirage-overlays]
[opam-overlays] Initialised
[NOTE] Repository opam-overlays has been added to the selections of switch mirage.5.3.0 only.
Run `opam repository add opam-overlays --all-switches|--set-default' to use it in all existing switches, or in
newly created switches, respectively.
[mirage-overlays] Initialised
[NOTE] Repository mirage-overlays has been added to the selections of switch mirage.5.3.0 only.
Run `opam repository add mirage-overlays --all-switches|--set-default' to use it in all existing switches, or in
newly created switches, respectively.
↳ generate lockfile for monorepo dependencies
==> Using 1 locally scanned package as the target.
==> Found 60 opam dependencies for the target package.
==> Querying opam database for their metadata and Dune compatibility.
==> Calculating exact pins for each of them.
==> Wrote lockfile with 25 entries to mirage/noop-unix.opam.locked. You can now run opam monorepo pull to fetch their sources.
removing overlay repository [opam-overlays, mirage-overlays]
Repositories removed from the selections of switch mirage.5.3.0. Use '--all' to forget about them altogether.
Repositories removed from the selections of switch mirage.5.3.0. Use '--all' to forget about them altogether.
The lock file has been generated. Run 'make pull' to retrieve the sources, or 'make install-switch' to install the host dependencies.
↳ opam install switch dependencies
[WARNING] Failed checks on noop-unix package definition from source at
file:///home/porky/ocaml/mirage/mirage-skeleton/tutorial/noop/mirage:
warning 68: Missing field 'license'
Nothing to do.
↳ install external dependencies for monorepo
==> Using lockfile mirage/noop-unix.opam.locked
The dependencies have been installed. Run 'make build' to build the unikernel.
↳ fetch monorepo dependencies in the duniverse folder
==> Using lockfile mirage/noop-unix.opam.locked
Successfully pulled 25/25 repositories
The sources have been pulled to the duniverse folder. Run 'make build' to build the unikernel.Compile
make buildExecute.
$ dist/noop
# see the exit status of the last executed function
$ echo $? #
0Create unikernal that returns log
Build hello.
cd tutorial/hello
mirage configure -t unix
make depends
dune buildRun hello.
dist/hello
2025-12-14T09:02:01-08:00: [INFO] [application] hello
2025-12-14T09:02:02-08:00: [INFO] [application] hello
2025-12-14T09:02:03-08:00: [INFO] [application] hello
2025-12-14T09:02:04-08:00: [INFO] [application] helloBuild for KVM
mirage configure -t hvt
make depends
dune build
solo5-hvt dist/hello.hvt $ solo5-hvt dist/hello.hvt
| ___|
__| _ \ | _ \ __ \
\__ \ ( | | ( | ) |
____/\___/ _|\___/____/
Solo5: Bindings version v0.10.0
Solo5: Memory map: 512 MB addressable:
Solo5: reserved @ (0x0 - 0xfffff)
Solo5: text @ (0x100000 - 0x230fff)
Solo5: rodata @ (0x231000 - 0x280fff)
Solo5: data @ (0x281000 - 0x33efff)
Solo5: heap >= 0x33f000 < stack < 0x20000000
2025-12-14T17:14:08-00:00: [INFO] [application] hello
2025-12-14T17:14:09-00:00: [INFO] [application] hello
2025-12-14T17:14:10-00:00: [INFO] [application] hello
2025-12-14T17:14:11-00:00: [INFO] [application] hello
Solo5: solo5_exit(0) calledRuntime arguments
cd tutorial/hello-key
mirage configure -t unix
make depends$ dist/hello-key
2025-12-14T12:25:06-08:00: [INFO] [application] Hello World!
2025-12-14T12:25:07-08:00: [INFO] [application] Hello World!
2025-12-14T12:25:08-08:00: [INFO] [application] Hello World!
2025-12-14T12:25:09-08:00: [INFO] [application] Hello World!$ dist/hello-key --hello="Good-bye!"
2025-12-14T12:25:34-08:00: [INFO] [application] Good-bye!
2025-12-14T12:25:35-08:00: [INFO] [application] Good-bye!
2025-12-14T12:25:36-08:00: [INFO] [application] Good-bye!
2025-12-14T12:25:37-08:00: [INFO] [application] Good-bye!…using HVT
mirage configure -t hvt
make depends
dune build$ solo5-hvt dist/hello-key.hvt
| ___|
__| _ \ | _ \ __ \
\__ \ ( | | ( | ) |
____/\___/ _|\___/____/
Solo5: Bindings version v0.10.0
Solo5: Memory map: 512 MB addressable:
Solo5: reserved @ (0x0 - 0xfffff)
Solo5: text @ (0x100000 - 0x231fff)
Solo5: rodata @ (0x232000 - 0x281fff)
Solo5: data @ (0x282000 - 0x33ffff)
Solo5: heap >= 0x340000 < stack < 0x20000000
2025-12-14T20:29:19-00:00: [INFO] [application] Hello World!
2025-12-14T20:29:20-00:00: [INFO] [application] Hello World!
2025-12-14T20:29:21-00:00: [INFO] [application] Hello World!
2025-12-14T20:29:22-00:00: [INFO] [application] Hello World!
Solo5: solo5_exit(0) called
$ solo5-hvt dist/hello-key.hvt --hello="HVT!"
| ___|
__| _ \ | _ \ __ \
\__ \ ( | | ( | ) |
____/\___/ _|\___/____/
Solo5: Bindings version v0.10.0
Solo5: Memory map: 512 MB addressable:
Solo5: reserved @ (0x0 - 0xfffff)
Solo5: text @ (0x100000 - 0x231fff)
Solo5: rodata @ (0x232000 - 0x281fff)
Solo5: data @ (0x282000 - 0x33ffff)
Solo5: heap >= 0x340000 < stack < 0x20000000
2025-12-14T20:29:44-00:00: [INFO] [application] HVT!
2025-12-14T20:29:45-00:00: [INFO] [application] HVT!
2025-12-14T20:29:46-00:00: [INFO] [application] HVT!
2025-12-14T20:29:47-00:00: [INFO] [application] HVT!
Solo5: solo5_exit(0) called
porky@macbuntu:~/ocaml/mirage/mirage-skeleton/tutorial/hello-key$ Access block device.
cd device-usage/block
mirage configure -t unix
make depeneds
dune buildCreate disk.img
dd if=/dev/zero of=disk.img count=100000 dd if=/dev/zero of=disk.img count=100000
100000+0 records in
100000+0 records out
51200000 bytes (51 MB, 49 MiB) copied, 0.16923 s, 303 MB/sExecute
./dist/block_test
2025-12-14T13:49:42-08:00: [INFO] [block] { Mirage_block.read_write = true;
sector_size = 512;
size_sectors = 100000L }
reading 1 sectors at 100000
reading 12 sectors at 99989
2025-12-14T13:49:42-08:00: [INFO] [block] Test sequence finished
2025-12-14T13:49:42-08:00: [INFO] [block] Total tests started: 10
2025-12-14T13:49:42-08:00: [INFO] [block] Total tests passed: 10
2025-12-14T13:49:42-08:00: [INFO] [block] Total tests failed: 0…using hvt
$ mirage configure -t hvt
$ make depends
$ dune build
$ solo5-hvt --block:storage=disk.img ./dist/block_test.hvt
| ___|
__| _ \ | _ \ __ \
\__ \ ( | | ( | ) |
____/\___/ _|\___/____/
Solo5: Bindings version v0.10.0
Solo5: Memory map: 512 MB addressable:
Solo5: reserved @ (0x0 - 0xfffff)
Solo5: text @ (0x100000 - 0x23dfff)
Solo5: rodata @ (0x23e000 - 0x290fff)
Solo5: data @ (0x291000 - 0x359fff)
Solo5: heap >= 0x35a000 < stack < 0x20000000
2025-12-14T21:53:45-00:00: [INFO] [block] { Mirage_block.read_write = true;
sector_size = 512;
size_sectors = 100000L }
reading 1 sectors at 100000
reading 12 sectors at 99989
2025-12-14T21:53:45-00:00: [INFO] [block] Test sequence finished
2025-12-14T21:53:45-00:00: [INFO] [block] Total tests started: 10
2025-12-14T21:53:45-00:00: [INFO] [block] Total tests passed: 10
2025-12-14T21:53:45-00:00: [INFO] [block] Total tests failed: 0
Solo5: solo5_exit(0) calledKey value store
cd device-usage/kv_ro
mirage configure -t unix
make depends
dune buildGenerating Static_t.ml
Generating Static_t.mliExecute…
$ dist/kv_ro
2025-12-14T14:15:05-08:00: [INFO] [application] Contents of extremely secret vital storage confirmed!Rebuild with --kv_ro=direct
mirage configure -t unix --kv_ro=direct
make depends
dune build
dist/kv_ro2025-12-14T14:17:40-08:00: [INFO] [application] Contents of extremely secret vital storage confirmed!Networking
mirage configure -t unix --net socket
make depends
dune buildStart the program
dist/networkIn another console run
echo -n hello tcp world | nc -nw1 127.0.0.1 8080…and you should see this in the command prompt
$ dist/network
2025-12-14T14:28:37-08:00: [INFO] [tcpip-stack-socket] Dual IPv4 and IPv6 socket stack: connect
2025-12-14T14:28:40-08:00: [INFO] [application] new tcp connection from IP 127.0.0.1 on port 56654Run program again with log set to debug
dist/network -l "*:debug"Run the command, echo -n hello tcp world | nc -nw1 127.0.0.1 8080 and you should see the output
$ dist/network -l "*:debug"
2025-12-14T14:29:06-08:00: [INFO] [tcpip-stack-socket] Dual IPv4 and IPv6 socket stack: connect
2025-12-14T14:29:09-08:00: [INFO] [application] new tcp connection from IP 127.0.0.1 on port 45528
2025-12-14T14:29:09-08:00: [DEBUG] [application] read: 15 bytes:
hello tcp worldUnix with the ocaml network stack
mirage configure -t unix --net direct
make depends
dune buildLoad the TUN (Tunnel) kernel module
To use tunctl, first install uml-utilities.
sudo apt install uml-utilitiesTo use ifconfig install net-tools
sudo apt install net-toolsYou can see if the TUN module is loaded like this
lsmod | grep tun # this doesn't seem to do anything Load TUN
sudo modprobe tun $ sudo tunctl -u $USER -t tap0
Set 'tap0' persistent and owned by uid 1000
$ sudo ifconfig tap0 10.0.0.1 upRun the program.
sudo dist/network -l "*:debug"In another tab run ping 10.0.0.2
…and you’ll see the output:
$ sudo dist/network -l "*:debug"
2025-12-14T15:05:51-08:00: [DEBUG] [netif] plugging into tap0 with mac 1a:34:c7:6a:96:46 and mtu 1500
2025-12-14T15:05:51-08:00: [INFO] [netif] connect tap0 with mac 1a:34:c7:6a:96:46
2025-12-14T15:05:51-08:00: [INFO] [ethernet] Connected Ethernet interface 1a:34:c7:6a:96:46
2025-12-14T15:05:51-08:00: [INFO] [ARP] Sending gratuitous ARP for 10.0.0.2 (1a:34:c7:6a:96:46)
2025-12-14T15:05:51-08:00: [INFO] [ipv6] IP6: Starting
2025-12-14T15:05:51-08:00: [DEBUG] [ndpc6] ND6: Sending RS
2025-12-14T15:05:51-08:00: [DEBUG] [ndpc6] ND6: Sending NS src=:: dst=ff02::1:ff6a:9646 tgt=fe80::1834:c7ff:fe6a:9646
2025-12-14T15:05:51-08:00: [DEBUG] [ndpc6] IP6: Processing HOPOPT header
2025-12-14T15:05:51-08:00: [INFO] [ndpc6] IP6: Processing unknown option, MSB 5
2025-12-14T15:05:51-08:00: [DEBUG] [ndpc6] IP6: Processing PADN option
2025-12-14T15:05:51-08:00: [INFO] [ndpc6] ICMP6: Unknown packet type: ty=143
2025-12-14T15:05:51-08:00: [DEBUG] [ndpc6] IP6: Processing HOPOPT header
2025-12-14T15:05:51-08:00: [INFO] [ndpc6] IP6: Processing unknown option, MSB 5
2025-12-14T15:05:51-08:00: [DEBUG] [ndpc6] IP6: Processing PADN option
2025-12-14T15:05:51-08:00: [INFO] [ndpc6] ICMP6: Unknown packet type: ty=143
2025-12-14T15:05:52-08:00: [INFO] [ndpc6] ND6: Unsupported ND option in RA: ty=14 len=1
2025-12-14T15:05:52-08:00: [DEBUG] [ndpc6] ND: Received NS: src=:: dst=ff02::1:ff5a:c22d tgt=fe80::c089:f7ff:fe5a:c22d
2025-12-14T15:05:52-08:00: [DEBUG] [ndpc6] SLAAC: fe80::1834:c7ff:fe6a:9646 --> PREFERRED
2025-12-14T15:05:52-08:00: [ERROR] [netif] [read] user program requested cancellation of listen on tap0
2025-12-14T15:05:52-08:00: [INFO] [ipv6] IP6: Started with fe80::1834:c7ff:fe6a:9646
2025-12-14T15:05:52-08:00: [INFO] [tcp.pcb] TCP layer connected on 10.0.0.2/24, fe80::1834:c7ff:fe6a:9646/64
2025-12-14T15:05:52-08:00: [INFO] [udp] UDP layer connected on 10.0.0.2/24, fe80::1834:c7ff:fe6a:9646/64
2025-12-14T15:05:52-08:00: [INFO] [tcpip-stack-direct] Dual TCP/IP stack assembled: mac=1a:34:c7:6a:96:46,ip=10.0.0.2/24, fe80::1834:c7ff:fe6a:9646/64
2025-12-14T15:05:52-08:00: [DEBUG] [tcpip-stack-direct] Establishing or updating listener for stack mac=1a:34:c7:6a:96:46,ip=10.0.0.2/24, fe80::1834:c7ff:fe6a:9646/64
2025-12-14T15:05:52-08:00: [DEBUG] [tcpip-stack-direct] Establishing or updating listener for stack mac=1a:34:c7:6a:96:46,ip=10.0.0.2/24, fe80::1834:c7ff:fe6a:9646/64
2025-12-14T15:05:53-08:00: [DEBUG] [ndpc6] IP6: Processing HOPOPT header
2025-12-14T15:05:53-08:00: [INFO] [ndpc6] IP6: Processing unknown option, MSB 5
2025-12-14T15:05:53-08:00: [DEBUG] [ndpc6] IP6: Processing PADN option
2025-12-14T15:05:53-08:00: [INFO] [ndpc6] ICMP6: Unknown packet type: ty=143
2025-12-14T15:05:53-08:00: [DEBUG] [ndpc6] IP6: Processing HOPOPT header
2025-12-14T15:05:53-08:00: [INFO] [ndpc6] IP6: Processing unknown option, MSB 5
2025-12-14T15:05:53-08:00: [DEBUG] [ndpc6] IP6: Processing PADN option
2025-12-14T15:05:53-08:00: [INFO] [ndpc6] ICMP6: Unknown packet type: ty=143
2025-12-14T15:05:53-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e17e, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:53-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e17f, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:53-08:00: [DEBUG] [ndpc6] IP6: Processing HOPOPT header
2025-12-14T15:05:53-08:00: [INFO] [ndpc6] IP6: Processing unknown option, MSB 5
2025-12-14T15:05:53-08:00: [DEBUG] [ndpc6] IP6: Processing PADN option
2025-12-14T15:05:53-08:00: [INFO] [ndpc6] ICMP6: Unknown packet type: ty=143
2025-12-14T15:05:53-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e1cb, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:53-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e1ef, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:53-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e1f0, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:54-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e24f, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:54-08:00: [DEBUG] [ndpc6] IP6: Processing HOPOPT header
2025-12-14T15:05:54-08:00: [INFO] [ndpc6] IP6: Processing unknown option, MSB 5
2025-12-14T15:05:54-08:00: [DEBUG] [ndpc6] IP6: Processing PADN option
2025-12-14T15:05:54-08:00: [INFO] [ndpc6] ICMP6: Unknown packet type: ty=143
2025-12-14T15:05:55-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e275, off 16384 proto 17, ttl 255, options
2025-12-14T15:05:57-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id e8ce, off 16384 proto 17, ttl 255, options
2025-12-14T15:06:16-08:00: [DEBUG] [ipv4] dropping IP fragment not for us or broadcast IPv4 packet 10.0.0.1 -> 224.0.0.251: id 1841, off 16384 proto 17, ttl 255, options
2025-12-14T15:06:27-08:00: [DEBUG] [ARP] replying to ARP request for 10.0.0.2 from 10.0.0.1 (mac c2:89:f7:5a:c2:2d)
2025-12-14T15:06:27-08:00: [DEBUG] [icmpv4] ICMP echo-request received: ICMP type echo request, code 0, subheader [subheader: id: 63675, sequence 1] (payload
f3 42 3f 69 00 00 00 00 39 02 06 00 00 00 00 00
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
30 31 32 33 34 35 36 37)
2025-12-14T15:06:27-08:00: [DEBUG] [ipv4] ip write: mtu is 1500, hdr_len is 20, size 0 payload len 64, needed_bytes 84
2025-12-14T15:06:28-08:00: [DEBUG] [icmpv4] ICMP echo-request received: ICMP type echo request, code 0, subheader [subheader: id: 63675, sequence 2] (payload
f4 42 3f 69 00 00 00 00 3b 03 06 00 00 00 00 00
10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
30 31 32 33 34 35 36 37)Run it again, but use echo -n hello tcp world | nc -nw1 10.0.0.2 8080
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.pcb] process-syn: [channels=0 listens=0 connects=0]
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.pcb] new-server-connection: [channels=0 listens=0 connects=0]
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Closed - Passive_open -> Listen
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Listen - Send_synack(3774277702) -> Syn_rcvd(3774277702)
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.tcptimer] timerloop
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.tcptimer] timerloop: sleeping for 667000000 ns
2025-12-14T15:18:40-08:00: [DEBUG] [ipv4] ip write: mtu is 1500, hdr_len is 20, size 28 payload len 0, needed_bytes 48
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.pcb] process-ack: [channels=0 listens=1 connects=0]
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.window] sequence validation: seq=2774950133 range=2774950133[262140] res=true
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Syn_rcvd(3774277702) - Recv_ack(3774277703) -> Established
2025-12-14T15:18:40-08:00: [INFO] [application] new tcp connection from IP 10.0.0.1 on port 51776
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.window] sequence validation: seq=2774950133 range=2774950133[262140] res=true
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Established - Recv_ack(3774277703) -> Established
2025-12-14T15:18:40-08:00: [DEBUG] [application] read: 15 bytes:
hello tcp world
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.pcb] Closing connection remote 10.0.0.1,51776 to local 10.0.0.2, 8080
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Established - Send_fin(3774277703) -> Fin_wait_1(3774277703)
2025-12-14T15:18:40-08:00: [DEBUG] [ipv4] ip write: mtu is 1500, hdr_len is 20, size 20 payload len 0, needed_bytes 40
2025-12-14T15:18:40-08:00: [DEBUG] [ipv4] ip write: mtu is 1500, hdr_len is 20, size 20 payload len 0, needed_bytes 40
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.window] sequence validation: seq=2774950148 range=2774950148[262140] res=true
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] finwait2timer 10000000000
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Fin_wait_1(3774277703) - Recv_ack(3774277704) -> Fin_wait_2(0)
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.window] sequence validation: seq=2774950148 range=2774950148[262140] res=true
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Fin_wait_2(0) - Recv_ack(3774277704) -> Fin_wait_2(1)
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] timewait 2000000000
2025-12-14T15:18:40-08:00: [DEBUG] [tcp.state] 1 Fin_wait_2(1) - Recv_fin -> Time_wait
2025-12-14T15:18:40-08:00: [DEBUG] [ipv4] ip write: mtu is 1500, hdr_len is 20, size 20 payload len 0, needed_bytes 40
2025-12-14T15:18:41-08:00: [DEBUG] [tcp.tcptimer] timerloop: stoptimer
2025-12-14T15:18:42-08:00: [DEBUG] [tcp.state] timewait on_close
2025-12-14T15:18:42-08:00: [DEBUG] [tcp.pcb] removing pcb from connection tables: [channels=1 listens=0 connects=0]
2025-12-14T15:18:42-08:00: [DEBUG] [tcp.pcb] removed remote 10.0.0.1,51776 to local 10.0.0.2, 8080 from active channels
2025-12-14T15:18:45-08:00: [DEBUG] [ARP] replying to ARP request for 10.0.0.2 from 10.0.0.1 (mac c2:89:f7:5a:c2:2d)
2025-12-14T15:18:50-08:00: [DEBUG] [tcp.state] finwait2timer: Closed