diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 67fa9f5a..6263e8df 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -2,13 +2,18 @@ name: Build and Test DCE on: push: - branches: [ sikabo_master ] + branches: [ master ] + # Run for every pull request (merge-request pipeline), whatever the target branch pull_request: - branches: [ sikabo_master ] workflow_dispatch: +# Cancel superseded runs of the same branch/PR +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + env: - NS3_VER: "3.46" + NS3_VER: "3.49" jobs: build: @@ -18,8 +23,6 @@ jobs: fail-fast: false matrix: include: - - distro: ubuntu-22.04 - image: ubuntu:22.04 - distro: ubuntu-24.04 image: ubuntu:24.04 - distro: debian-trixie @@ -44,7 +47,44 @@ jobs: libsqlite3-dev \ libxml2-dev \ wget \ - make + make \ + bc \ + flex \ + bison \ + byacc \ + gawk \ + file \ + xz-utils \ + libssl-dev \ + libdb-dev \ + libsctp-dev \ + libx11-dev \ + libxext-dev \ + libxv-dev \ + libpulse-dev \ + libsdl2-dev \ + libfltk1.3-dev \ + libpng-dev \ + libjpeg-dev \ + autoconf \ + automake \ + libtool \ + meson \ + libgtk-4-dev \ + libuchardet-dev \ + libpsl-dev \ + libseccomp-dev \ + libharfbuzz-dev \ + libfribidi-dev \ + libcairo2-dev \ + libfontconfig-dev \ + libfreetype-dev \ + xvfb \ + dbus \ + libdav1d-dev \ + libvpx-dev \ + libopenh264-dev \ + libopus-dev - name: Configure git safe directory run: git config --global --add safe.directory "$GITHUB_WORKSPACE/ns-3-dce" @@ -88,24 +128,82 @@ jobs: working-directory: ns-${{ env.NS3_VER }} run: ./ns3 install + # DCE builds of ip, iperf, thttpd, wget, ping and quagga, and the + # ns-3-dce-quagga module. + - name: Cache kernel stack dependencies + id: cache-kernel-deps + uses: actions/cache@v4 + with: + path: | + deps/bin_dce + deps/ns-3-dce-quagga + key: kernel-deps-${{ matrix.distro }}-${{ hashFiles('ns-3-dce/utils/build_kernel_deps.sh', 'ns-3-dce/utils/*.patch') }} + + - name: Build kernel stack dependencies + if: steps.cache-kernel-deps.outputs.cache-hit != 'true' + working-directory: ns-3-dce + run: | + ./utils/build_kernel_deps.sh "$GITHUB_WORKSPACE/deps" + rm -rf "$GITHUB_WORKSPACE/deps/src" + + # Linux 6.12 kernel library (LKL) and its headers. + - name: Cache LKL + id: cache-lkl + uses: actions/cache@v4 + with: + path: deps/lkl + key: lkl-${{ matrix.distro }}-${{ hashFiles('ns-3-dce/utils/build_lkl.sh', 'ns-3-dce/utils/lkl-patches/*.patch', 'ns-3-dce/utils/lkl-dce.config') }} + + - name: Build LKL + if: steps.cache-lkl.outputs.cache-hit != 'true' + working-directory: ns-3-dce + run: | + ./utils/build_lkl.sh "$GITHUB_WORKSPACE/lkl-src" "$GITHUB_WORKSPACE/deps/lkl" + rm -rf "$GITHUB_WORKSPACE/lkl-src" "$GITHUB_WORKSPACE/deps/lkl/liblkl.so.debug" + + - name: Add Quagga module + run: cp -a deps/ns-3-dce-quagga ns-3-dce/myscripts/ + - name: Configure DCE working-directory: ns-3-dce run: | ./waf configure \ --with-ns3="$GITHUB_WORKSPACE/build" \ --prefix="$GITHUB_WORKSPACE/build" \ + --with-lkl="$GITHUB_WORKSPACE/deps/lkl" \ --enable-opt - name: Build DCE working-directory: ns-3-dce run: ./waf build - - name: Build bash for DCE + - name: Install DCE binaries working-directory: ns-3-dce - run: ./utils/build_bash.sh build/bin_dce + run: | + cp -a "$GITHUB_WORKSPACE/deps/bin_dce/." build/bin_dce/ + cp "$GITHUB_WORKSPACE/deps/lkl/liblkl.so" build/bin_dce/ + ./utils/build_bash.sh build/bin_dce - name: Run DCE tests working-directory: ns-3-dce env: LD_LIBRARY_PATH: ${{ github.workspace }}/build/lib:${{ github.workspace }}/build/lib64 - run: python3 test.py + # xvfb-run provides the X server used by dce-x11-hello and the + # browsers; dbus-run-session the session bus GApplication (Northstar) + # registers on. + run: xvfb-run -a dbus-run-session -- python3 test.py -r + + # Sockets cannot be uploaded; symlinks would be followed (dce-browser + # links its browser's /usr and /etc to the host's). + - name: Remove sockets and symlinks from test output + if: always() + working-directory: ns-3-dce + run: if [ -d testpy-output ]; then find testpy-output \( -type s -o -type l \) -delete; fi + + - name: Upload test output + if: always() + uses: actions/upload-artifact@v4 + with: + name: testpy-output-${{ matrix.distro }} + path: ns-3-dce/testpy-output + if-no-files-found: ignore diff --git a/.github/workflows/dce-gaction-test.yml b/.github/workflows/dce-gaction-test.yml deleted file mode 100644 index 14983f28..00000000 --- a/.github/workflows/dce-gaction-test.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: DCE Build -on: [push, pull_request] - -# Anchors are not supported, so could not make use of modular workflow - -jobs : - build: - strategy: - matrix: - job: - # Used a personal Ubuntu-16.04 docker image since the previous once defaulted to root user, - # so had to prefix all commands with sudo, and Github actions like checkout and caching throws permission error - # [ 'job-name' , 'docker-image-name' ] - # [ 'ubuntu-16.04', 'parthpratim27/ns-3-dce:ubuntu-16.04' ] - - [ 'ubuntu-20.04', 'parthpratim27/ns-3-dce:ubuntu-20.04' ] - - name : ${{ matrix.job[0] }} - runs-on : ubuntu-20.04 # This is ignored, but is a required field so is being set to this - container: ${{ matrix.job[1] }} - - env: - BAKE_GIT: 'https://gitlab.com/nsnam/bake.git' - BAKE_CONFIG: 'dce-linux-dev' # list-type, for each entry, -e {bake_config_entry} ... , will be added - GIT_HEAD_TRACK : '.git/HEAD' - HG_CHANGE_LOG : '.hg/store/00changelog.i' - - steps: - - name: Pull Source Tree - uses: actions/checkout@v2 - with: - path : target-dce-repo - - - name: Download Bake - run: git clone $BAKE_GIT bake - - - name: Configure Bake - run: ./bake/bake.py configure -e $BAKE_CONFIG - - - name: Download all required repos - run: ./bake/bake.py download -vvv - - - name: Copy Current ${{ github.event_name }} to source/ns-3-dce - run: cp -a target-dce-repo/. source/ns-3-dce - - # Below Caching saves 16 mins of build time (if an ns-3-dev build with same HEAD already exists) - # Couldn't add the same for net-next-nuse-4.4.0, because it would anyways still build the entire project after defconfig - - name: Load Build Cache for ns-3-dev - uses: actions/cache@v2 - with: - id : cache-ns-3-dev - path: source/ns-3-dev - key: ${{ matrix.job[0] }}-src-build-ns-3-dev-${{ hashFiles('source/ns-3-dev/.git/HEAD') }} - - - name: Build Bake - run: ./bake/bake.py build -vvv - - - name: Run Tests - run: | - cd source/ns-3-dce && ./test.py -r - find "testpy-output/" -type s -exec rm -f {} \; - - - name: Save Test Output Artifacts - uses: actions/upload-artifact@v1 - with: - name: ${{ github.job }}-test-py-output - path: source/ns-3-dce/testpy-output - - - diff --git a/.gitignore b/.gitignore index ea3361b1..3ce5a85b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ GRTAGS GTAGS myscripts/ns-3-dce-quagga/ myscripts/ns-3-dce-umip/ +testpy-output/ \ No newline at end of file diff --git a/RELEASE_NOTES b/RELEASE_NOTES index dcfc1986..602362d1 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -5,6 +5,78 @@ This file contains Direct Code Execution release notes (most recent releases fir Please refer the page https://www.nsnam.org/projects/direct-code-execution/ for detailed instructions +Changes since dce-1.12 (development, ns-3.49) +============================================== + +New examples +------------ +- dce-wifi-video: real ffmpeg streaming video over simulated 802.11n + (station - access point - station) on the Linux kernel stack or the ns-3 + stack, watched live with a host player or in a window drawn from inside + the simulation; as a test, the received streams must be identical to the + sent ones. utils/build_kernel_deps.sh builds the DCE version of ffmpeg. +- x11-hello, gtk4-hello and dce-x11-hello: X11 and GTK4 clients built for + DCE draw their windows on the host display. +- dce-browser: a web browser on a Wi-Fi station browses a site served by + thttpd on another station, or the Internet through a host tap device + (utils/dce-tap-setup.sh): Dillo and Northstar (GTK4, JavaScript, sound) + built for DCE by utils/build_kernel_deps.sh, or unmodified prebuilt + Chromium and Brave, which play YouTube with sound. The Wi-Fi capture and + the browser's TLS key log let Wireshark decrypt the traffic. See + doc/source/dce-x11.rst. +- build-and-run.sh builds everything in a container and runs the demos. + +New DCE features +---------------- +- Host socket passthrough: the DceHostUnixSocketPaths global value lists + AF_UNIX path prefixes for which connect() reaches a real host socket. + DceX11Helper uses it for the host's X server, D-Bus session bus and + PulseAudio server. +- eventfd(2), epoll(7), pipe2(2), futex(2) (through syscall(2)), SOCK_SEQPACKET, + connected socketpair(), descriptor passing (SCM_RIGHTS), FIONREAD/FIONBIO, + memfd_create, shm_open, gettid, /proc/self/exe, and several hundred more + libc entry points for ffmpeg, the X11 libraries, GLib/GIO/GTK4, Dillo, + Northstar and Chromium. +- Programs built as executables (PIE), such as a prebuilt Chromium, run + unmodified: main() is found from the entry point, their thread local + variables get their own room (DCE_RESERVE_EXECUTABLE_TLS), their own + allocator is used by their libraries, and their references to old glibc + symbol versions bind to DCE. +- TaskManager::PerFiberTls gives every task thread local storage of its + own (UcontextFiberManager, x86_64). +- A program which crashes (SIGSEGV, SIGBUS, SIGILL, SIGFPE) ends its + process, as the kernel would, instead of the simulation. +- DcePcapCheck reads ns-3 captures back (802.11 with A-MPDU, Ethernet, raw + IP) for examples that check their traffic. +- Performance: libc functions are forwarded with a tail jump, the heap no + longer walks all its chunks, mutexes and condition variables are found + by id, and a zero-timeout poll() or epoll_wait() lets the simulated clock + catch up with the wall clock instead of spinning. +- malloc() and friends return 16 byte aligned memory, like glibc. + +Bugs fixed +---------- +- LKL: the kernel's re-armed timer events piled up in the simulator's + queue (gigabytes per minute with a busy kernel), and frames larger than + 1500 bytes were truncated (every full-size Wi-Fi TCP segment). +- pthread_cond_wait() could miss a wake-up, and a timed-out waiter could + take the wake-up of another thread: programs froze. +- socketpair() did not connect its two ends; shutdown() did not wake a + blocked reader; a write to a closed peer returned 0 instead of EPIPE; a + blocking read waited for the whole buffer; sendmsg()/recvmsg() mishandled + their control data and errors. +- fork() returned 0 in the parent with UcontextFiberManager; it now fails + with ENOSYS. PthreadFiberManager's switches aborted under _FORTIFY_SOURCE. +- openat() relative to a directory ran a shell; F_DUPFD_CLOEXEC returned a + host descriptor; dlerror() was the host's; SIG_DFL and SIG_IGN were + called as handlers; exit() ran thread specific data destructors. +- open64() passed garbage as the file creation mode; socket() with + SOCK_NONBLOCK/SOCK_CLOEXEC and setsockopt(SO_SNDBUF) on ns-3 UDP failed; + closing a pipe end whose peer was closed failed; realpath() leaked host + paths; the fortified __sprintf_chk() took the buffer size as the format + string, and wrote one character into a buffer of unknown size; a task + could be woken up after its process had exited. + Release dce-1.12 ================ In order to use DCE, please use the bake tool to install DCE. DCE 1.12 diff --git a/bindings/python/ns3modulegen_core_customizations.py b/bindings/python/ns3modulegen_core_customizations.py index 04c59ad9..9a122df3 100644 --- a/bindings/python/ns3modulegen_core_customizations.py +++ b/bindings/python/ns3modulegen_core_customizations.py @@ -449,18 +449,16 @@ def add_ipv4_address_tp_hash(module): def post_register_types(root_module): remove_classes = [ - # These are classes which get introduced when KERNEL_STACK is enabled , so should be removed for non-linux DCE builds + # The Linux stack (LKL) is not part of the Python bindings 'KernelSocketFdFactory', 'LinuxSocketImpl', 'KernelSocketFd', 'LinuxSocketFdFactory', - 'FreeBSDSocketFdFactory' ] - if "KERNEL_STACK" not in os.environ: - for classname in remove_classes: - try: - root_module.classes.remove(root_module['ns3::%s' % classname]) - except: - pass + for classname in remove_classes: + try: + root_module.classes.remove(root_module['ns3::%s' % classname]) + except: + pass diff --git a/bindings/python/wscript b/bindings/python/wscript index cda0936b..e74c09f7 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -584,9 +584,6 @@ def build(bld): if not env['ENABLE_PYTHON_BINDINGS']: return - if bld.env['KERNEL_STACK']: - os.environ['KERNEL_STACK'] = bld.env['KERNEL_STACK'] - dce_generate_python_bindings(bld) diff --git a/build-and-run.sh b/build-and-run.sh new file mode 100755 index 00000000..83f65891 --- /dev/null +++ b/build-and-run.sh @@ -0,0 +1,389 @@ +#!/bin/bash +set -e + +# DCE Build and Run Script +# Inspired by .github/workflows/build-and-test.yml + +NS3_VER="3.49" +DCE_DIR=$(pwd) +BUILD_DIR="$DCE_DIR/build" +DEPS_DIR="$DCE_DIR/deps" +LKL_SRC_DIR="$DCE_DIR/lkl-src" +NS3_DIR="$DCE_DIR/../ns-$NS3_VER" +CONTAINER_NAME="ns3-dce-dev" + +# Function to show usage +usage() { + echo "Usage: $0 [command]" + echo "Commands:" + echo " setup-container - Create a Distrobox container with all dependencies" + echo " clean - Remove all build artifacts" + echo " build - Build everything (NS-3, deps, LKL, DCE)" + echo " run [args] - Run a DCE application" + echo " demo [args] - Run a demonstration (vlc, northstar or chrome)" + echo " vlc: --viewer for internal X11 window" + echo " test - Run DCE tests" + echo " env - Print environment variables to source" + echo "" + echo "Options:" + echo " --host - (internal) Skip Distrobox re-entry (used automatically)" + exit 1 +} + +if [ $# -lt 1 ]; then + usage +fi + +USE_DISTROBOX=true +INSIDE_CONTAINER=false + +# Detect if we are already inside a container (distrobox or docker) +if [ -f /run/.containerenv ] || [ -f /.dockerenv ]; then + INSIDE_CONTAINER=true + USE_DISTROBOX=false +fi + +# --host is an internal flag added when re-entering via distrobox; treat as inside-container +if [[ "$@" == *"--host"* ]]; then + INSIDE_CONTAINER=true + USE_DISTROBOX=false + # Remove --host from arguments + NEW_ARGS=() + for arg in "$@"; do + if [[ "$arg" != "--host" ]]; then + NEW_ARGS+=("$arg") + fi + done + set -- "${NEW_ARGS[@]}" +fi + +# If distrobox is not available, we can't use it +if ! command -v distrobox >/dev/null 2>&1; then + USE_DISTROBOX=false +fi + +COMMAND=$1 +shift + +# If we should use distrobox, re-run ourselves inside it +if [ "$USE_DISTROBOX" = true ] && [ "$COMMAND" != "setup-container" ]; then + if ! distrobox list | grep -q "$CONTAINER_NAME"; then + echo "Container $CONTAINER_NAME not found. Please run '$0 setup-container' first." + exit 1 + fi + + # Check if we need to setup tap-dce on the host before entering + if [ "$COMMAND" == "demo" ]; then + if ! ip link show tap-dce > /dev/null 2>&1; then + echo "--- Setting up tap-dce on host (requires sudo) ---" + echo "Please enter your password to configure the network tap device:" + sudo -v || { echo "ERROR: sudo authentication failed. Cannot set up tap device."; exit 1; } + sudo ./utils/dce-tap-setup.sh up "$(whoami)" tap-dce + fi + fi + + echo "--- Running $COMMAND inside distrobox $CONTAINER_NAME ---" + + # Pass through X11 and PulseAudio environment variables + distrobox enter "$CONTAINER_NAME" -- \ + env DISPLAY="$DISPLAY" \ + XAUTHORITY="$XAUTHORITY" \ + PULSE_SERVER="$PULSE_SERVER" \ + PULSE_COOKIE="$PULSE_COOKIE" \ + XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR" \ + DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" \ + HOME="$HOME" \ + "$DCE_DIR/build-and-run.sh" "$COMMAND" "$@" --host + exit $? +fi + +setup_env() { + export LD_LIBRARY_PATH="$BUILD_DIR/lib:$BUILD_DIR/lib64:$LD_LIBRARY_PATH" + export DCE_PATH="$BUILD_DIR/bin_dce:$BUILD_DIR/sbin" + # Also include deps in DCE_PATH if they are there + if [ -d "$DEPS_DIR/bin_dce" ]; then + export DCE_PATH="$DCE_PATH:$DEPS_DIR/bin_dce" + fi +} + +do_setup_container() { + echo "=== Creating Distrobox Container: $CONTAINER_NAME ===" + if distrobox list | grep -q "$CONTAINER_NAME"; then + echo "Container $CONTAINER_NAME already exists." + else + distrobox create -n "$CONTAINER_NAME" -i ubuntu:24.04 --yes + fi + + echo "=== Installing dependencies inside container ===" + distrobox enter "$CONTAINER_NAME" -- sudo apt-get update + distrobox enter "$CONTAINER_NAME" -- sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ + git gcc g++ python3 python3-dev cmake ninja-build pkg-config libgsl-dev \ + libsqlite3-dev libxml2-dev wget make bc flex bison byacc gawk file xz-utils \ + libssl-dev libdb-dev libsctp-dev libx11-dev libxext-dev libxv-dev libpulse-dev \ + libsdl2-dev libfltk1.3-dev libpng-dev libjpeg-dev autoconf automake libtool \ + meson libgtk-4-dev libuchardet-dev libpsl-dev libseccomp-dev libharfbuzz-dev \ + libfribidi-dev libcairo2-dev libfontconfig-dev libfreetype-dev xvfb dbus \ + libdav1d-dev libvpx-dev libopenh264-dev libopus-dev sudo vlc +} + +do_build() { + echo "=== Building NS-3 $NS3_VER ===" + if [ ! -d "$NS3_DIR" ]; then + git clone --branch "ns-$NS3_VER" --depth 1 \ + https://gitlab.com/nsnam/ns-3-dev.git "$NS3_DIR" + fi + + pushd "$NS3_DIR" + ./ns3 configure \ + --disable-werror \ + --prefix="$BUILD_DIR" \ + --build-profile=optimized + ./ns3 build + ./ns3 install + popd + + echo "=== Building Kernel Stack Dependencies ===" + mkdir -p "$DEPS_DIR" + CFLAGS="-O3 -g -fPIC" CXXFLAGS="-O3 -g -fPIC" ./utils/build_kernel_deps.sh "$DEPS_DIR" + # Clean up src to save space, like CI does + rm -rf "$DEPS_DIR/src" + + echo "=== Building LKL ===" + KCFLAGS="-O3" ./utils/build_lkl.sh "$LKL_SRC_DIR" "$DEPS_DIR/lkl" + rm -rf "$LKL_SRC_DIR" + + echo "=== Integrating Quagga Module ===" + if [ -d "$DEPS_DIR/ns-3-dce-quagga" ]; then + cp -a "$DEPS_DIR/ns-3-dce-quagga" "$DCE_DIR/myscripts/" + fi + + echo "=== Configuring and Building DCE ===" + CFLAGS="-O3 -g" CXXFLAGS="-O3 -g" ./waf configure \ + --with-ns3="$BUILD_DIR" \ + --prefix="$BUILD_DIR" \ + --with-lkl="$DEPS_DIR/lkl" \ + --enable-opt + ./waf build + + echo "=== Finalizing DCE Binaries ===" + mkdir -p "$BUILD_DIR/bin_dce" + if [ -d "$DEPS_DIR/bin_dce" ]; then + cp -a "$DEPS_DIR/bin_dce/." "$BUILD_DIR/bin_dce/" + fi + if [ -f "$DEPS_DIR/lkl/liblkl.so" ]; then + cp "$DEPS_DIR/lkl/liblkl.so" "$BUILD_DIR/bin_dce/" + fi + # Only build bash if we are in the distrobox or have headers + if [ -f /usr/include/termio.h ]; then + ./utils/build_bash.sh "$BUILD_DIR/bin_dce" + else + echo "Skipping build_bash.sh (termio.h missing on host). Run inside setup-container for full build." + fi + + echo "=== Verifying Key Binaries ===" + for bin in ip iperf wget ffmpeg northstar; do + if [ -f "$BUILD_DIR/bin_dce/$bin" ]; then + echo "[OK] $bin found" + else + echo "[WARNING] $bin NOT found in $BUILD_DIR/bin_dce" + fi + done + + echo "=== Build Complete ===" +} + +do_run() { + setup_env + APP=$1 + shift + # If the app is in bin_dce, we might want to run it via a helper or directly if it's an ns-3 script + ARGS="$*" + if [ -f "$BUILD_DIR/bin/$APP" ] || [ -f "$DCE_DIR/build/bin/$APP" ]; then + echo "Running ns-3 application $APP with args: $ARGS" + ./waf --run "$APP $ARGS" + elif [ -f "$BUILD_DIR/bin_dce/$APP" ]; then + echo "Running DCE binary $APP directly (not recommended for ns-3 scenarios)" + "$BUILD_DIR/bin_dce/$APP" "$@" + else + echo "Application $APP not found in build/bin or build/bin_dce. Trying waf --run anyway..." + ./waf --run "$APP $ARGS" + fi +} + +do_demo() { + TYPE=$1 + shift + setup_env + case $TYPE in + vlc) + echo "=== Running VLC over Wi-Fi Demo ===" + echo "This demo uses ffmpeg to stream video over a simulated Wi-Fi network." + echo "It can either play on a host player or via an X11 window inside DCE." + # Ensure the video file exists where dce-wifi-video expects it + if [ ! -f "$BUILD_DIR/bin_dce/video.ts" ]; then + echo "Warning: video.ts not found in bin_dce. Build might be incomplete." + fi + + FIRST_ARG="$1" + if [ "$FIRST_ARG" == "--viewer" ]; then + shift + echo "Starting with internal X11 viewer..." + ./waf --run "dce-wifi-video --viewer=1 $@" + elif [ "$FIRST_ARG" == "--help" ]; then + ./waf --run "dce-wifi-video --help" + else + echo "Starting with host VLC player..." + # Only launch VLC if not just asking for help + if [[ "$@" != *"--help"* ]]; then + ./waf --run "dce-wifi-video --player='vlc --play-and-exit - < {}' $@" + else + ./waf --run "dce-wifi-video $@" + fi + fi + ;; + northstar) + echo "=== Running Northstar Browser Demo ===" + echo "This demo runs the Northstar web browser inside DCE, drawing to your X server." + if [ ! -f "$BUILD_DIR/bin_dce/northstar" ]; then + echo "Error: northstar binary not found. Make sure it was built (requires GTK4 headers)." + exit 1 + fi + + # Default URL + URL="http://10.1.1.1/browser/" + + # Collect arguments, separating the URL if provided + REMAINING_ARGS=() + HAS_HELP=false + for arg in "$@"; do + if [[ "$arg" == "http"* ]]; then + URL="$arg" + elif [[ "$arg" == "--help" ]]; then + HAS_HELP=true + elif [ "$arg" != "northstar" ]; then + REMAINING_ARGS+=("$arg") + fi + done + + # Early exit for help + if [ "$HAS_HELP" = true ]; then + ./waf --run "dce-browser --help" + return + fi + + # Use tap if the interface exists (set up on host before entering container) + TAP_ARGS="" + if ip link show tap-dce > /dev/null 2>&1; then + TAP_ARGS="--tap=tap-dce" + fi + + if [ -n "$DISPLAY" ]; then + ./waf --run "dce-browser --browser=northstar --url=$URL $TAP_ARGS ${REMAINING_ARGS[*]}" + else + echo "DISPLAY not set, falling back to xvfb-run..." + xvfb-run -a dbus-run-session -- ./waf --run "dce-browser --browser=northstar --url=$URL $TAP_ARGS ${REMAINING_ARGS[*]}" + fi + ;; + chrome) + echo "=== Running Chromium Demo ===" + echo "This demo runs an unmodified Chromium build inside DCE, drawing to your X server." + # A Chromium continuous build: unlike Chrome for Testing, it keeps + # its symbols and the messages of its CHECKs. + CHROMIUM_REV="1725601" + CHROME_DIR="${CHROME_DIR:-$DEPS_DIR/chromium-$CHROMIUM_REV}" + if [ ! -x "$CHROME_DIR/chrome" ]; then + echo "Downloading Chromium r$CHROMIUM_REV..." + mkdir -p "$CHROME_DIR.tmp" + wget -q -O "$CHROME_DIR.tmp/chrome-linux.zip" \ + "https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/$CHROMIUM_REV/chrome-linux.zip" + (cd "$CHROME_DIR.tmp" && unzip -q chrome-linux.zip) + mv "$CHROME_DIR.tmp/chrome-linux" "$CHROME_DIR" + rm -rf "$CHROME_DIR.tmp" + fi + export DCE_PATH="$DCE_PATH:$CHROME_DIR" + + URL="http://10.1.1.1/browser/" + REMAINING_ARGS=() + for arg in "$@"; do + if [[ "$arg" == "http"* ]]; then + URL="$arg" + else + REMAINING_ARGS+=("$arg") + fi + done + TAP_ARGS="" + if ip link show tap-dce > /dev/null 2>&1; then + TAP_ARGS="--tap=tap-dce" + fi + if [ -n "$DISPLAY" ]; then + ./waf --run "dce-browser --browser=chrome --url=$URL $TAP_ARGS ${REMAINING_ARGS[*]}" + else + echo "DISPLAY not set, falling back to xvfb-run..." + xvfb-run -a dbus-run-session -- ./waf --run "dce-browser --browser=chrome --url=$URL $TAP_ARGS ${REMAINING_ARGS[*]}" + fi + ;; + *) + echo "Unknown demo type: $TYPE. Available: vlc, northstar, chrome" + exit 1 + ;; + esac +} + +do_test() { + setup_env + # We might need xvfb-run if graphical tests are involved + if command -v xvfb-run >/dev/null 2>&1; then + xvfb-run -a dbus-run-session -- python3 test.py -r + else + python3 test.py -r + fi +} + +do_clean() { + echo "=== Cleaning all build artifacts ===" + echo "Cleaning DCE build..." + ./waf distclean 2>/dev/null || true + echo "Cleaning NS-3 build..." + if [ -d "$NS3_DIR" ]; then + pushd "$NS3_DIR" + ./ns3 clean 2>/dev/null || true + popd + fi + echo "Removing build directory..." + rm -rf "$BUILD_DIR" + echo "Removing deps directory..." + rm -rf "$DEPS_DIR" + echo "Removing generated files..." + rm -rf elf-cache files-* *.pcap exitprocs testpy-output + rm -f .lock-waf_* build-*.log configure-*.log testpy-*.log + echo "=== Clean Complete ===" +} + +case $COMMAND in + setup-container) + do_setup_container + ;; + clean) + do_clean + ;; + build) + do_build + ;; + run) + do_run "$@" + ;; + demo) + do_demo "$@" + ;; + test) + do_test + ;; + env) + setup_env + echo "export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\"" + echo "export DCE_PATH=\"$DCE_PATH\"" + ;; + *) + usage + ;; +esac diff --git a/doc/source/dce-kernel.rst b/doc/source/dce-kernel.rst index 14fe220f..169e4956 100644 --- a/doc/source/dce-kernel.rst +++ b/doc/source/dce-kernel.rst @@ -2,6 +2,12 @@ Kernel Developer Information **************************** +.. note:: + + This page describes the former kernel support, net-next-sim and libos + (Linux up to 4.4), which DCE no longer uses: DCE now runs the Linux + Kernel Library (LKL); see :doc:`dce-user-kernel`. + This technical documentation is intended for developers who want to build a Linux kernel in order to use with DCE. A first part will describe the architecture and the second will show how we went from a net-next kernel 2.6 has a Linux kernel-stable 3.4.5. diff --git a/doc/source/dce-subprojects.rst b/doc/source/dce-subprojects.rst index 276d0ec5..5ef18317 100644 --- a/doc/source/dce-subprojects.rst +++ b/doc/source/dce-subprojects.rst @@ -44,19 +44,15 @@ The `umip `_ (Usagi-Patched Mobile IPv6 stack) support on DCE e For more information, see the latest support `document `_. -Linux kernel (from 2.6.36 to 3.14 version) -============================================ -Linux kernel support is built with a separate 'dce-linux' module, -available on `github -`_. Many +Linux kernel (6.12) +=================== +Linux kernel support uses the `Linux Kernel Library (LKL) +`_, built by ``utils/build_lkl.sh``. Many protocols implemented in kernel space such as TCP, IPv4/IPv6, Mobile -IPv6, Multipath-TCP, SCTP, DCCP, etc, are available with ns-3. - -FreeBSD kernel (10.0.0 version) -========================================= -`FreeBSD kernel support `_ is based on Linux kernel module of DCE. A few -protocols implemented in kernel space such as TCP, IPv4, etc, are -available with ns-3. +IPv6, Multipath TCP, SCTP, DCCP, etc, are available with ns-3. It +replaces the former net-next-sim/libos (Linux up to 4.4) kernel modules; +the FreeBSD kernel support, which was built on them, was removed with +them. thttpd diff --git a/doc/source/dce-user-basic.rst b/doc/source/dce-user-basic.rst index d8b282e0..aec44e9a 100644 --- a/doc/source/dce-user-basic.rst +++ b/doc/source/dce-user-basic.rst @@ -12,6 +12,7 @@ Basic Use Cases dce-user-submodule dce-user-config dce-cradle + dce-wifi-video + dce-x11 dce-user-aspect-trace - dce-user-freebsd diff --git a/doc/source/dce-user-freebsd.rst b/doc/source/dce-user-freebsd.rst deleted file mode 100644 index 5290c4a7..00000000 --- a/doc/source/dce-user-freebsd.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. include:: replace.txt - -FreeBSD kernel support with DCE -=============================== - -Overview --------- -This module provides an additional network stack support for DCE with FreeBSD kernel. - -.. u POSIX POSIX POSIX ns-3 -.. k ns-3 Linux FreeBSD Linux - -.. image:: images/dce-freebsd.png - :scale: 70% - :align: center - - -* This is a POSIX userspace application support over FreeBSD kernel over DCE, over ns-3 over Linux. -* This is **NOT** the DCE running on FreeBSD operating system (right now DCE only run on Linux). - -Usage ------ - -:: - - bake.py configure -e dce-freebsd-dev - bake.py download - bake.py build - -Once you finished to build dce-freebsd module, you can write a -simulation script that uses FreeBSD kernel as a network stack. All -you need is to specify the library name with an attribute **Library** -to **ns3::FreeBSDSocketFdFactory**, then install -**FreeBSDStackHelper** to the nodes. - -:: - - DceManagerHelper processManager; - processManager.SetNetworkStack ("ns3::FreeBSDSocketFdFactory", - "Library", StringValue ("libfreebsd.so")); - processManager.Install (nodes); - FreeBSDStackHelper stack; - stack.Install (nodes); - - -How to use your kernel extension with DCE ? -------------------------------------------- - -No configuration support (like *make menuconfig* in Linux) right -now. You need to add files into sys/sim/Makefile. - -The following represents an example to add Multipath-TCP feature of -FreeBSD by adding **mptcp_subr.o** to the -freebsd-sim/sys/sim/Makefile. If you want to add your code into the -kernel build, you may add object file names that is from your own -codes. - -:: - - diff --git a/sys/sim/Makefile b/sys/sim/Makefile - index 8115e3d..1b2feab 100644 - --- a/sys/sim/Makefile - +++ b/sys/sim/Makefile - @@ -100,7 +100,7 @@ ip_divert.o tcp_hostcache.o ip_ecn.o tcp_input.o ip_encap.o - \ - tcp_lro.o ip_fastfwd.o tcp_offload.o ip_gre.o tcp_output.o \ - ip_icmp.o tcp_reass.o ip_id.o tcp_sack.o ip_input.o tcp_subr.o \ - tcp_syncache.o ip_mroute.o tcp_timer.o ip_options.o tcp_timewait.o \ - -ip_output.o tcp_usrreq.o raw_ip.o udp_usrreq.o if_llatbl.o - +ip_output.o tcp_usrreq.o raw_ip.o udp_usrreq.o if_llatbl.o mptcp_subr.o - - -Limitations ------------ -While this release gives a proof of concept to use FreeBSD kernel with -DCE, there are tons of limitations (listed below) that have to be -improved in near future. - -* No sysctl -* No IPv6 address configuration -* No ifconfig - * No delete IP addresses support -* No route command -* No quagga, new FreeBSD-version glue code needed -* No extensive test -* No DCE Cradle -* No Poll implementation -* No getifaddr/routing socket implementations -* Socket options missing (incosistent defined value: SOL_SOCKET(Linux/FreeBSD)=20/0xffff) - -TODO ----- -* refactoring with LinuxStackHelper/FreeBSDStackHelper -* refactoring with wscript (--enable-kernel-stack uses both FreeBSD/Linux for now) - diff --git a/doc/source/dce-user-kernel.rst b/doc/source/dce-user-kernel.rst index 5ea9a0fc..844dff1f 100644 --- a/doc/source/dce-user-kernel.rst +++ b/doc/source/dce-user-kernel.rst @@ -1,237 +1,92 @@ .. include:: replace.txt -.. _net-next-sim: https://github.com/direct-code-execution/net-next-sim -.. _mptcp: https://github.com/multipath-tcp/mptcp -.. _mptcp_git: git://github.com/multipath-tcp/mptcp -.. _libos: https://github.com/libos-nuse/net-next-nuse .. _lkl: https://github.com/lkl/linux -.. _linux_vanilla_git: git@github.com:torvalds/linux.git Using your in-kernel protocol implementation ============================================ -There are a number of protocols implemented in kernel space, like many transport protocols (e.g., TCP, UDP, SCTP), Layer-3 forwarding plane (IPv4, v6 with related protocols ARP, Neighbor Discovery, etc). -DCE can simulate these protocols with |ns3| and a kernel compiled as a library. This is not possible with the linux vanilla kernel hence you need to use a slightly modified kernel (net-next-sim_ - deprecated - or libos_ which is the successor of net-next-sim_). +There are a number of protocols implemented in kernel space, like many +transport protocols (e.g., TCP, UDP, SCTP, DCCP, MPTCP) and the Layer-3 +forwarding plane (IPv4, IPv6 with related protocols ARP, Neighbor +Discovery, etc). DCE simulates these protocols with |ns3| and the Linux +kernel built as a library: the Linux Kernel Library, lkl_ (Linux 6.12). -This document describes how to retrieve, configure, compile and use this custom kernel in DCE. -As an example, it shows how to enable an in-tree but optional protocol - Stream Control Transmission Protocol (SCTP) - and an out of tree protocol: Multipath TCP (MPTCP). -Although other protocols may not adapt these patterns as-is, you will see what's needed to implement for your purpose. +Each node gets its own kernel; every ns-3 NetDevice of the node becomes a +kernel network device named ``sim``, and the sockets of DCE +applications are kernel sockets. + +This document describes how to build the kernel library, configure it, +and use it in DCE, with SCTP and Multipath TCP (MPTCP) as examples. .. contents:: :local: .. _configure: -1. Configure a kernel (make menuconfig in Linux) ------------------------------------------------------------ -In Linux kernel, there is a configuration system in Linux kernel to enable/disable features. This is typically done by *make menuconfig* command, and it writes a file (.config) at the kernel source directory. Build system (e.g., make bzImage) refers the file which source files are compiled. - -.. figure:: images/dce-make-menuconfig.png - :scale: 70 % - -In our DCE Linux kernel module (i.e. **net-next-sim** available at github.com), we have *arch/sim/defconfig* file to store the default configuration of kernel features. You may need to add proper configuration parameters (e.g., CONFIG_IP_SCTP) to build the protocol by default. - -For the Linux SCTP implementation, we need at least the following configuration parameters. - - - CONFIG_IP_SCTP=y - - CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE=y - - CONFIG_CRYPTO_CRC32C=y - - CONFIG_CRC32=y - -These can be added to the file (*arch/sim/defconfig*) or added manually to *.config* when needed. - -**Note** - -These configuration options **SHOULD** be minimized at the beginning since adding new option may require new functions which DCE doesn't support at the time and need to implement glue code. - +1. Configure the kernel +----------------------- +``utils/build_lkl.sh`` configures the kernel with LKL's defconfig plus +``utils/lkl-dce.config``, which enables what the DCE examples and tests +use: SCTP, DCCP, MPTCP, AF_PACKET, IPsec, Mobile IPv6, tunnels, multicast +routing and more TCP congestion controls. To enable another feature, add +its options to ``utils/lkl-dce.config``; ``build_lkl.sh`` reconfigures the +kernel when the file changes. -2. Build a DCE compatible linux kernel ---------------------------------------------- +For instance, the Linux SCTP implementation needs ``CONFIG_IP_SCTP=y``. -DCE can simulate these protocols with |ns3| and a kernel compiled as a library named *liblinux.so*. This is not possible with the linux vanilla kernel hence you need to use a slightly modified kernel (net-next-sim_ - deprecated - or libos_ which is the successor of net-next-sim_). -The following section presents the two methods, net-next-sim_ being slightly easier to install. - -2.1 With *net-next-sim* (deprecated) ----------------------------------------------------------------------- -To build the liblinux.so, DCE version of Linux kernel, - -:: - - make defconfig ARCH=sim - make library ARCH=sim - -You can use *make menuconfig* command (below) instead of editing the defconfig file. -If everything is fine, you will see **liblinux.so** linked to **libsim-linuxv.y.z.so** file at the root directory of Linux kernel. +2. Build the kernel library +--------------------------- :: - make menuconfig ARCH=sim - - -2.2 Build an MPTCP kernel with *net-next-sim* --------------------------------------------------------------------- - -DISCLAIMER: This is a transcript of `Hajime's guide https://plus.google.com/+HajimeTazaki/posts/1QUmR3n3vNA` updated on a best effort basis. Hence it may be possible to compile newer versions, in which case patches are welcome. -Build steps for DCE integration is also available as a script in *utils/mptcp-build.sh*. - -#) Get linux kernel mptcp_ variant: - - .. code:: shell - - $ git clone mptcp_git_ - $ git checkout -b sim3.14 mptcp_v0.89 - -.. 3.14 can work with mptcp 0.89.8. - -#) Merge in mptcp code the changes required to make it work in DCE. Those changes are listed in the net-next-sim_ project - - .. code:: shell - - % cd mptcp - % git remote add dce git://github.com/direct-code-execution/net-next-sim.git - % git fetch dce - % git merge dce/sim-ns3-3.14.0-branch - -#) Enable mptcp in the kernel configuration. There are 2 ways possible: - - - patch the kernel config manually (you can - - .. code:: shell - - % cat >> arch/sim/defconfig <`_ and `sctp-server.cc `_. +---------------------------------------------------- -**Optional** -You may optinally need external libraries to build/run the applications. In this case, the applications need *lksctp-tools*, so that applications fully benefit the features of SCTP, rather than only using standard POSIX socket API. +Then, we need userspace applications using the new feature of the kernel +protocol. In case of SCTP, see ``example/sctp-client.cc`` and +``example/sctp-server.cc``. These need *lksctp-tools* (``libsctp-dev``) +to build. -Moreover, adding system dependency to bake configuration file (i.e., bakeconf.xml) would be nice to assist build procedure. -The following is an example of *lksctp-tools*, which above applications use. - -:: +4. Write ns-3 scenario to use above applications +------------------------------------------------ - - - - - - - - - - - - -4. Write ns-3 scenario to use above applications. ---------------------------------------------------- -The next step would be writing |ns3| simulation scenario to use the applications you prepared. -You need first to add to your *DCE_PATH* the path to the previously compiled *liblinux.so*: +``example/dce-sctp-simple.cc`` is the script that we prepared. The kernel +is selected with ``ns3::LinuxSocketFdFactory`` (its *Library* attribute +defaults to ``liblkl.so``; ``liblinux.so``, the name of the former libos +kernel, selects it too): :: - $ export DCE_PATH="$HOME/net-next-sim:$DCE_PATH" -`dce-sctp-simple.cc `_ is the script that we prepared. In the script, you may need to load the applications by using *DceApplicationHelper* as follows. - -:: + DceManagerHelper dceManager; + dceManager.SetNetworkStack ("ns3::LinuxSocketFdFactory"); + dceManager.Install (nodes); DceApplicationHelper process; ApplicationContainer apps; - /* by default DCE, loads liblinux.so. If for some reason your library has a different name, - either create a symlink or use these commands to change the name */ - DceManagerHelper dceManager; - dceManager.SetNetworkStack ("ns3::LinuxSocketFdFactory", "Library", StringValue ("liblinux.so")); - process.SetBinary ("sctp-server"); process.ResetArguments (); process.SetStackSize (1<<16); @@ -244,19 +99,39 @@ You need first to add to your *DCE_PATH* the path to the previously compiled *li apps = process.Install (nodes.Get (1)); apps.Start (Seconds (1.5)); -5. run it ! -------------- +Network configuration is done as on Linux, with ``ip`` (iproute2 6.12, +built by ``utils/build_kernel_deps.sh``) through +``LinuxStackHelper::RunIp``, and with sysctls through +``LinuxStackHelper::SysctlSet`` and ``SysctlGet``: ``".net.ipv4.tcp_sack"`` +is ``/proc/sys/net/ipv4/tcp_sack``; ``SysctlGet`` also reads any kernel +file given by its absolute path, e.g. ``/proc/net/netstat``. + +Multipath TCP +~~~~~~~~~~~~~ + +Linux uses MPTCP only for sockets created with ``IPPROTO_MPTCP``. The +*Mptcp* attribute makes the TCP sockets of all applications MPTCP sockets, +like ``mptcpize`` does on Linux; the subflows are configured with +``ip mptcp``: :: - ./waf --run dce-simple-sctp + Config::SetDefault ("ns3::KernelSocketFdFactory::Mptcp", BooleanValue (true)); + ... + LinuxStackHelper::RunIp (client, Seconds (0.5), "mptcp limits set subflows 4 add_addr_accepted 4"); + LinuxStackHelper::RunIp (server, Seconds (0.5), "mptcp limits set subflows 4 add_addr_accepted 4"); + // a subflow from the client's second address + LinuxStackHelper::RunIp (client, Seconds (0.5), "mptcp endpoint add 10.1.1.1 dev sim1 subflow"); -If you're lucky, it's done. +See ``example/dce-iperf-mptcp.cc`` and ``test/dce-mptcp-test.cc``. -If you aren't lucky, you may face errors of DCE, such as unresolved symbols in system calls (called by userspace applications) or missing kernel functions (used by newly added CONFIG_IP_SCTP option), or invalid memory access causing segmentation fault. In that case, adding missing functions, so called *glue-code* would be the next step. +5. Run it +--------- - +:: + ./waf --run dce-sctp-simple -.. 6. Write Wrapper classes for DCE Cradle -.. 7. Test +If you face errors, such as unresolved symbols in system calls (called by +userspace applications) or invalid memory access, adding the missing +functions to DCE, so called *glue-code*, is the next step. diff --git a/doc/source/dce-wifi-video.rst b/doc/source/dce-wifi-video.rst new file mode 100644 index 00000000..971a318a --- /dev/null +++ b/doc/source/dce-wifi-video.rst @@ -0,0 +1,146 @@ +.. include:: replace.txt + +Example: video streaming over Wi-Fi with ffmpeg +=============================================== + +The ``dce-wifi-video`` example runs a real video streaming server and a real +receiver inside |ns3|: both are the unmodified ``ffmpeg`` command line tool, +executed by DCE on the real Linux kernel stack (or on the |ns3| stack), and +the packets go through a simulated 802.11n infrastructure network. + +:: + + node 0 (server STA) node 1 (AP) node 2 (client STA) + +-------------------+ +--------------+ +------------------------+ + | ffmpeg -re ... udp| | | | ffmpeg -i udp://... | + +-------------------+ | | | -c copy -f mpegts | + | Linux 4.4 (DCE) | | Linux 4.4 | | /stream.ts | + +-------------------+ +--------------+ +------------------------+ + | 802.11n 10.1.1.1| | 10.1.1.3 | | 10.1.1.2 802.11n | + +-------------------+ +--------------+ +------------------------+ + ))))))))))))))))))))) ((((((((((((((((((((((((( + +The server reads an MPEG-TS file at its native frame rate (``ffmpeg -re``) +and sends it as MPEG-TS over UDP to the client station. The access point +relays the frames between the two stations, like a real AP does. The client +receives the stream with another ``ffmpeg`` and writes it unchanged +(``-c copy``) to ``/stream.ts`` in its DCE file system, that is +``files-2/stream.ts`` on the host. + +Building ffmpeg for DCE +----------------------- + +``utils/build_kernel_deps.sh`` builds a minimal, single-threaded, position +independent ``ffmpeg`` 5.1 (the last version whose command line tool works +without threads) with only the file/UDP/RTP protocols, the MPEG-TS demuxer +and muxer and the parsers and decoders needed to probe the copied streams. +It is installed as ``bin_dce/ffmpeg`` with the other DCE binaries. The clip +streamed by the example, ``example/dce-wifi-video-sample.ts`` (47 s, H.264 +480x270 and AAC audio), is copied to ``bin_dce/video.ts`` by the DCE build. + +Running +------- + +The example is built as ``build/bin/dce-wifi-video``. Its main options are: + +``--video`` + The MPEG-TS file to stream. A relative path is looked up in the current + directory and then in the directories of ``DCE_PATH``, so the default + ``video.ts`` finds the sample clip. Any file that ffmpeg can demux and + that MPEG-TS can carry (H.264, MPEG-2, AAC, MP2...) can be used. +``--stack`` + ``linux`` (default) runs the real Linux kernel stack on the three nodes, + ``ns3`` uses the |ns3| TCP/IP stack. +``--realtime`` + Run |ns3| in real-time mode (default), so that the ``-re`` pacing of the + server matches the wall clock and the video plays at normal speed. +``--fifo`` + Write the received stream to a named pipe (default) so that a player on + the host can watch it live, instead of a regular file. +``--player`` + A host command started on ``files-2/stream.ts`` while the simulation runs. + ``{}`` is replaced by that path, otherwise the path is appended. +``--distance`` + Distance between the AP and the client station, in meters (default 10). +``--ping`` + Also run a real ``ping`` from the client to the server (default true). +``--viewer`` + Display the received video in an X11 window drawn by the receiving + ffmpeg from inside the simulation (default false, see below). + +To watch the video live while it crosses the simulated network:: + + $ cd build && ./bin/dce-wifi-video --player="vlc --play-and-exit - < {}" + +or start the simulation alone and open ``files-2/stream.ts`` with any +player that can read a pipe. Reading the pipe as standard input (``- < {}``) +avoids the seeks that players attempt on a path. + +The standard output of the example reports the frames received by the +client station every second. The output of each DCE process is in +``files-{0,2}/var/log//{stdout,stderr}``, and the 802.11 frames seen by +the AP are captured in ``dce-wifi-video-*.pcap``. + +Watching from inside the simulation (X11) +----------------------------------------- + +With ``--viewer=1`` the receiving ``ffmpeg`` does not write a file: it +decodes the stream and displays it in an X11 window, using its ``xv`` +output device, while running inside the simulated client station:: + + $ ./bin/dce-wifi-video --viewer=1 + +This works because DCE can now hand a host socket to a DCE application. +The ``DceHostUnixSocketPaths`` global value lists AF_UNIX socket path +prefixes (the example sets it to ``/tmp/.X11-unix/`` when the viewer is +enabled); when a DCE process connects a stream socket to a matching path, +DCE replaces its simulated socket by a connected socket of the host +(``model/host-socket-fd.h``). The X11 client libraries (libX11, libxcb, +libXext, libXv...) are loaded by DCE like any other dependency of the +application and run against DCE's libc, only the bytes exchanged with the X +server leave the simulation. The host socket is kept non-blocking and its +readiness is re-checked every millisecond while a DCE task waits on it, so +this needs the real-time simulator, which ``--viewer=1`` enables. The +receiver finds its X authority file as ``/.Xauthority`` in its own file +system (``files-2/``), copied there from ``$XAUTHORITY``. + +The ``DceX11Helper`` class (``helper/dce-x11-helper.h``) does the three +things a simulation needs for this: ``Enable()`` adds ``/tmp/.X11-unix/`` +to ``DceHostUnixSocketPaths``, ``InstallAuthority(node)`` writes the host's +X authority cookies into the node's file system as wildcard entries (the +hostname a DCE application sees is its node's, so host-bound entries would +not match), and ``SetEnvironment(dce)`` sets ``DISPLAY``, ``XAUTHORITY`` and +``HOME`` for the applications to install. + +The same mechanism works for any single-process X11 client built for DCE +(``-fPIC -pie -rdynamic``) that is started with ``DISPLAY`` in its +environment. ``example/x11-hello.cc`` is such a client (built when +libx11-dev is installed) and ``dce-x11-hello`` runs it on a simulated node +for a few seconds; ``test.py`` runs it when ``DISPLAY`` is set, under +``xvfb-run`` in CI. It is not meant for multi-process or heavily threaded +applications (web browsers, VLC), which DCE cannot run. The viewer plays +the sound on the host's PulseAudio server through the same passthrough +(``DceX11Helper::UsePulseAudio``, ``--audio=0`` to disable); see +:doc:`dce-x11`. + +Regression test +--------------- + +With ``--realtime=0 --fifo=0`` the example runs as fast as possible, writes +the received stream to a regular file and, when the simulation ends, +extracts the elementary streams (the concatenated PES payloads) of the sent +and received files and compares them. A stream copy re-multiplexes the +transport layer (new PAT/PMT, PCR, continuity counters, PES headers) but the +elementary streams must be bit-exact. The example exits with an error if +they are not, and ``test.py`` runs it in this mode with both IP stacks when +``bin_dce/ffmpeg`` and ``bin_dce/video.ts`` are present:: + + $ ./build/bin/dce-wifi-video --realtime=0 --fifo=0 --stopTime=60 + ... + Input .../bin_dce/video.ts: 1187784 bytes, 2 elementary streams + Output files-2/stream.ts: 1187784 bytes, 2 elementary streams + input PID 0x100: 546787 bytes + input PID 0x101: 298547 bytes + output PID 0x100: 546787 bytes + output PID 0x101: 298547 bytes + PASS: the received elementary streams are identical to the sent ones diff --git a/doc/source/dce-x11.rst b/doc/source/dce-x11.rst new file mode 100644 index 00000000..e17c311f --- /dev/null +++ b/doc/source/dce-x11.rst @@ -0,0 +1,166 @@ +you .. include:: replace.txt + +Graphical applications: X11 clients inside DCE +============================================== + +DCE applications can open windows on the host X display. The applications +run entirely inside the simulation (their X11 client libraries are loaded +by DCE against its libc, their sockets are the ones of the simulated node) +and only their connection to the X server is handed to the host. + +How it works +------------ + +The ``DceHostUnixSocketPaths`` global value lists AF_UNIX socket path +prefixes. When a DCE process connects a stream socket to a matching path, +DCE replaces the never-connected local socket by a ``HostSocketFd`` +(``model/host-socket-fd.h``): a connected socket of the host seen as a +regular file descriptor of the DCE process. The host socket is kept +non-blocking, blocking reads and writes of the application are emulated by +sleeping the DCE task, and the readiness of the socket is re-checked every +millisecond while a DCE task polls it. Since the host side lives in +wall-clock time, this requires ``ns3::RealtimeSimulatorImpl``. + +``DceX11Helper`` (``helper/dce-x11-helper.h``) does the three things a +simulation needs: ``Enable()`` adds ``/tmp/.X11-unix/`` to the global +value, ``InstallAuthority(node)`` writes the host's X authority cookies +into the node's file system (``files-/.Xauthority``) as wildcard +entries, because a DCE application sees its node's hostname and +host-bound cookies would not match, and ``SetEnvironment(dce)`` sets +``DISPLAY``, ``XAUTHORITY`` and ``HOME`` for the applications the helper +installs. + +Applications are built like any DCE application (``-fPIC``, ``-pie +-rdynamic``, no fortified libc calls or stack protector, and +``-ftls-model=global-dynamic`` if the program itself has thread local +variables: DCE loads a program like a shared object, so the fixed ``%fs`` +offsets of the local-exec model an executable normally uses would point +into the simulator's own thread local storage); the X11 client libraries +of the host (libX11, libxcb, libXext, libXv, libXft, FLTK...) are loaded +as they are. + +Examples +-------- + +``dce-x11-hello`` + Runs ``x11-hello`` (``example/x11-hello.cc``, a minimal Xlib client) on a + node and fails if it cannot draw its window. ``test.py`` runs it when + ``DISPLAY`` is set; CI runs the tests under ``xvfb-run``. + +``dce-wifi-video --viewer=1`` + The receiving ffmpeg of the video streaming example decodes the stream + and displays it in a window with its ``xv`` output device, from inside the + simulated client station (see :doc:`dce-wifi-video`). XVideo is not + available on Xvfb, so this one needs a real display. + +``dce-browser`` + A real graphical web browser. Dillo (HTML, CSS, images, no JavaScript) + runs on a Wi-Fi station, fetches pages from a real thttpd running on + another station through the access point, and draws its window on the + host display. The example writes a small web site in ``files-0/`` and + gives the browser the host's ``/usr`` and ``/etc`` (symlinks in + ``files-2/``) so that fontconfig finds its configuration and the fonts. + Closing the browser window ends the simulation:: + + $ ./bin/dce-browser + $ ./bin/dce-browser --url=http://10.1.1.1/big.html --distance=40 + + ``utils/build_kernel_deps.sh`` builds Dillo 3.1.1 for DCE (single + process, no threaded DNS, no TLS) when the FLTK headers are installed. + +``dce-browser --browser=northstar`` + Northstar, a GTK4 web browser with its own engine, CSS, JavaScript + (QuickJS) and software rendering, built for DCE by + ``utils/build_kernel_deps.sh`` when the GTK4 development packages and + meson are installed (against a minimal static libcurl, with ``-fPIC`` and + without LTO: a PIE compiled executable gets copy relocations of + stdin/stdout/stderr that DCE's per-process stdio cannot reach, and its + ``main`` exported from the project's version script). The example runs it + with ``NS_NO_SANDBOX=1 --no-watchdog`` (its Landlock/seccomp sandbox would + apply to the simulator, and its supervisor process cannot be spawned), + the GTK4 environment above, and the host's D-Bus session bus through the + passthrough (``DceX11Helper::UseSessionBus``): GApplication registers on + the bus, and GIO would otherwise try to spawn dbus-launch. + +Sound +----- + +The same passthrough carries sound: ``DceX11Helper::UsePulseAudio(node, +dce)`` adds the host's PulseAudio (or PipeWire) native socket to +``DceHostUnixSocketPaths``, copies the authentication cookie into the +node's file system and writes a client configuration without shared memory +transport, and the application plays through libpulse running inside DCE. +``dce-wifi-video --viewer=1`` plays the soundtrack this way with ffmpeg's +``pulse`` output device (``--audio=0`` to stay silent). The passthrough +rewrites the ``SCM_CREDENTIALS`` libpulse sends to the simulator's real +credentials, which is what the server authenticates. + +Checking the traffic +-------------------- + +The examples that generate traffic capture it at the access point +(``-1-0.pcap``) and check it before exiting, so that they fail as +tests when the expected traffic is missing: ``DcePcapCheck`` +(``helper/dce-pcap-check.h``) reads an ns-3 pcap back (802.11 frames, with +or without QoS control and A-MPDU aggregation, Ethernet, raw IPv4) and +counts TCP/UDP packets, payload bytes and HTTP request lines. +``dce-wifi-video`` requires the UDP stream to the receiver's port, +``dce-browser`` at least ``--minRequests`` HTTP requests (1 by default; the +test of the JavaScript page asks for 8 in 16 s). + +``dce-browser`` also starts ``numbers-server`` (``example/numbers-server.cc``, +a tiny HTTP server built as a DCE application) on the server station: its +page, ``http://10.1.1.1:8080/``, polls ``/next`` from JavaScript twice a +second and shows the Fibonacci numbers the server answers, each request an +HTTP exchange over the simulated link. The site also serves the video +sample of ``dce-wifi-video``, as the MPEG-TS file and, on +``http://10.1.1.1/video.html``, in an HTML5 ``