Skip to content

Switch DCE from NUSE (Linux 4.4) to LKL (Linux 6.12) - #150

Open
Gabrielcarvfer wants to merge 37 commits into
direct-code-execution:masterfrom
Gabrielcarvfer:lkl-port
Open

Gabrielcarvfer wants to merge 37 commits into
direct-code-execution:masterfrom
Gabrielcarvfer:lkl-port

Conversation

@Gabrielcarvfer

@Gabrielcarvfer Gabrielcarvfer commented Oct 9, 2026 •

Copy link
Copy Markdown

There is still an option to keep NUSE and FreeBSD by dropping the commit. But I wont't be the one making this call. :)

Requires #149

Gabrielcarvfer and others added 9 commits October 7, 2026 17:14
Run the GitHub Actions workflow on pushes to master and on every pull request, and drop the obsolete bake-based workflow (Ubuntu 20.04 runners and upload-artifact@v1 no longer exist).

Add utils/build_kernel_deps.sh, which builds what bake used to provide: net-next-nuse (Linux 4.4 libos), freebsd-sim, DCE builds of ip, iperf, thttpd, wget, ping/ping6 and quagga, and the ns-3-dce-quagga module, patched for current ns-3 and toolchains (GCC 11-14, Make 4.4). The workflow caches its output and configures DCE with --enable-kernel-stack.

Fix DCE for ns-3.46 and Linux semantics:
- set the node on Ipv4Interfaces (Linux and FreeBSD glue) and give kernel-stack nodes an inert ArpL3Protocol for CSMA devices
- notify connection success for connectionless kernel sockets
- deliver multicast locally in Ipv4DceRouting (raw sockets, OSPF)
- implement sigwait()
- ns-3 stream sockets: connect() succeeds when the peer accepts and closes at once, and reports ECONNREFUSED when refused
- ns-3 stack: IPv6 UDP sockets, IPV6_JOIN/LEAVE_GROUP, IPV6_RECVPKTINFO, ICMP6_FILTER, IPV6_PKTINFO on send, no IPv6 header on raw receive, delivery for raw-only IPv6 protocols such as OSPFv3
- netlink and pktinfo use Linux-style interface indexes consistently
- reset glibc's getopt state for each simulated process
- drop the NSC stacks from dce-tcp-ns3-nsc-comparison; use 802.11a in the MPTCP handoff examples

Enable the disabled select/poll/tcp-socket/signal process-manager tests, all quagga test cases and the dce-xfrm example. 79/79 pass on Ubuntu 22.04, Ubuntu 24.04 and Debian trixie with ns-3.46.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Ipv4InterfaceAddress no longer has secondary addresses: drop the IsSecondary() checks in the Linux and FreeBSD IPv4 glue
- Ipv4GlobalRouting moved to ns3/global-routing.h
- Address::Register() now takes the address kind and its length
- CI: build against ns-3.49; drop Ubuntu 22.04, whose CMake (3.22) is older than ns-3.49 requires

79/79 tests and examples pass on Ubuntu 24.04 and Debian trixie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TaskManager:
  - RunningTask(): the task whose fiber runs, ignoring the hi task of DceManager temporary tasks (only a fiber can block).
  - RunNow(): from the main fiber, run the ready tasks until a condition holds, without advancing time.
  - SetTaskEndNotifier(): called when a task exits or is stopped.
- LinuxSocketImpl (ns-3 sockets backed by the kernel):
  - Uses the generic UnixFd, not the libos KernelSocketFd.
  - Delivers the data that arrived with the end of a connection before treating the socket as closed.
  - After a send failed with EAGAIN, notifies the application again only once time went on: DCCP reports sockets writable while its sends fail, and the failed send wakes the socket again (zero-time livelock).
- getaddrinfo: ignore AI_ADDRCONFIG (also the default without hints), which checks the host's addresses, not the node's: without IPv6 on the host, IPv6 addresses did not resolve.
- if_nametoindex/if_indextoname ask the kernel of LKL nodes too, and close the socket they open.
- ns3waf: the headers copied to build/include/ns3 are outputs of the copy task, so that waf rebuilds what includes them when they change (stale example objects crashed after a class layout change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
utils/build_lkl.sh builds LKL (Linux 6.12, pinned commit, shallow fetch) for DCE: it applies utils/lkl-patches, configures LKL's defconfig plus utils/lkl-dce.config, and relinks liblkl.so without default libraries (its libc references bind to the simulator's libc; DCE loads a copy per kernel instance) and stripped, with the debug information in liblkl.so.debug. It also copies LKL's headers: configure DCE with --with-lkl=<output>.

utils/lkl-dce.config adds what the examples and tests use, as the libos kernel had it: SCTP, DCCP (CCID 3), MPTCP, AF_PACKET, IPsec and its crypto, Mobile IPv6, tunnels, multicast routing, more TCP congestion controls.

Patch 0001 backports the upstream fix for the 6.12 deadline server counted in nr_running (52d15521eb75), which kept an idle kernel busy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 0002 lkl: stop the tick while the host is idle. The CPU idles in idle_host_task, a regular task, and after system calls while the host task stays current: the tick never stopped, and a simulator processes every tick (~100 per second per node). Enter NOHZ idle there and leave it when work resumes (system call, thread exit, woken task); re-stop the tick after interrupts that wake nothing, at the outermost level only; mark the clocksource valid for high resolution timers.
- 0003 crypto: jitter - do not loop forever on a clock that does not advance. Outside FIPS mode, jent_gen_entropy() never gave up on stuck measurements: the kernel hung at boot on simulated time once the DRBG (pulled in by IPsec) was built in.
- 0004 lkl: show_stack: stop at the first frame outside of the kernel. It followed the frame chain into host code without frame pointers and crashed the host on any kernel warning.

TOUPSTREAM

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
configure --with-lkl=<utils/build_lkl.sh output> builds DCE with LKL, the Linux kernel as a library, as a node's network stack. Without --enable-kernel-stack (libos, Linux 4.4), LKL provides ns3::LinuxSocketFdFactory, so the existing kernel examples and tests run on Linux 6.12 unchanged (the libos library name liblinux.so selects liblkl.so); with it, libos stays ns3::LinuxSocketFdFactory and LKL is ns3::LklSocketFdFactory.

LklKernel (model/lkl/lkl-kernel.*): one kernel per node, its own copy of liblkl.so (a new one for each simulation in a process), with LKL's host operations on DCE:
- Threads are DCE tasks; semaphores and mutexes wait lists of tasks; thread-local storage per task, dropped when the task ends (destructors run in a task of their own).
- Time is the simulation time; timers are simulator events.
- System calls from the main fiber (ns-3 sockets of LinuxSocketImpl) run in a task, driven without advancing time.
- Kernel memory is released when the kernel object is destroyed.

LklSocketFdFactory (model/lkl/lkl-socket-fd-factory.*):
- Boots the kernel at simulation start, mounts procfs.
- Every ns-3 NetDevice becomes a virtio-net device sim<n> (found by MAC address), with the device's MTU; IFF_NOARP when the device cannot carry ARP (point-to-point). The netdev poll task does not re-report frames the kernel has no buffers for; at most 1024 wait.
- Sockets are LklSocketFd, whose operations are system calls; poll and select through a per-node epoll watcher (persistent, edge-triggered: a kernel wait queue).
- Set/Get: sysctls as libos names them (".net.ipv4.tcp_sack"), or any kernel file by absolute path (/proc/net/netstat); ScheduleTask; the Mptcp attribute makes applications' TCP sockets MPTCP sockets.

Build (wscript): LKL_STACK and, without libos, LKL_LINUX; LINUX_STACK (any Linux stack provides ns3::LinuxSocketFdFactory) for LinuxStackHelper, the Linux socket factories and the iperf examples; KERNEL_STACK keeps meaning libos; dce-freebsd is built only with libos.

Examples: dce-lkl-boot (kernels boot, exact 10 s sleep), dce-lkl-p2p (ip configuration, TCP and UDP between two kernels or over loopback; --stack=linux for libos). The cradle, process manager and quagga test suites look for liblkl.so in LKL-only builds (they skipped otherwise).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Routers enable IPv4 forwarding where the examples assumed it, with conf.all.forwarding (conf.default.forwarding only applies to interfaces created afterwards): dce-tcp-ns3-nsc-comparison received nothing on LKL.
- The MPTCP test and examples were written for the out-of-tree MPTCP (v0) that libos never had: the test skipped itself, the examples ran single-path TCP if anything. Now on Linux MPTCP (v1), with LKL:
  - Applications get MPTCP sockets (LklSocketFdFactory::Mptcp); subflows come from "ip mptcp limits/endpoint" (v0's fullmesh path manager, "ip link set multipath" and the net.mptcp.mptcp_* sysctls are gone).
  - iperf servers no longer use -P 1: they stop listening after that many connections, and Linux MPTCP accepts subflows on the listener.
  - dce-mptcp test: runs when .net.mptcp.enabled is 1 and checks that packets from both client addresses reach the server.
  - dce-cradle-mptcp, dce-iperf-mptcp fail unless both paths carry data; the handoff examples report what each address carried.
- build_kernel_deps.sh builds iproute2 6.12 (has "ip mptcp"; also works with libos); LIBOS=0 skips net-next-nuse and freebsd-sim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ered

- LKL is built (utils/build_lkl.sh, cached on the script, the patches and the kernel configuration) and installed with the DCE binaries.
- Matrix: Ubuntu 24.04 and Debian trixie build DCE with LKL as ns3::LinuxSocketFdFactory (no --enable-kernel-stack, dependencies with LIBOS=0); Ubuntu 24.04 also builds the libos configuration (Linux 4.4 and FreeBSD, LKL as ns3::LklSocketFdFactory).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LKL (Linux 6.12) runs every kernel example and test the libos kernel did, so the libos kernel stack goes, and with it the FreeBSD stack, which was built on the same libos API (KernelSocketFdFactory and libos' sim.h headers).

- configure: --enable-kernel-stack is gone; --with-lkl provides ns3::LinuxSocketFdFactory and defines LINUX_STACK (KERNEL_STACK, LKL_LINUX and LKL_STACK are gone).
- LKL takes over the libos classes and files: LklSocketFdFactory and LklSocketFd become KernelSocketFdFactory and KernelSocketFd (model/kernel-socket-fd*), and the LKL LinuxSocketFdFactory moves to model/linux-socket-fd-factory.cc.
- Removed: the libos KernelSocketFdFactory/KernelSocketFd and LinuxSocketFdFactory code, FreeBSDSocketFdFactory, Ipv4FreeBSD, FreeBSDStackHelper, the dce-freebsd and freebsd-iproute examples and the FreeBSD test cases, utils/freebsd-sim-dce.patch, utils/mptcp-build.sh (libos MPTCP kernel); the Python bindings drop their KERNEL_STACK switch.
- utils/build_kernel_deps.sh builds only the programs (ip, iperf, ping, quagga, ...) and the quagga module; CI has one job per distribution.
- Tests and examples drop their libos branches; the process manager test's stack masks are renamed (LINUX_STACK is the build's macro).
- Documentation: the kernel guide, getting started and the subprojects page describe LKL; the FreeBSD page is removed; the kernel developer page is marked as describing the former kernel support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thehajime

Copy link
Copy Markdown
Member

thanks !

I will look into detail.

One immediate note for this: it would be great if you have CI tests, a simple integration with LKL and DCE in this PR.

@thehajime

Copy link
Copy Markdown
Member

One immediate note for this: it would be great if you have CI tests, a simple integration with LKL and DCE in this PR.

sorry, it is already.

I allowed the permission of workflow and now it's running.

anyway, thanks and will be back later.

@thehajime

Copy link
Copy Markdown
Member

@cubic-dev-ai ultrareview

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 10, 2026

Copy link
Copy Markdown

Starting ultrareview. I'll post findings when complete.

@cubic-dev-ai

cubic-dev-ai Bot commented Oct 10, 2026

Copy link
Copy Markdown

@cubic-dev-ai ultrareview

@thehajime Starting ultrareview. I'll post findings when complete.

@direct-code-execution direct-code-execution deleted a comment from cubic-dev-ai Bot Oct 10, 2026
@direct-code-execution direct-code-execution deleted a comment from cubic-dev-ai Bot Oct 10, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultrareview took 11m 54s and investigated 55 possible issues.

All reported issues were addressed across 92 files

Reply to a comment to ask cubic a question or push back. It learns from your replies.
Click Fix with cubic on a comment, or reply @cubic-dev-ai fix this, and cubic writes the fix.

View guided diff | Re-trigger cubic

Comment thread example/dce-mptcp-lte-wifi-v6.cc Resolved
Comment thread model/dce.cc Resolved
Comment thread doc/source/getting-started.rst Resolved
Comment thread doc/source/getting-started.rst Resolved
Comment thread example/dce-mptcp-lte-wifi.cc Resolved
Comment thread example/dce-lkl-boot.cc Resolved
Comment thread example/dce-lkl-boot.cc Resolved
Comment thread example/dce-lkl-boot.cc Resolved
Comment thread example/dce-lkl-boot.cc Resolved
Comment thread model/kernel-socket-fd-factory.h Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultrareview took 14m 35s and investigated 58 possible issues.

All reported issues were addressed across 92 files

Requires human review: Auto-approval blocked because this review re-detected 33 unresolved issues already reported by Cubic.

View guided diff | Re-trigger cubic

Comment thread model/linux/linux-socket-impl.cc Resolved
Comment thread utils/build_kernel_deps.sh Resolved
Comment thread model/kernel-socket-fd-factory.cc Resolved
Comment thread netlink/netlink-socket.cc Resolved
Comment thread .github/workflows/build-and-test.yml Resolved
Comment thread test/dce-manager-test.cc Resolved
Comment thread model/lkl/lkl-kernel.h Resolved
Comment thread example/dce-iperf-emulation.cc Resolved
Comment thread doc/source/dce-user-kernel.rst Resolved
Comment thread model/lkl/lkl-kernel.h Resolved
@thehajime

thehajime commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

cubic review gives us tons of comments, which I wasn't expected...

there are labels, like P1, P2, P3, which I guess the priority number. please take this into account and not spending too much effort on lower prioriy issues.

I'll also give a shot with my eyes to your patches.

anyway, this is great. thank you for your patch !

Gabrielcarvfer and others added 10 commits October 10, 2026 11:58
A process still running when the simulation stops is deleted with it, and its finished callback then reported success: a program that hung passed. The programs that do not end today are listed explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pthread_kill only woke a thread that did not block the signal, while sigwait callers block the signals they wait for: the thread slept forever. sigwait also consumed both the thread's and the process' pending instance of a signal, losing one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The timer host operation runs in the node's tasks, which run on threads of their own with PthreadFiberManager, the default: Simulator::Schedule must not be called there (ns-3 asserts it in debug builds). Use TaskManager::ScheduleMain, like the other events DCE tasks schedule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DCE deletes the tasks of a process it stops (e.g. ApplicationContainer Stop) at once, wherever they are. A task blocked in an LKL system call leaves the kernel referring to it: the entries on its stack in the socket's wait queues, its LKL wait lists. The next wake-up of the socket crashed the simulation (dce-lkl-p2p --stop).

Blocking socket calls (read, recvmsg, sendmsg, accept, connect) are now made non-blocking, and wait in DCE for the socket's events, honoring SO_RCVTIMEO and SO_SNDTIMEO and DCE signals; a blocking send still returns once all its data is queued. The LKL wait lists forget stopped tasks, as do the thread identities of the tasks the kernel did not create, which leaked. The files of a stopped process are released without being closed: release their kernel socket then.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LTE devices do not support promiscuous mode, so the promiscuous protocol handler never got their packets: an LTE UE received nothing (the libos stack registered a non-promiscuous handler for them). The destination address of their frames was also converted from the device's Mac64Address; use the address of the kernel device instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The libos stack's LinuxSocketFdFactory enabled it, and scripts written for it rely on it: routers that set no sysctl did not forward (e.g. dce-mptcp-lte-wifi-v6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dce-mptcp-lte-wifi: the WiFi router was out of range (ns-3 no longer detects frames below -82 dBm), its route to node 1 pointed at its own address, and node 1 had no route back to the WiFi network for the subflow it gets on its LTE-side address.

dce-mptcp-lte-wifi-v6: the mobile node's addresses were hardcoded from an older MAC address allocation; compute them from the devices. The connection starts over IPv4, so the IPv6 subflow needs an IPv6 socket (the IPv4-mapped server address) and the server's IPv6 address, which it now announces; the mobile node's address is configured before the announcement arrives.

Both examples now fail unless the server receives TCP payload from every address of the client, and run with test.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
if_nametoindex returned the IPv4 interface index, while netlink messages and socket options now use NetDevice index + 1, like Linux with its loopback device first: a route added with "dev" could go to another device. Link messages also reported the previous device as the parent link (IFLA_LINK) of each device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a list of targets, its name became their comma-joined list, so the use of it by name did not resolve. (Headers were still generated first: their group is built before the modules'.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tarballs are checked against pinned SHA-256 digests, and thttpd is downloaded over HTTPS. The iperf and ping DCE patches were applied with "|| true", which also accepted a patch that does not apply, building programs without the changes DCE needs; a patch is now skipped only if already applied. The quagga module patch is skipped the same way, which made a second run abort. A source tree from an earlier run must be at the requested revision (e.g. not a libos-era iproute2 without "ip mptcp").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gabrielcarvfer and others added 15 commits October 10, 2026 13:59
dce-lkl-boot: reject --nodes=0, count a failed uname, fail if the sleep took host time, and run long enough for all the nodes. dce-lkl-p2p: check that tcp-server read all that tcp-client sent, not only that it stopped reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The check that every path carries data counted TCP headers, so a path with only a handshake or ACKs passed. The subflow limits were fixed at 4, while the fullmesh endpoints need nRtrs * nRtrs - 1 subflows: derive them, and reject the router counts Linux MPTCP's 8 subflows cannot cover (more than 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mobile node was out of the second access point's range (ns-3 no longer detects frames below -82 dBm): place it within reach of both. Its table 3 default route was added before sim1 was up, and failed; the address added at 15.2 s had no source routing rule, and its MPTCP endpoint was added while the address was still tentative, so no subflow was opened from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a static multicast route, Ipv4StaticRouting only forwards the packet, so raw sockets (e.g. a routing daemon's for 224.0.0.5) stopped receiving the group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On EMFILE, dce_socket leaked the socket it had created: with LKL, its kernel socket, so that the kernel soon ran out of descriptors too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The interface selected with IPV6_PKTINFO was ignored when sending on a connected socket. Like Linux, sendmsg now fails with ENODEV for an interface that does not exist, instead of using the default route, and with EINVAL for a control message longer than the control buffer, instead of reading past it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The option was accepted and ignored, so the socket received the ICMPv6 types it blocked. ns-3 does not expose its raw socket's filter: DCE keeps it, drops the blocked messages when receiving, and returns it from getsockopt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a send failed with EAGAIN, the poll loop notified the application again only once time went on, and then waited for the socket to wake: when the kernel had already reported the socket writable at the same time, nothing woke it, and the application stopped sending (DCCP bulk transfers ran at a fourth of their rate). Wait at most 1 ms then.

dce-tcp-ns3-nsc-comparison now fails if a flow stalls or the flows use little of the bottleneck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each node loads a copy of liblkl.so from elf-cache, where gdb did not find the liblkl.so.debug its debug link names: link it next to the copy when it is next to the library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…odule

The build instructions only mentioned build_kernel_deps.sh: run it, add its ns-3-dce-quagga module before configuring, and copy its programs and the kernel's debug information to build/bin_dce.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop an unused include from lkl-kernel.h, say that the loopback device gets no kernel device, and point dce-iperf-emulation to --with-lkl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WaitPoint dropped a wake-up that arrived while its thread was not waiting yet. dce_poll registers its waits as it polls each file, then waits: with LKL, polling a later file is a system call that can wait for the kernel's CPU, during which an event of an earlier file was lost, and poll slept until another event or its timeout. Remember such a wake-up, so that the next wait returns at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sctp-client passed its uninitialized flags to sctp_recvmsg, as the flags of recvmsg: every receive failed, and the client closed the association without reading anything. The example now fails unless the client receives messages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mobile node was out of range of the access points (ns-3 no longer detects frames below -82 dBm), so iperf could not even connect: place it within reach of both. Its sim1 routes were added before sim1 was up. With an IPv4 primary path, the client now uses the IPv4-mapped address, so that its MPTCP socket can also have IPv6 subflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ut data

With --bulk=1 and an IPv6 protocol, BulkSend sent to the sending node's own address, so the sink received nothing. The example now fails when a flow carries no data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Gabrielcarvfer

Copy link
Copy Markdown
Author

Updated with some fixes (see verdict below). Some open design issues for maintainers below.

Decisions for maintainers

PR #150 review: decisions left to the maintainers

The automated review of PR #150 (cubic-dev-ai ultrareview, 62 inline
comments) was checked comment by comment; the verdict for each, its
reasoning, the fixing commit and a draft reply are in
PR150-review-verdicts.md. The fixes are local commits on branch
lkl-port-review (on top of origin/lkl-port at bd598bd); with them the
full python3 test.py -r passes, 88 of 88.

Priority Valid Partly valid Invalid
P1 (17) 10 5 2
P2 (41) 26 10 5
P3 (4) 4 0 0

The points below change behaviour or were left open, and are for the
maintainers to decide.

1. Behaviour changes made by the fixes

  • Blocking LKL socket calls wait in DCE, not in the kernel (b0ce167).
    Stopping a process blocked in an LKL socket call used to crash the
    simulation (reproduced). Blocking calls now wait on the DCE side, stopped
    tasks leave the LKL wait lists, and thread identities no longer leak.
  • IPv4 forwarding is on by default for LinuxSocketFdFactory
    (ad1b540), as it was with the libos (Linux 4.4) stack.
  • The MPTCP examples reject --nRtrs above 3 (a3b6a91). Their
    subflow limits are now derived from nRtrs; with 5 or more routers the
    LKL kernel panics with "System is deadlocked on memory" at mem=64M
    during a 200 s bulk transfer (not investigated further).
  • Example positions and addresses changed in the handoff and LTE
    examples (213cdfd, c5d7d0d, 73c36d5), so that every path actually
    carries data; the examples now fail when it does not.
  • The process-test harness is strict (ba5fa65): a test program that
    never ends now fails instead of passing. Four existing programs never
    end and are listed as exceptions (test-timer-fd, test-local-socket,
    test-bug-multi-select, and test-socket on Linux); they were hidden
    before and may deserve a look.

2. Valid comments that were not fixed

  • Per-socket IPv6 multicast membership (P2, model/unix-socket-fd.cc):
    membership is kept per node, so one socket leaving a group affects the
    others. Fixing it needs per-socket tracking.
  • Python bindings (P2, wscript): --with-lkl builds drop the Linux
    stack API from the generated bindings; the API definitions need
    regenerating.

3. Comments judged partly valid or invalid

Argued one by one in PR150-review-verdicts.md. Among them, the getopt
P1 (model/dce.cc, glibc's hidden permutation state across processes) is
only theoretical and would need DCE's own getopt to address.

Reviews verdict from Claude
# PR #150 review (cubic): verdicts

Branch `lkl-port-review` on top of `origin/lkl-port` (bd598bd). One entry per review comment, in priority order.

| Priority | VALID | PARTIALLY VALID | INVALID |
|---|---|---|---|
| P1 | 10 | 5 | 2 |
| P2 | 26 | 10 | 5 |
| P3 | 4 | 0 | 0 |

## P1 .github/workflows/build-and-test.yml:58, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600463
- Claim: The LKL build is missing `libelf-dev` from the container dependencies. `build_lkl.sh` invokes Linux Kbuild host tooling, which requires libelf headers; add `libelf-dev` so both matrix jobs can reach the DCE build.
- Verdict: **INVALID**. Kbuild needs libelf only for objtool/BTF/module signing, none of which LKL's config (utils/lkl-dce.config on the lkl defconfig) enables. Both matrix jobs passed at bd598bd, and LKL was built locally in a container without libelf-dev.
- Fix: none
- Draft reply: LKL's configuration does not use objtool, BTF or module signing, so Kbuild never needs libelf here: both CI jobs built LKL and passed at bd598bd, and it also builds in a container without libelf-dev.

## P1 doc/source/getting-started.rst:227, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584087
- Claim: This dependency step is incomplete: the recipe never runs `build_kernel_deps.sh` and omits its `ns-3-dce-quagga` module. Add the build and, before `waf configure`, merge `bin_dce` contents into `build/bin_dce` and copy the quagga module into `myscripts`; otherwise kernel examples cannot find `ip`/`iperf`/`ping`, and quagga examples are not configured.
- Verdict: **PARTIALLY VALID**. getting-started.rst:225 mentioned build_kernel_deps.sh without commands and did not mention the ns-3-dce-quagga module, which must be in myscripts/ before configure. Copying bin_dce after the build (as CI does) is fine.
- Fix: 748c45d
- Draft reply: Agreed, the recipe now runs build_kernel_deps.sh, copies its ns-3-dce-quagga module into myscripts/ before configuring, and copies its bin_dce (and liblkl.so.debug) into build/bin_dce. Fixed in 748c45d.

## P1 doc/source/getting-started.rst:225, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584091
- Claim: A fresh WAF build never sets `DCE_PATH`, so copying `liblkl.so` here still makes every Linux-stack run fail with “LKL library not found.” Export this build directory after the copy.
- Verdict: **INVALID**. The examples are run with ./waf --run, and wutils.py:105-111 sets DCE_PATH to include build/bin_dce (test.py does the same from LD_LIBRARY_PATH), so liblkl.so copied there is found.
- Fix: none
- Draft reply: ./waf --run (used by the following examples) sets DCE_PATH to include build/bin_dce (wutils.py), so the copied liblkl.so is found without exporting anything.

## P1 example/dce-mptcp-lte-wifi-v6.cc:289, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584072
- Claim: Because the connection starts over `sv_sim0_v4`, the client’s IPv6 `subflow` has no IPv6 peer address: this server never signals `2001:1:2:3::1`. Add a server `signal` endpoint so the example actually creates the Wi-Fi subflow.
- Verdict: **VALID**. Confirmed by running it: no IPv6 subflow was ever opened. Worse, the example could not work at all: LTE UEs received nothing (see the Mac64 thread), the router had no IPv4 forwarding, the mobile node's addresses were hardcoded from an older MAC allocation, and the IPv4 connection used an AF_INET socket, which cannot have IPv6 subflows.
- Fix: 213cdfd (with d44e6d9, ad1b540)
- Draft reply: Confirmed: no IPv6 subflow was opened, and the example had more problems (LTE UEs received nothing, no IPv4 forwarding on R, addresses hardcoded from an older MAC allocation, and an AF_INET socket that cannot have IPv6 subflows). Fixed in 213cdfd: the server signals 2001:1:2:3::1, the client uses the IPv4-mapped address, and the addresses come from the devices. The example now fails unless both paths carry payload, and it runs in test.py.

## P1 example/dce-mptcp-lte-wifi.cc:193, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584094
- Claim: This endpoint setup does not establish the advertised WiFi subflow in the default LTE+WiFi scenario: node 1 never signals its WiFi address, while replies for the LTE primary address use the LTE policy table. Add a server-side `mptcp endpoint add 10.2.1.1 dev sim1 signal` (only when both links are enabled) or provide an equivalent return path so the client can create a reachable WiFi subflow.
- Verdict: **VALID**. Confirmed by pcap: the WiFi MP_JOIN got no SYN-ACK. Node 1 replied from 10.2.0.1 through the LTE side. On top of that, the WiFi router was out of range (ns-3 drops frames below -82 dBm) and routed 10.2.0.0/16 via its own address.
- Fix: 213cdfd
- Draft reply: Confirmed with pcaps. The return route was missing, and the WiFi router was out of range and routed 10.2.0.0/16 via its own address. Fixed in 213cdfd: node 1 routes 10.1.0.0/16 back through the WiFi router. The example now checks that both paths carry TCP payload (about 9 MB each) and runs in test.py.

## P1 model/dce-signal.cc:127, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584112
- Claim: A thread blocked in `sigwait` can deadlock when another thread uses `pthread_kill` to send one of the waited-for signals. Update the thread-directed signal path to wake a blocked target when its `sigwaitSet` contains the signal, just as `UtilsSendSignal` does for process-directed signals.
- Verdict: **VALID**. dce-pthread.cc:395 woke the target only if the signal was unblocked, and sigwait callers block the signals they wait for, so the thread slept forever.
- Fix: ad02895
- Draft reply: Confirmed. Fixed in ad02895: pthread_kill now also wakes a thread whose sigwait set contains the signal. test-signal covers it (it hung before the fix).

## P1 model/dce.cc:458, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584083
- Claim: Switching processes after one has advanced `optind` past 1 leaves glibc’s hidden permutation state from the previous process, so `getopt` can parse the wrong `argv` and return incorrect options. Preserve or replay the complete parser state per process instead of skipping reset solely when `optind > 1`.
- Verdict: **PARTIALLY VALID**. The hidden glibc state is shared, but DCE threads are cooperative: processes only switch inside blocking calls, so another process's getopt can only run between two getopt calls of a program that blocks in the middle of its option loop. Every new image starts with optind 0, which makes glibc reinitialize. Saving glibc's private state is not possible without reimplementing getopt.
- Fix: none
- Draft reply: True in principle, but DCE processes only switch inside blocking calls, so this needs a program that blocks in the middle of its getopt loop. Every program image starts with optind 0, which makes glibc reinitialize, and before this PR there was no reset at all. Replaying glibc's private state would need our own getopt implementation, so this is not addressed here.

## P1 model/linux/linux-socket-impl.cc:673, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600441
- Claim: `m_sendBlocked` suppresses `NotifySend` after an EAGAIN, then the following `table->Wait (Seconds (0))` blocks indefinitely rather than waiting for time to advance. LKL may have already consumed the only POLLOUT edge, so the socket never retries; re-arm a timed poll or retry when suppressing the callback.
- Verdict: **VALID**. Reproduced: dce-tcp-ns3-nsc-comparison --stack=dce-dccp --bulk=1 ran at 131 kbps, with the sender stuck waiting for NotifySend. With a 1 ms retry when the notification is suppressed, it ran at 496 kbps.
- Fix: b727b87
- Draft reply: Reproduced: DCCP bulk transfers through LinuxSocketImpl stalled (131 kbps against 496 kbps). Fixed in b727b87: when the notification is suppressed, the poll loop retries after 1 ms instead of waiting indefinitely. The example now fails when a flow stalls or uses little of the bottleneck.

## P1 model/lkl/lkl-kernel.cc:365, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584096
- Claim: `TimerSetOneshot` schedules from an LKL task instead of the main simulator thread. Use `TaskManager::ScheduleMain` to post this event, otherwise timer setup violates the scheduler contract and can corrupt event/context handling.
- Verdict: **VALID**. lkl-kernel.cc:365 called Simulator::Schedule from LKL tasks. With the default PthreadFiberManager these run on other threads, and DefaultSimulatorImpl::Schedule asserts the main thread in ns-3 debug builds.
- Fix: 94bcc3e
- Draft reply: Agreed. Fixed in 94bcc3e: the timer is now scheduled with TaskManager::ScheduleMain.

## P1 model/lkl/lkl-kernel.cc:173, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584101
- Claim: These wait lists retain blocked tasks after `TaskManager::Stop` deletes them, so a later semaphore or mutex wake dereferences a freed `Task*`. Remove stopped tasks from every LKL wait list before deletion.
- Verdict: **VALID**. Reproduced: stopping a process blocked in recv and then sending to its socket crashed in __wake_up_common. The kernel's wait-queue entry was on the deleted task's stack, so removing the task from the LKL wait lists alone was not enough.
- Fix: b0ce167
- Draft reply: Reproduced, as a segfault in the kernel's __wake_up_common: the wait-queue entry lives on the deleted task's stack. Fixed in b0ce167: blocking socket calls no longer block in the kernel; they are made non-blocking and wait in DCE, honoring SO_RCVTIMEO and SO_SNDTIMEO. The LKL wait lists also forget stopped tasks, and a stopped process's sockets are closed. dce-lkl-p2p --stop (now in test.py) covers it.

## P1 model/lkl/lkl-socket-fd-factory.cc:605, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584102
- Claim: `EPOLLET` can lose a readiness transition between `LklSocketFd::Poll`'s zero-time probe and its wait registration, leaving a receiving or connecting task asleep forever. Register the wait before probing, or use a notification scheme that cannot consume an edge without an active waiter.
- Verdict: **PARTIALLY VALID**. EPOLLET itself is not the problem: each Poll does a level-triggered probe, PollWait registers before Watch, and epoll_ctl ADD reports current readiness. But an event that arrived after the registration and before the poller called Wait was dropped by WaitPoint::WakeUpCallback (m_waitTask == 0). With LKL that window is real, because a later system call in the same dce_poll can wait for the kernel's CPU lock.
- Fix: 8086dcc
- Draft reply: The edge-triggered epoll is only the wake-up source: Poll probes the level each time, and registration precedes Watch. A wake-up could be lost, though, in the window between registration and WaitPoint::Wait, for example when polling another fd waits for LKL's CPU lock. Fixed at the root in 8086dcc: a wake-up that arrives before Wait is remembered.

## P1 model/lkl/lkl-socket-fd-factory.cc:544, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600453
- Claim: The RX path still assumes every NetDevice has a Mac48 address, even though `AddDevice` explicitly accepts other address types. A Mac64/other-address device will hit `Mac48Address::ConvertFrom(device->GetAddress())` here, while its source address becomes all zeroes; convert supported address types consistently and retain the per-device LKL MAC.
- Verdict: **VALID**. Worse than claimed: LTE devices do not support promiscuous mode, so the promiscuous handler (lkl-socket-fd-factory.cc:521) never received anything from an LTE UE. The libos factory registered non-promiscuous handlers for LTE. The Mac64 conversion at :545 was also wrong.
- Fix: d44e6d9
- Draft reply: Confirmed, and worse: LTE devices do not support promiscuous mode, so an LTE UE received nothing at all. Fixed in d44e6d9: LTE devices get a non-promiscuous handler (as with libos), and frames from devices with non-Mac48 addresses use the kernel device's own MAC as destination. dce-mptcp-lte-wifi --disWifi=1 (now in test.py) covers it.

## P1 netlink/netlink-socket.cc:1304, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600459
- Claim: Native ns-3 route commands now resolve `RTA_OIF` against the wrong device: this lookup expects Linux’s 1-based ifindex, while `dce_if_nametoindex()` supplies the 0-based IPv4 interface index. Normalize the native index to `GetIfIndex () + 1` or translate the input using the actual device ifindex before subtracting one; otherwise routes using `dev` can target loopback or the preceding interface.
- Verdict: **VALID**. dce.cc:729 returned the IPv4 interface index for the ns-3 stack, while netlink (netlink-socket.cc:1304) and the socket options use NetDevice index + 1.
- Fix: c535c3b
- Draft reply: Confirmed. Fixed in c535c3b: if_nametoindex now returns NetDevice index + 1 for the ns-3 stack, consistent with netlink. New test test-ifindex.

## P1 ns3waf/__init__.py:375, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584117
- Claim: `target=[target] + copies` changes this Waf task generator’s name from `target` to a comma-joined list, but the following `use=[target]` still resolves the old name. The `NS3_HEADERS_*` dependency is therefore lost, allowing targeted or incremental builds to compile against missing or stale generated headers; keep the producer name explicit.
- Verdict: **PARTIALLY VALID**. The name did change and `use=[target]` no longer resolves. But that reference had no effect anyway (the NS3_HEADERS tgen has no C/C++ features, so `use` is not processed), and the header group is built before the module groups even for --targets builds (verified by deleting the generated headers and building one example target).
- Fix: b09e89b
- Draft reply: The name did change, so the reference no longer resolved. In practice headers are still generated first, because their group is built before the modules' even with --targets (verified). Restored the name anyway in b09e89b.

## P1 utils/build_kernel_deps.sh:80, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584107
- Claim: `|| true` lets a patch mismatch produce an unpatched iperf binary. The required `sleep(1)` is what lets DCE escape iperf's `thread_rest` loop, so reject patch failures while allowing an already-applied patch.
- Verdict: **VALID**. `patch -N ... || true` also accepted a patch that does not apply.
- Fix: c941c90
- Draft reply: Agreed. Fixed in c941c90: a patch is skipped only if it reverse-applies (already applied), and otherwise must apply.

## P1 utils/build_kernel_deps.sh:88, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600449
- Claim: This plaintext archive is compiled into the shipped DCE binaries without integrity verification, allowing an on-path attacker to inject build-time code. Use an authenticated HTTPS source and verify a pinned digest for every archive.
- Verdict: **PARTIALLY VALID**. Only thttpd was fetched over plain HTTP (the other tarballs already used HTTPS), and none was verified.
- Fix: c941c90
- Draft reply: thttpd was fetched over plain HTTP. Fixed in c941c90: every tarball is fetched over HTTPS and checked against a pinned SHA-256 (quagga's matches the MD5 in Fedora's lookaside URL; the git sources are pinned by tag or commit).

## P1 utils/lkl-patches/0003-crypto-jitter-do-not-loop-forever-on-a-clock-that-do.patch:44, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584115
- Claim: This also breaks on an intermittent RCT failure, not only on a clock that never advances. Since `jent_gen_entropy()` is `void` and this path reports no error, a transient health-test event can return a partially generated entropy value; use a separate no-progress limit for the simulated-clock case or propagate the failure so the caller discards the generation.
- Verdict: **VALID**. Also on real hardware: health_failure is never cleared outside FIPS mode, so after one intermittent RCT failure every jent_gen_entropy() call would break at the first stuck sample.
- Fix: 8c7e064
- Draft reply: Agreed, and the RCT flag stays set outside FIPS mode, so it would have degraded every later generation. Fixed in 8c7e064: the loop gives up only after 1024 consecutive stuck measurements, far above the RCT cutoffs. Rebuilt LKL with it, and the kernels boot fine.

## P2 doc/source/dce-user-kernel.rst:102, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584136
- Claim: `dce-sctp-simple` invokes `RunIp`, but these steps never build or copy the `ip` binary. Run `utils/build_kernel_deps.sh` and put its `bin_dce` output in `build/bin_dce` before running the example; otherwise DCE cannot launch `ip`.
- Verdict: **VALID**. dce-user-kernel.rst never said to build or copy ip.
- Fix: 748c45d
- Draft reply: Fixed in 748c45d: the steps now run build_kernel_deps.sh and copy its bin_dce.

## P2 example/dce-cradle-mptcp.cc:129, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584125
- Claim: `nRtrs` is unbounded, but this line fixes both MPTCP limits at four while the result loop requires every router path to carry traffic. Reject unsupported router counts or derive and validate the limits from `nRtrs`.
- Verdict: **VALID**. The fullmesh endpoints need nRtrs*nRtrs-1 subflows. With the fixed limit of 4, --nRtrs=4 left 10.1.3.1 without data.
- Fix: a3b6a91
- Draft reply: Confirmed. Fixed in a3b6a91: the limits are derived from nRtrs, and router counts that Linux's 8 subflows cannot cover (more than 3) are rejected.

## P2 example/dce-cradle-mptcp.cc:25, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584130
- Claim: `ServerRx` counts TCP headers, so a subflow SYN or pure ACK makes `g_received` positive even when that path carries no application payload. Count only TCP payload before `bytes > 0`.
- Verdict: **VALID**. ServerRx counted IP payload, TCP header included.
- Fix: a3b6a91
- Draft reply: Fixed in a3b6a91: only TCP payload is counted.

## P2 example/dce-iperf-mptcp.cc:129, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584140
- Claim: `nRtrs` can create more secondary paths than the fixed `subflows 4 add_addr_accepted 4` limit permits. The final loop still requires every `10.1.i.1` to report bytes, so larger configurations fail despite valid paths; derive these limits from `nRtrs` or reject unsupported values.
- Verdict: **VALID**. Same as dce-cradle-mptcp: --nRtrs=5 left 10.1.4.1 at 0 bytes.
- Fix: a3b6a91
- Draft reply: Confirmed (--nRtrs=5 left one path empty). Fixed in a3b6a91, as in dce-cradle-mptcp.

## P2 example/dce-iperf-mptcp.cc:25, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584149
- Claim: `ServerRx` counts the TCP header as received data, so a secondary subflow handshake can make `bytes > 0` even when that path carries no iperf payload. Remove the TCP header and count only a positive TCP payload before updating `g_received`.
- Verdict: **VALID**. Same header counting as dce-cradle-mptcp.
- Fix: a3b6a91
- Draft reply: Fixed in a3b6a91: only TCP payload is counted.

## P2 example/dce-lkl-boot.cc:92, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584241
- Claim: `--nodes=0` schedules no checks, leaving both counters at zero, so the example reports `OK (0/0)` without booting a kernel. Reject zero nodes.
- Verdict: **VALID**. --nodes=0 printed OK (0/0).
- Fix: c8e5e9d
- Draft reply: Fixed in c8e5e9d: --nodes=0 is rejected.

## P2 example/dce-lkl-boot.cc:31, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584246
- Claim: `uname` failures are only printed, then `ret` is overwritten by `nanosleep`; a broken uname syscall can therefore still produce `OK`. Count a nonzero uname result as a failure.
- Verdict: **VALID**. A failing uname was only printed.
- Fix: c8e5e9d
- Draft reply: Fixed in c8e5e9d: a failing uname counts as a failure.

## P2 example/dce-lkl-boot.cc:88, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584248
- Claim: The fixed 30-second stop truncates valid larger `--nodes` runs: with `--nodes=40`, the last check starts at 20.5 s and cannot finish its 10-second sleep. Derive the stop time from `nNodes`.
- Verdict: **VALID**. Stop at 30 s cut off the last nodes for --nodes=40.
- Fix: c8e5e9d
- Draft reply: Fixed in c8e5e9d: the stop time is derived from the node count (--nodes=40 passes).

## P2 example/dce-lkl-boot.cc:49, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584252
- Claim: `wall` is measured and printed but never checked, so a host-blocking 10-second sleep still reports `OK`. Add a wall-time bound to the failure condition.
- Verdict: **VALID**. Wall time was not checked.
- Fix: c8e5e9d
- Draft reply: Fixed in c8e5e9d: a sleep that takes more than 5 s of host time fails.

## P2 example/dce-lkl-p2p.cc:37, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584172
- Claim: The marker is not evidence that all data arrived: a truncated TCP client or failed UDP reads can still make the server print it and increment `g_succeeded`. Make the servers report exact received counts and validate those counts here.
- Verdict: **PARTIALLY VALID**. tcp-server prints its marker even when the stream ends early. udp-server only prints it after 1000 blocking reads.
- Fix: c8e5e9d
- Draft reply: Valid for TCP. Fixed in c8e5e9d: tcp-server must report all 1024000 bytes. udp-server prints the marker only after 1000 successful blocking reads, so it already means that all datagrams arrived.

## P2 example/dce-mptcp-handoff-v6.cc:260, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584155
- Claim: The new endpoint has no matching source-policy route: the only `sim1` rule targets the old address, which `addr change` replaces. Add a rule for `2001:1:2:7::3939` before this command; otherwise this subflow cannot route through `sim1`.
- Verdict: **VALID**. There was no rule for the new address. Running it also showed that no sim1 subflow was ever opened: the table 3 route was added while sim1 was down, the mobile node was out of AP2's range, and the 3939 endpoint was added while the address was still tentative.
- Fix: c5d7d0d
- Draft reply: Confirmed, and running it showed that no sim1 subflow ever opened (the table 3 route was added while sim1 was down, AP2 was out of range, and the endpoint was added during DAD). Fixed in c5d7d0d, and the sim1 path now carries data.

## P2 model/dce-manager.cc:341, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584161
- Claim: `Execve` reuses the `Process` object, so `getoptStarted` remains true after a successful exec. Reset it when installing the new image; otherwise the new program can inherit the old image's getopt state and parse its first options incorrectly.
- Verdict: **INVALID**. Each program image has its own optind, initialized to 0 (libc-global-variables.cc:12), and optind 0 makes glibc reinitialize. A test that exec's a permuting parser after a "+" parser passes without any change.
- Fix: none
- Draft reply: Every loaded image has its own optind, starting at 0, which makes glibc reinitialize on the new program's first call. A test that exec's a program after a "+" (REQUIRE_ORDER) parser parses correctly as is.

## P2 model/dce-signal.cc:121, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584211
- Claim: `dce_sigwait` consumes both the thread- and process-pending instance of `s`. A thread-directed and process-directed occurrence of the same signal can therefore make one `sigwait` lose two pending signals; delete only the set that supplied the match.
- Verdict: **VALID**. dce-signal.cc:120 cleared both pending sets.
- Fix: ad02895
- Draft reply: Fixed in ad02895: one occurrence is consumed, the thread-directed one first. test-signal covers it (it failed before the fix).

## P2 model/ipv4-dce-routing.cc:86, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584123
- Claim: `RouteInput` returns before the new `lcb` path whenever the inherited routing finds a multicast forwarding route. That path invokes `mcb`, not local delivery, so installing a static multicast route makes raw OSPF sockets stop receiving the group; invoke `lcb` in addition to forwarding.
- Verdict: **VALID**. With a static multicast route, Ipv4StaticRouting::RouteInput only calls mcb.
- Fix: 577d665
- Draft reply: Fixed in 577d665: multicast is delivered locally, and also forwarded when there is a route. Covered by the new dce-ipv4-routing test suite.

## P2 model/lkl/lkl-kernel.cc:80, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584170
- Claim: Application and timer tasks that use `thread_self` leak their `LklThread` records because their normal `TaskManager::Exit` path never erases `g_threads`. Clean these identities from `TaskEnd`; otherwise recycled `Task*` addresses can inherit stale LKL thread IDs.
- Verdict: **VALID**. Identities created by ThreadOf for non-kernel tasks were never erased.
- Fix: b0ce167
- Draft reply: Fixed in b0ce167: TaskEnd drops the identity of tasks the kernel did not create.

## P2 model/lkl/lkl-kernel.h:71, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600517
- Claim: `m_start` uses a variadic function-pointer type, but LKL exports `lkl_start_kernel(const char *)`. Use the exact non-variadic signature so the dynamically resolved entry point is called through a compatible type.
- Verdict: **INVALID**. LKL defines it as variadic: arch/lkl/kernel/setup.c:79 `int __init lkl_start_kernel(const char *fmt, ...)`.
- Fix: none
- Draft reply: lkl_start_kernel is variadic in this LKL (`int lkl_start_kernel(const char *fmt, ...)` in arch/lkl/kernel/setup.c), so the pointer type matches.

## P2 model/lkl/lkl-linux-socket-fd-factory.cc:12, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600491
- Claim: `LinuxSocketFdFactory` now inherits LKL's startup path, which omits the old factory's default forwarding setup. Nodes selecting this compatibility type no longer enable IPv4 forwarding automatically, breaking router scenarios unless every caller adds a sysctl; preserve the defaults or explicitly migrate the contract.
- Verdict: **VALID**. The libos LinuxSocketFdFactory set .net.ipv4.conf.all.forwarding=1. dce-mptcp-lte-wifi-v6's router relied on it.
- Fix: ad1b540
- Draft reply: Confirmed (dce-mptcp-lte-wifi-v6's router did not forward). Fixed in ad1b540: LinuxSocketFdFactory enables IPv4 forwarding at boot, before the scripts' own sysctls.

## P2 model/lkl/lkl-socket-fd-factory.cc:98, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584194
- Claim: `SendMain` discards `NetDevice::Send`'s failure result, but `NetTx` always reports the full frame as transmitted. Propagate the result back through the main-thread event and return an appropriate failure or short-write result to LKL.
- Verdict: **PARTIALLY VALID**. The result is dropped, but a failed Send is a full device queue, which is the same as a NIC dropping the frame. Returning an error to LKL's virtio backend would leave the request uncompleted (virtio_process_queue stops on errors), stalling TX until the next kick.
- Fix: none
- Draft reply: A failed NetDevice::Send means the device queue is full, which amounts to a NIC dropping the frame. Reporting it to LKL's virtio backend would leave the TX request pending until the next kick, which is worse, so this is left as is.

## P2 model/lkl/lkl-socket-fd-factory.cc:291, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600487
- Claim: The factory never owns or stops the infinite watcher task. Stop and wake the watcher, close its epoll descriptor, and only then release `m_kernel`; otherwise teardown leaks a task per node and a pending epoll wake can dereference the cleared kernel.
- Verdict: **PARTIALLY VALID**. Nothing stops the watcher, but the factory is only disposed when the simulation is destroyed, when no events or tasks run any more.
- Fix: none
- Draft reply: The watcher is not stopped, but the factory is only disposed in Simulator::Destroy, when no task runs any more, so it cannot touch the cleared kernel.

## P2 model/lkl/lkl-socket-fd.cc:27, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584177
- Claim: `Register` runs before any `FileUsage` owns the wrapper. When `dce_socket` cannot allocate a DCE fd, its `EMFILE` branch returns without closing or deleting this object, leaking the LKL fd and factory entry; close the created socket on that failure path.
- Verdict: **VALID**. The EMFILE path leaked the socket. With LKL, the kernel then ran out of descriptors and later socket() calls returned EINVAL.
- Fix: 4f9551f
- Draft reply: Confirmed (with LKL, socket() then failed with EINVAL once the kernel ran out too). Fixed in 4f9551f. New test test-emfile.

## P2 model/lkl/lkl-socket-fd.cc:266, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584182
- Claim: `Poll` bypasses `Call`, so a negative LKL `ppoll` result leaves `Current()->err` unchanged while returning `POLLERR`; `dce_poll` treats that mask as a ready descriptor. Route this syscall through `Call` or explicitly set the negative return as `errno`.
- Verdict: **INVALID**. A per-fd error is reported in revents, as poll does. ppoll on one valid kernel fd only fails for EFAULT/ENOMEM, and dce_poll returns a count, not -1, so errno is irrelevant.
- Fix: none
- Draft reply: poll reports per-descriptor errors in revents, which POLLERR does. dce_poll returns a count and not -1 here, so errno is not consulted.

## P2 model/lkl/lkl-socket-fd.cc:63, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584191
- Claim: `Close` removes the socket from the factory and closes it without waking `PollTable` waiters. A blocked DCE poll, including `LinuxSocketImpl::Poll`, can therefore wait forever after a socket close; wake waiters with `POLLHUP` before unregistering.
- Verdict: **PARTIALLY VALID**. Linux does not wake poll() waiters when another thread closes the fd either. The only effect is that a LinuxSocketImpl poll task lingers after Close, never woken.
- Fix: none
- Draft reply: Linux does not wake a poll() blocked in another thread when the fd is closed either. The remaining effect is a lingering LinuxSocketImpl poll task, which is not addressed here.

## P2 model/ns3-socket-fd-factory.cc:161, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584235
- Claim: This insertion outlives the raw descriptor that required it. After the last raw-only socket for a protocol closes, packets with that next header still match `DceIpv6RawOnlyProtocol`, are discarded with `RX_OK`, and no longer receive the normal unknown-protocol ICMPv6 response; track handlers per protocol and remove each one when its last socket closes.
- Verdict: **PARTIALLY VALID**. True, but the only effect is a missing ICMPv6 parameter problem for that next header after the last raw socket closes. Reference counting needs hooks in the close path.
- Fix: none
- Draft reply: True, but the only effect is that the node stops answering an ICMPv6 parameter problem for that protocol after its daemon exits. Leaving it for now.

## P2 model/task-manager.h:132, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584228
- Claim: `RunningTask()` returns a task during `m_todoOnMain` callbacks even though those callbacks execute on `m_mainFiber`. Return `0` for that window, or track the executing fiber separately; otherwise LKL per-task TLS and mutex state can be attributed to the suspended task.
- Verdict: **PARTIALLY VALID**. RunningTask() is non-null in that window, but the only callbacks run there (SendMain, MainSchedule) never reach LKL's TLS or mutex host operations.
- Fix: none
- Draft reply: True, but the callbacks run there (device Send, Simulator::Schedule) never reach LKL's TLS or mutex operations, so no state is misattributed.

## P2 model/unix-datagram-socket-fd.cc:305, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584215
- Claim: An invalid positive `ipi6_ifindex` is silently treated as if no interface were requested, so the datagram can take the default route instead of failing as Linux does. Detect this case and return `ENODEV`/`EINVAL` before sending.
- Verdict: **VALID**. An out-of-range ipi6_ifindex was ignored.
- Fix: 9fdf09f
- Draft reply: Fixed in 9fdf09f: ENODEV, like Linux. New test test-ipv6-pktinfo (it passes on both stacks).

## P2 model/unix-datagram-socket-fd.cc:297, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584218
- Claim: The selected `IPV6_PKTINFO` interface is ignored for connected datagram sends because this value reaches only `MainSendTo`, not the `msg_name == nullptr` `MainSend` path. Pass the selected device through that path so connected IPv6 `sendmsg` honors the ancillary data.
- Verdict: **VALID**. MainSend ignored the selected device.
- Fix: 9fdf09f
- Draft reply: Fixed in 9fdf09f.

## P2 model/unix-datagram-socket-fd.cc:301, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584220
- Claim: `cmsg_len` is trusted without checking the bytes remaining in `msg_controllen`, so a malformed control header can make `CMSG_DATA` read past the caller’s buffer. Validate the current header’s offset and length before dereferencing it, and reject malformed ancillary data with `EINVAL`.
- Verdict: **VALID**. CMSG_FIRSTHDR does not check cmsg_len against msg_controllen.
- Fix: 9fdf09f
- Draft reply: Fixed in 9fdf09f: EINVAL, like Linux.

## P2 model/unix-datagram-socket-fd.cc:303, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600481
- Claim: This `IPV6_PKTINFO` implementation silently drops the requested IPv6 source address and supports only interface selection. Apply `ipi6_addr` to the outgoing socket or reject nonzero source addresses instead of accepting them as handled.
- Verdict: **PARTIALLY VALID**. ns-3 sockets cannot set a per-packet source. Rejecting a nonzero ipi6_addr would break quagga's ospf6d, which passes the interface's own link-local address, the one ns-3 picks anyway.
- Fix: none
- Draft reply: ns-3 sockets cannot take a per-packet source address, and rejecting a nonzero one would break ospf6d, which passes the interface's own link-local address (the one ns-3 selects anyway). Leaving it as is.

## P2 model/unix-socket-fd.cc:435, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584165
- Claim: This stores multicast membership globally on the node instead of per socket, so closing or leaving one socket can remove or leak membership for other sockets. Track memberships per `UnixSocketFd` and remove them when the fd closes, with reference counting for shared groups.
- Verdict: **VALID**. Memberships are node-wide (Ipv6L3Protocol::AddMulticastAddress) and are not dropped on close. A fix needs per-socket tracking with node-level reference counts.
- Fix: none
- Draft reply: Valid. In practice each group has one listening daemon per node, so this is left for a follow-up: it needs per-socket membership tracking with node-level reference counts.

## P2 model/unix-socket-fd.cc:530, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600499
- Claim: This branch reports `ICMP6_FILTER` success while discarding the filter, so callers receive ICMPv6 types they explicitly asked to block. Retain and apply the mask during receive, or return `ENOPROTOOPT` instead of silently accepting unsupported behavior.
- Verdict: **VALID**. The filter was accepted and ignored.
- Fix: 8c8d882
- Draft reply: Fixed in 8c8d882: DCE keeps the filter, drops blocked types on receive, and returns it from getsockopt. New test test-icmp6-filter (both stacks). Rejecting the option would have broken zebra's router advertisements.

## P2 model/unix-socket-fd.cc:424, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600507
- Claim: `ipv6mr_interface == 0` is not Linux’s “all interfaces” value; it asks the kernel to select the multicast-route interface. Resolve that interface before adding or removing membership instead of using the unscoped overload, or zero-index joins will subscribe every simulated interface.
- Verdict: **PARTIALLY VALID**. Linux picks one interface by a route lookup, while this joins on all of them, a superset. Applications join link-local groups with an explicit interface.
- Fix: none
- Draft reply: True: Linux picks the interface of the route to the group, while this joins on all interfaces, a superset. Applications join link-local groups on explicit interfaces, so this is left as is.

## P2 model/unix-stream-socket-fd.cc:596, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584240
- Claim: `handshakeStarted` only indicates that `UnixSocketFd::Connect()` returned 0; it does not identify why the asynchronous handshake failed. `ConnectionError()` also represents non-refusal failures, so this branch reports timeouts or unreachable hosts as `ECONNREFUSED`; carry the socket's actual failure reason through instead.
- Verdict: **PARTIALLY VALID**. ns-3's TcpSocket does not report why a connection failed (ConnectionFailed carries no reason). Before this PR, errno was stale in that case.
- Fix: none
- Draft reply: ns-3's TcpSocket does not tell why the connection failed, so DCE cannot distinguish a timeout from a refusal. ECONNREFUSED replaces the stale errno from before.

## P2 netlink/netlink-socket.cc:938, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584122
- Claim: `ifinfomsg.SetInterfaceIndex` now reports `interface_num + 1`, but this message still emits `IFL_A_LINK` as `interface_num`. `ip link` interprets `IFL_A_LINK` as the parent ifindex, so later devices appear attached to the previous device. Emit the matching Linux index or omit this attribute for unstacked devices.
- Verdict: **VALID**. IFLA_LINK pointed at the previous device, so iproute2 shows @<previous> or @NONE.
- Fix: c535c3b
- Draft reply: Fixed in c535c3b: IFLA_LINK is the device itself, as for unstacked devices. The netlink test checks it.

## P2 test/dce-manager-test.cc:190, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600513
- Claim: `test-select` and `test-tcp-socket` were previously fully disabled (mask 0) with comments scoping them to the Linux stack (`0 /*LINUX_STACK*/`). They now run on the ns-3 stack as well, which this PR does not change. If they were re-enabled only because LKL supports them, use `LINUX_MASK` here (as `test-poll`'s `/*NS3_STACK|LINUX_STACK*/` intent is the only one that historically included both); otherwise confirm the ns-3 runs pass.
- Verdict: **INVALID**. Both run and pass on the ns-3 stack: the dce-process-manager suite passes, with test-select (ns3) and test-tcp-socket (ns3).
- Fix: none
- Draft reply: They pass on the ns-3 stack too, so both stacks are enabled on purpose.

## P2 utils/build_kernel_deps.sh:143, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584196
- Claim: The quagga module patch step is not rerunnable: after one successful build, the next `git apply --check` fails for the already-applied patch and `set -e` aborts the script. Accept the reverse-applied state or fail only when neither orientation applies.
- Verdict: **VALID**. `git apply --check ... && git apply` failed the subshell, and set -e aborted, when the patch was already applied.
- Fix: c941c90
- Draft reply: Fixed in c941c90: an already applied patch is skipped. Verified with two runs into the same directory.

## P2 utils/build_kernel_deps.sh:120, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584200
- Claim: `|| true` also accepts a failed DCE-specific ping patch, leaving the zero-timeout receive loop that can spin a DCE task instead of advancing simulation time. Distinguish an already-applied patch from a mismatch and stop on mismatch.
- Verdict: **VALID**. Same `|| true` as for iperf.
- Fix: c941c90
- Draft reply: Fixed in c941c90.

## P2 utils/build_kernel_deps.sh:52, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584204
- Claim: `fetch_git` reuses any existing source directory without enforcing the requested revision. Reusing a NUSE-era `deps_dir` silently rebuilds old iproute2, so the documented `ip mptcp` commands fail; refresh or reclone when the revision differs.
- Verdict: **VALID**. A source tree from an earlier run was reused at any revision.
- Fix: c941c90
- Draft reply: Fixed in c941c90: a tree at another revision is an error asking to delete it.

## P2 utils/build_lkl.sh:31, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584232
- Claim: This guard accepts any existing LKL directory without verifying `HEAD` against `LKL_REV`, so rerunning the script on an older or newer checkout can produce an incompatible `liblkl.so` while reporting a successful build. Validate the repository revision (or fail with a clear error) before applying the patches.
- Verdict: **VALID**. Any existing LKL tree was accepted.
- Fix: c6b41ce
- Draft reply: Fixed in c6b41ce: the tree must be at LKL_REV. The patches are applied as working-tree changes, so HEAD stays there.

## P2 utils/build_lkl.sh:66, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600477
- Claim: The stripped library is copied per node without its `liblkl.so.debug`, so GDB cannot resolve the debug link for the loaded `elf-cache/lkl/.../liblkl.so`. Copy the debug file alongside each cached library or configure the debugger's separate-debug-file search path.
- Verdict: **VALID**. gdb looks for the debug link next to elf-cache/lkl/<n>/liblkl.so.
- Fix: 8a85fcc
- Draft reply: Fixed in 8a85fcc: liblkl.so.debug is linked next to each node's copy when it is next to the library (gdb then shows source lines).

## P2 utils/lkl-patches/0002-lkl-stop-the-tick-while-the-host-is-idle.patch:127, INVALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584222
- Claim: This suppresses the scheduler tick without telling RCU that the CPU is idle. Pair this interval with `rcu_idle_enter()`/`rcu_idle_exit()` (or the complete idle transition), otherwise RCU grace periods and callbacks can stall while the host runs.
- Verdict: **INVALID**. LKL is uniprocessor with TINY_RCU (no context tracking), where rcu_idle_enter/ct_idle_enter does nothing. Tiny RCU reports quiescent states on context switches.
- Fix: none
- Draft reply: LKL is built uniprocessor with TINY_RCU, which has no RCU idle tracking (ct_idle_enter is a no-op without context tracking), and Tiny RCU reports quiescent states on context switches.

## P2 wscript:158, PARTIALLY VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584224
- Claim: Python-enabled `--with-lkl` builds remove the Linux stack API from the generated bindings because binding generation never recognizes `LINUX_STACK`. Update binding generation and its API definitions for LKL so Python users retain access to the usable Linux factory.
- Verdict: **PARTIALLY VALID**. post_register_types removes LinuxSocketFdFactory when KERNEL_STACK is not set. But the API definitions (bindings/modulegen__*.py) still describe libos classes such as KernelSocketFdFactory, which no longer exist, so keeping them needs regenerated bindings. Python bindings are off without pybindgen.
- Fix: none
- Draft reply: True, but the API definitions still describe the libos classes (KernelSocketFdFactory and others) that this PR removes, so keeping LinuxSocketFdFactory needs regenerated bindings. Left for when the bindings are regenerated.

## P3 doc/source/dce-user-kernel.rst:58, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600535
- Claim: This command drops the `liblkl.so.debug` artifact described above, so the library loaded from DCE's cache cannot resolve its separate debug information. Preserve the debug file alongside the runtime copy or document how to configure GDB to find it.
- Verdict: **VALID**. The cp dropped liblkl.so.debug.
- Fix: 748c45d (with 8a85fcc)
- Draft reply: Fixed in 748c45d: both files are copied, and 8a85fcc makes gdb find the debug file from elf-cache.

## P3 example/dce-iperf-emulation.cc:136, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600527
- Claim: The new `LINUX_STACK` guard makes this the LKL configuration path, but the fallback still directs users to build the removed dce-linux module. Update the diagnostic to mention LKL and `--with-lkl`.
- Verdict: **VALID**. Outdated diagnostic.
- Fix: 71ad79f
- Draft reply: Fixed in 71ad79f.

## P3 model/lkl/lkl-kernel.h:6, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235600544
- Claim: This header does not use `EventId`, so including `ns3/event-id.h` needlessly increases the public include dependency. Remove the unused include.
- Verdict: **VALID**. Unused include.
- Fix: 71ad79f
- Draft reply: Fixed in 71ad79f.

## P3 model/lkl/lkl-socket-fd-factory.h:25, VALID

- Thread: https://github.com/direct-code-execution/ns-3-dce/pull/150#discussion_r4235584258
- Claim: `AddDevice` does not mirror every `NetDevice`: it explicitly skips `LoopbackNetDevice` because LKL supplies its own loopback. Describe the mapping as non-loopback devices so the public contract matches runtime behavior.
- Verdict: **VALID**. Loopback is skipped.
- Fix: 71ad79f
- Draft reply: Fixed in 71ad79f.

## Beyond the review comments

These problems turned up while verifying the comments and checking that every traffic example moves data. They are fixed on the same branch:

- ba5fa65: the dce-process-manager harness reported a program that never ended as passing. It is now strict; 4 existing programs that never end are listed explicitly (test-timer-fd, test-local-socket, test-bug-multi-select, and test-socket on Linux). They were hidden before and still need investigating.
- c5d7d0d: dce-mptcp-handoff-v6 never opened its sim1 subflow.
- 73c36d5: dce-mptcp-handoff-v4v6 carried no data at all, already at bd598bd ("connect failed: No route to host", because the mobile node was out of Wi-Fi range).
- 6b4de5d: in dce-sctp-simple the client received nothing (it passed uninitialized recvmsg flags). It now checks that the client receives messages.
- bbd9cd5: dce-cradle-simple --bulk=1 with IPv6 protocols sent to its own address, so 0 bytes arrived. It now fails on 0 bytes.
- b727b87: dce-tcp-ns3-nsc-comparison now fails when a flow stalls.
- dce-cradle-mptcp/dce-iperf-mptcp with --nRtrs>=5: node 0's LKL panics with "System is deadlocked on memory" (mem=64M) during a 200 s bulk transfer. These values are now rejected anyway (nRtrs <= 3), but it suggests that 64M of kernel memory is tight for heavy MPTCP runs. Not investigated.
- The dce-ping-mt1 run outside test.py aborted because ssmpingd is not in bin_dce. That is a missing dependency, not a code issue.

irq_enter() brings a stale jiffies up to date only when the idle task is current, but LKL idles in idle_host_task or in a host task that stays current after a system call. With the tick stopped by 0002, interrupts ran with an old jiffies, and the timers armed from them, such as the TCP retransmission timer armed on an incoming ACK, expired early: a transfer losing 10 segments retransmitted 84. Update jiffies on interrupt entry when another task is current, as the idle task would.
@Gabrielcarvfer

Copy link
Copy Markdown
Author

Additional fix found while running TCP calibration

@thehajime

Copy link
Copy Markdown
Member

Updated with some fixes (see verdict below). Some open design issues for maintainers below.

could you use the reply field of each comment made by reviewer (cubic)?
some of them are automatically resolved (judged by cubic followup review) but some of them are left without any comments there.

since the number of comments are large, it's not easy to follow which comments you addressed your summary.

I'm not going to blindly push the merge button without interpreting the contents of each of them.

@Gabrielcarvfer

Copy link
Copy Markdown
Author

Done

@thehajime thehajime left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave a shot to run-through whole patch.
I don't think I covered all this (great) contribution in this run, but hope this helps to improve.

anyway, thanks for the patch !

Comment thread .github/workflows/build-and-test.yml Resolved
Comment thread doc/source/dce-subprojects.rst Resolved
Comment thread example/dce-mptcp-lte-wifi.cc Resolved
Comment thread example/examples-to-run.py Resolved
Comment thread helper/linux-stack-helper.cc Resolved
Comment thread test/dce-manager-test.cc Resolved

diff --git a/crypto/jitterentropy.c b/crypto/jitterentropy.c
index 3b390bd6c..9025437f9 100644
--- a/crypto/jitterentropy.c

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm really not sure but UML has time-travel mode, which is similar to the situation of DCE and wondering how they (TT mode) solve this kind of things.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have zero idea of what you mean by that. 😅

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this patch with UML in time-travel=inf-cpu mode (which I mean similar situation using virtual clocksource with DCE), using Linux v6.12 and v7.3-rc3 with CONFIG_CRYPTO_JITTERENTROPY=y and FIPS disabled. Without the patch, both versions hang in jent_gen_entropy(). With the patch, initialization proceeds. On v7.3-rc3, jent_mod_init() returns 0 and the jitterentropy RNG is registered, even though the measurements remain stuck.

This suggests that the current patch stops the hang but does not reliably reject an unsuitable entropy source: jent_gen_entropy() breaks after 1024 stuck measurements without reporting an error to its caller.

I would prefer to minimize the LKL patch set. Could you first check whether DCE needs the jitterentropy RNG? If it does not, disabling it in the LKL build configuration may let us remove this patch. If it is needed, I think the fix should propagate the collection failure so that initialization does not register the RNG and later reads cannot report success without collecting entropy. Since the same hang reproduces on UML, that would also be a candidate for an upstream kernel fix.

@@ -0,0 +1,500 @@
diff -ruN a/example/dce-quagga-bgpd-caida.cc b/example/dce-quagga-bgpd-caida.cc

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you propose a PR to the reposistory ? (sorry IIRC there is a repo for quagga dce).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After you are happy with the patches here, yes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unlike the patches to LKL, this (ns-3-dce-quagga) is very close repository to this (ns-3-dce) so, instead of including the patch to ns-3-dce, I'd prefer to fix the repository.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just meant that when you are satisfied with all the changes here, then I will open MRs with the patches to external code. Otherwise it is additional MRs to manage, and I already have 40 something open, plus 3 journal papers in progress, etc. =x

@Gabrielcarvfer

Copy link
Copy Markdown
Author

hmm, this jiffies patch that entered last broke the applications. will retest it later with all applications again

@Gabrielcarvfer

Copy link
Copy Markdown
Author

It is actually ok. :)

#include "linux-stack-helper.h"
#include "ipv4-linux.h"
#include "ipv6-linux.h"
#include "linux-socket-fd-factory.h"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This didn't build when I tried it. I added "#ifdef LINUX_STACK" around the include of linux-socket-fd-factory.h to get everything working for me.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this branch? Or without it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was on pull request 151 branch and also before recent changes. I applied our patches on current state for this branch and it works without "#ifdef LINUX_STACK" around linux-socket-fd-factory.h.

So this was a false remark, from my side. All our tests pass with this pull request as base (we have commits on top of this branch that we can look at when this is merged (or after pull request 151)).

@Gabrielcarvfer

Copy link
Copy Markdown
Author

Consolidated a bit the changes to remove NUSE more thoroughly, and reuse existing filenames for LKL instead of creating all new files.

@thehajime thehajime left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gabrielcarvfer pls also confirm unresolved items in addition to this comment.

Comment thread model/lkl/lkl-kernel.cc Resolved

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants