diff --git a/Dockerfile b/Dockerfile index 7c9bfb7..54eb4d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,6 +65,7 @@ RUN echo "Building for platform '$TARGETPLATFORM'" \ netcat-openbsd \ uuidgen \ usbutils \ + pciutils \ openssh-client \ util-linux-misc \ iproute2 \ @@ -152,12 +153,14 @@ RUN echo "Building for platform '$TARGETPLATFORM'" \ && until ssh root@localhost -p $SSH_PORT "${PACKAGE_UPDATE}"; do echo "Retrying ${PACKAGE_UPDATE} ..."; sleep 1; done\ # Download Luci, qemu guest agent and mDNS support \ && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} qemu-ga luci luci-ssl umdns losetup ${PACKAGE_EXTRA}" \ + # Download USB, PCI and PCIe support \ + && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} usbutils pciutils" \ # Download Wi-Fi access point support and Wi-Fi USB devices support \ && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} hostapd wpa-supplicant kmod-mt7921u" \ # Download celluar network support \ && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} modemmanager kmod-usb-net-qmi-wwan luci-proto-modemmanager qmi-utils" \ # Download basic GPS support \ - && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} kmod-usb-serial usbutils minicom gpsd" \ + && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} kmod-usb-serial minicom gpsd" \ # Add Wireguard support \ && ssh root@localhost -p $SSH_PORT "${PACKAGE_INSTALL} wireguard-tools luci-proto-wireguard" \ # Add Power off script support \ diff --git a/docker-compose.yml b/docker-compose.yml index 459bc5f..4020ae8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,12 +28,21 @@ services: # Make LuCI OpenWrt web interface accessible via host LAN FORWARD_LUCI: "true" - # USB passthrough + # USB pass-through # Format : like 'lsusb' is given # Currently only two USB devices are supported #USB_1: "0bda:8153" #USB_2: "0e8d:7961" + # PCI and PCIe pass-through + # Host system requirements + # - IOMMU CPU feature has to enabled in bios + # - Host kernel has to switch on intel_iommu=on or amd_iommu=on + # - Host kernel module 'vfio-pci' has to exists + # - Ubuntu 24.04 is working by default + # Format like 'lspci' is given + #PCI_1: "00:1f.6" + # Define number of VM CPUs # If unset -> 1 #CPU_COUNT: 2 @@ -48,6 +57,7 @@ services: #OPENWRT_AFTER_BOOT_CMD: "/usr/bin/fw_wan_open_http; /usr/bin/fw_wan_open_ssh" # Example to execute two commands #OPENWRT_AFTER_BOOT_CMD: "cat /etc/banner" # Custom commands example #OPENWRT_AFTER_BOOT_CMD: "a=$$((1+1)); echo The result of 1+1 is $${a}" # Shell script example + #OPENWRT_AFTER_BOOT_CMD: '[ -f /etc/first_boot_over ] || { touch /etc/first_boot_over; echo "Crazy first boot"; }' # Example to run a command only once #DEBUG: "y" @@ -61,7 +71,7 @@ services: # Use script /usr/bin/fw_wan_open_ssh to open the port. Works only if WAN_IF=host #- 8022:8022 stop_grace_period: 2m - restart: on-failure + #restart: on-failure # The following settings are mandatory to run the container devices: diff --git a/readme.md b/readme.md index 9a87c29..e8724c7 100644 --- a/readme.md +++ b/readme.md @@ -18,6 +18,7 @@ This Docker container brings you OpenWrt, a software-based router solution, in a - Attaches two physical Ethernet interfaces (LAN/WAN) exclusively into the docker container - Create virtual LAN between OpenWrt and host system (LAN only) - USB pass-through e.g. for modem or Wi-Fi dongles + - PCI pass-through e.g. for Wi-Fi dongles or PCIe Ethernet cards - Automatic configuration migration when OpenWrt image is updated - Run shell commands inside OpenWrt after boot-up diff --git a/src/cleanup_container.sh b/src/cleanup_container.sh new file mode 100644 index 0000000..2725436 --- /dev/null +++ b/src/cleanup_container.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +. /run/helpers.sh + +cleanup() { + info "Container is stopping. Performing cleanup..." + # Add your cleanup commands here + + if [ -n "$PCI_1" ]; then + echo "Attaching PCI device $PCI_1 back to the host" + echo 1 > /sys/bus/pci/devices/0000:$PCI_1/remove + echo 1 > /sys/bus/pci/rescan + fi + + echo "Cleanup complete." + exit 0 +} + +# Catch signals and call cleanup function +trap cleanup SIGTERM SIGINT + +# Wait forever to prevent that multirun will stop the complete container +# This script purpose is only to catch the signal SIGTERM and SIGINT and to execute some last commands +sleep infinity & wait \ No newline at end of file diff --git a/src/helpers.sh b/src/helpers.sh index 4603798..83caf77 100644 --- a/src/helpers.sh +++ b/src/helpers.sh @@ -14,6 +14,7 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR : "${LAN_IF:=""}" # Physical LAN interface name : "${USB_1:=""}" # USB 1 vendor and device ID : "${USB_2:=""}" # USB 2 vendor and device ID +: "${PCI_1:=""}" # PCI 1 slot (first number in lspci) : "${FORWARD_LUCI:=""}" # Make LuCI OpenWrt web interface accessible via host LAN : "${OPENWRT_AFTER_BOOT_CMD:=""}" # User command or script to run after OpenWrt is booted : "${IS_U_OS_APP:=""}" # By default this container is not a u-OS app diff --git a/src/init_container.sh b/src/init_container.sh index a8867f8..6c673a6 100644 --- a/src/init_container.sh +++ b/src/init_container.sh @@ -45,13 +45,19 @@ echo "* Content of /var/vm/openwrt_metadata.conf *" > /var/www/system_info.txt cat /var/vm/openwrt_metadata.conf >> /var/www/system_info.txt echo $'\n' >> /var/www/system_info.txt -echo "* Enviroment variables *" >> /var/www/system_info.txt +echo "* Environment variables *" >> /var/www/system_info.txt export >> /var/www/system_info.txt echo $'\n' >> /var/www/system_info.txt echo "* USB devices *" >> /var/www/system_info.txt set +e # Enable that the next command can fail lsusb >> /var/www/system_info.txt +echo $'\n' >> /var/www/system_info.txt +set -e # Revert set +a + +echo "* PCI devices *" >> /var/www/system_info.txt +set +e # Enable that the next command can fail +lspci >> /var/www/system_info.txt set -e # Revert set +a # ******* nginx handling ******* @@ -85,6 +91,7 @@ fi # Start processes exec multirun \ + "/run/cleanup_container.sh" \ "qemu-openwrt-web-backend" \ "nginx" \ "/run/run_openwrt.sh" \ diff --git a/src/run_openwrt.sh b/src/run_openwrt.sh index b6f1537..6e4c698 100755 --- a/src/run_openwrt.sh +++ b/src/run_openwrt.sh @@ -135,6 +135,44 @@ attach_veth_if () { fi } +# Prepare PCI device for qemu +attach_pci_device () { + local PCI_DEVICE=$1 + local PCI_PATH="/sys/bus/pci/devices/$PCI_DEVICE" + + info "Preparing PCI pass-through for device $PCI_DEVICE ..." + + if [ ! -e "$PCI_PATH" ]; then + echo "Error: PCI device $PCI_DEVICE not found" + exit 1 + fi + + # Unbind from current driver + local DRIVER=$(basename "$(readlink "$PCI_PATH/driver")" 2>/dev/null) + if [ "$DRIVER" ]; then + echo "Unbinding $PCI_DEVICE from $DRIVER" + echo "$PCI_DEVICE" > "$PCI_PATH/driver/unbind" + fi + + # Ensure vfio-pci module is loaded + if ! lsmod | grep -q '^vfio_pci ' ; then + echo "Loading vfio-pci kernel module" + nsenter --target 1 --uts --net --ipc --mount modprobe vfio-pci + fi + + # Bind to vfio-pci + echo "vfio-pci" > "$PCI_PATH/driver_override" + echo "$PCI_DEVICE" > /sys/bus/pci/drivers/vfio-pci/bind + + # Verify binding + if [ "$(basename "$(readlink "$PCI_PATH/driver" 2>/dev/null)")" != "vfio-pci" ]; then + echo "Failed to bind $PCI_DEVICE to vfio-pci" + exit 1 + fi + + echo "Successfully bound $PCI_DEVICE to vfio-pci" +} + # Handle dirfferent architectures if [ $CPU_ARCH = "aarch64" ]; then CPU_ARGS="-M virt -bios /usr/share/qemu/edk2-aarch64-code.fd -vga none -device ramfb" @@ -212,6 +250,20 @@ fi USB_ARGS="${USB_1_ARGS} ${USB_2_ARGS}" +# Attaching PCI devices +PCI_ARGS="" +PCI_1_ARGS="" +if [[ -z "$PCI_1" ]]; then + PCI_1_ARGS="" +else + PCI_SLOT="0000:$PCI_1" + attach_pci_device "$PCI_SLOT" + PCI_1_ARGS="-device vfio-pci,host=$PCI_SLOT" +fi + +PCI_ARGS="${PCI_1_ARGS}" + + info "Booting image using $VERS..." # See qemu command if debug is enabled @@ -230,9 +282,8 @@ exec qemu-system-"$CPU_ARCH" \ $LAN_ARGS \ $WAN_ARGS \ $USB_ARGS \ + $PCI_ARGS \ -qmp unix:/run/qmp-sock,server=on,wait=off \ -chardev socket,path=/run/qga.sock,server=on,wait=off,id=qga0 \ -device virtio-serial \ -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 - -# -kernel /var/vm/kernel.bin -append "root=fe00 console=tty0" \