first commit

This commit is contained in:
Resneptacle 2025-03-29 03:52:35 -04:00
commit a463791ea0
14 changed files with 3954 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
boot-disk.img
src/*
build/*
initramfs-source/etc/wireguard/*.conf

56
.include.sh Normal file
View File

@ -0,0 +1,56 @@
#!/bin/bash
SUDO=
if [ $UID -ne 0 ]; then
SUDO=sudo
fi
function fail {
echo -e "\n\e[31m---> Failed on step $*\e[0m" >&2
exit 1
}
function log {
echo -en "\033]0;bootstrap-chroot - $*\a"
echo -e "\e[92m---> $*\e[0m"
}
function builddir {
if [ -z "${1}" ]; then
echo "Missing argument 'dir'"
return 1
fi
if [ -d "${BUILD_DIR}/${1}" ] && [ $# -lt 2 ] || [ "${2}" != "--keep" ]; then
log "Removing previous build directory for ${1}"
rm -rf "${BUILD_DIR}/${1}" || $SUDO rm -r "${BUILD_DIR}/${1}" || fail removing previous build dir for "${1}"
fi
[ -d "${BUILD_DIR}/${1}" ] || mkdir -p "${BUILD_DIR}/${1}"
cd "${BUILD_DIR}/${1}"
log "Preparing to build ${1}"
}
function get_pkg_src {
local PKG="${1^^}"
local PKG_DIR="${1,,}"
local VAR_PKG_VERSION="${PKG}_VERSION"
local VAR_PKG_URL="${PKG}_URL"
# Replace underscores with dashes in directory
PKG_DIR="${PKG_DIR//_/-}"
local CACHE_DIR="${SOURCE_DIR}/.cache"
[ -d "${CACHE_DIR}" ] || mkdir -p "${CACHE_DIR}" || fail create source file caching directory
if [ ! -d "${SOURCE_DIR}/${PKG_DIR}-${!VAR_PKG_VERSION}" ]; then
log "Downloading ${1} source ..."
SRC_FILE=$(curl --output-dir "${CACHE_DIR}" -C- -LOsw "%{filename_effective}" "${!VAR_PKG_URL}") || fail download "${1}" source file
log "Extracting ${1} source ..."
tar -xf "${SRC_FILE}" -C "${SOURCE_DIR}/" || fail extract "${1}" source
log "Got source for ${1}"
fi
}

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# Linux Floppy Router
A Linux based router OS that fits inside a single floppy!
## Features
This system comes with:
- BusyBox based DHCP client and server
- Wireguard
- Basic IPtables build
- BusyBox telnetd for remote configuration
- Driver for the Realtek RTL8139C NIC
## Anti-Features
This system does not come with:
- A way to store configuration files, it lives entirely in RAM
- SSH or any kind of authentication on telnet
- Full IPtables feature set
- Anything advanced
## Why?
Just because it's a fun little project!
## How to build
You shold be able to build this project simply by running the `build.sh` script.
It requires the `nasm` compiler for the bootloader and a working GCC toolchain for the rest.
On Debian, run `sudo apt install nasm build-essentials` to install the required packages.
## Default configuration
The default configuration is to configure the first NIC as WAN via DHCP and the second NIC as LAN, with a static IP of 192.168.100.1/24 and a DHCP server handing out addresses from 192.168.100.101 to 192.168.100.254. Outbound connections through WAN or the Wireguard interface described below are masqueraded by default, any incoming connections on WAN or the Wireguard interface aside from established and related connections are dropped.
To configure a Wireguard tunnel, place your tunnel configuration in `initramfs-source/etc/wireguard/wg0.conf` and configure your local tunnel IP in `initramfs-source/init` in the variable `WG_IP`, in CIDR notation (e.g. `172.16.0.1/24`). If you want to route a subnet through the Wireguard tunnel, you can add that subnet in CIDR notation in the same file in the variable `WG_ROUTED_SUBNET`, e.g. `10.10.10.0/24`.
## Notes
Due to size constraints, the `cat` program is a simple shell script making use of the `read` command, in a very janky fashion. It will probably break quite easily, but should be fine for simply reading files.

141
build.sh Normal file
View File

@ -0,0 +1,141 @@
#!/bin/bash
# Builds a bootable floppy that contains a Linux OS acting as a router
set -e +h
if [ -z "${SCRIPT_DIR}" ]; then
export SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
fi
source "${SCRIPT_DIR}/.include.sh"
source "${SCRIPT_DIR}/packages.conf"
export BUILD_DIR="${SCRIPT_DIR}/build"
export SOURCE_DIR="${SCRIPT_DIR}/src"
export SYSROOT="${BUILD_DIR}/sysroot"
export OUT_FILE="${SCRIPT_DIR}/boot-disk.img"
export TARGET="i486-linux-musl"
# Default to using as many cores for building software as your system has for "make"
export MAKEFLAGS="${MAKEFLAGS:--j$(nproc)}"
if [ ! -d "${SYSROOT}/dev" ]; then
log "Creating basic directories and symlinks in target"
test -d "${SYSROOT}" || mkdir -p "${SYSROOT}" || fail create target directory "${SYSROOT}"
for DIR in /dev /proc /run /sys /usr/bin /usr/sbin /var/lib/misc /etc; do
test -d "${SYSROOT}${DIR}" || mkdir -p "${SYSROOT}${DIR}" || fail create basic directory "${DIR}" in target
done
test -e "${SYSROOT}/var/run" || ln -s ../run "${SYSROOT}/var/run" || fail create basic symlink /var/run
test -e "${SYSROOT}/bin" || ln -s usr/bin "${SYSROOT}/bin" || fail create basic symlink /bin
test -e "${SYSROOT}/sbin" || ln -s usr/sbin "${SYSROOT}/sbin" || fail create basic symlink /sbin
fi
if [ ! -e "${SYSROOT}/dev/console" ]; then
log "Creating basic device nodes"
test -e "${SYSROOT}/dev/console" || \
$SUDO mknod -m 640 "${SYSROOT}/dev/console" c 5 1 || fail create console node
test -e "${SYSROOT}/dev/null" || \
$SUDO mknod -m 664 "${SYSROOT}/dev/null" c 1 3 || fail create null node
fi
if [ ! -d "${BUILD_DIR}/musl-cross-toolchain" ]; then
log "Build musl-cross-make toolchain"
get_pkg_src "musl_cross_make"
cd "${SOURCE_DIR}/musl-cross-make-${MUSL_CROSS_MAKE_VERSION}"
echo "TARGET = ${TARGET}" > config.mak
echo "OUTPUT = ${BUILD_DIR}/musl-cross-toolchain" >> config.mak
make >/dev/null || fail build musl-cross-make toolchain
make install >/dev/null || fail install musl-cross-make toolchain
fi
export PATH="$PATH:${BUILD_DIR}/musl-cross-toolchain/bin"
if [ ! -e "${SYSROOT}/bin/busybox" ]; then
log "Build BusyBox binary"
get_pkg_src "busybox"
if [ ! -e "${SOURCE_DIR}/busybox-${BUSYBOX_VERSION}/.config" ]; then
cd "${SOURCE_DIR}/busybox-${BUSYBOX_VERSION}"
cp "${SCRIPT_DIR}/configs/busybox.config" ".config" || fail copy BusyBox configuration
sed "s#%%COMPPREFIX%%#${TARGET}-#" -i ".config" || fail Modify BusyBox compiler prefix
sed "s#%%TARGET_DIR%%#${SYSROOT}#" -i ".config" || fail Modify BusyBox target directory
fi
make >/dev/null || fail build BusyBox
make install >/dev/null || fail install BusyBox
fi
if [ ! -e "${SYSROOT}/usr/sbin/wg" ]; then
log "Build Wireguard Tools"
get_pkg_src "wireguard_tools"
cd "${SOURCE_DIR}/wireguard-tools-${WIREGUARD_TOOLS_VERSION}/src"
CC="${TARGET}-gcc" LD="${TARGET}-ld" CFLAGS="--static -static" LDFLAGS="-static" \
make >/dev/null || fail build Wireguard Tools
"${TARGET}-strip" wg || fail strip Wireguard binary
test -d "${SYSROOT}/usr/sbin" || mkdir -p "${SYSROOT}/usr/sbin" || fail create /usr/sbin in target
cp wg "${SYSROOT}/usr/sbin/wg" || fail copy Wireguard binary to target
fi
if [ ! -e "${SYSROOT}/usr/sbin/iptables" ]; then
log "Build IPtables"
get_pkg_src iptables
# When building IPtables statically, it includes all known extensions into the final binary
# We only need a few of them, so here we copy only the necessary extensions into the source tree
cd "${SOURCE_DIR}/iptables-${IPTABLES_VERSION}"
test -d "extensions.orig" || mv "extensions" "extensions.orig" || fail move IPtables extensions directory
test -d "extensions" || mkdir "extensions" || fail create new IPtables extensions directory
while read -r FILE; do
cp "extensions.orig/${FILE}" "extensions/" || fail copy IPtables extension file "${FILE}"
done < "${SCRIPT_DIR}/configs/iptables-extension-files"
builddir "iptables"
CFLAGS="--static -static" LDFLAGS="-static" \
"${SOURCE_DIR}/iptables-${IPTABLES_VERSION}/configure" \
--prefix= \
--disable-nftables \
--disable-devel \
--disable-ipv6 \
--disable-largefile \
--enable-static \
--disable-shared \
--host="${TARGET}" >/dev/null || fail configure IPtables
make >/dev/null || fail build IPtables
make install DESTDIR="${BUILD_DIR}/iptables-install" >/dev/null || fail temporarily install IPtables
"${TARGET}-strip" "${BUILD_DIR}/iptables-install/sbin/xtables-legacy-multi" || fail strip IPtables binary
cp -a "${BUILD_DIR}/iptables-install/sbin/." "${SYSROOT}/usr/sbin" || fail copy IPtables binary to target
fi
if [ -d "${SCRIPT_DIR}/initramfs-source" ]; then
log "Copying base files into target"
cp -a "${SCRIPT_DIR}/initramfs-source/." "${SYSROOT}/" || fail copy initramfs base files into target
fi
log "Building Linux kernel"
get_pkg_src "linux"
cd "${SOURCE_DIR}/linux-${LINUX_VERSION}"
if [ ! -e "${SOURCE_DIR}/linux-${LINUX_VERSION}/.config" ]; then
cp "${SCRIPT_DIR}/configs/linux.config" ".config" || fail copy Linux configuration
sed "s#%%INITRAMFS_SOURCE%%#${SYSROOT}#" -i ".config" || fail Modify Linux initramfs file source
sed "s#%%BUILDER_UID%%#$(id -u)#" -i ".config" || fail Modify Linux building user ID
sed "s#%%BUILDER_GID%%#$(id -g)#" -i ".config" || fail Modify Linux building user group ID
fi
make bzImage >/dev/null || fail build Linux kernel
KERNEL_SIZE="$(stat -c "%s" "arch/x86/boot/bzImage")"
if (( KERNEL_SIZE + 512 > 1024 * 1440 )); then
fail "check kernel size, $(( 1024 * 1440 - 512 - KERNEL_SIZE )) bytes over"
else
log "Kernel size valid"
fi
log "Create bootable floppy image"
get_pkg_src "tiny_floppy_bootloader"
cp "${SCRIPT_DIR}/configs/tfb-build.sh" "${SOURCE_DIR}/tiny-floppy-bootloader-${TINY_FLOPPY_BOOTLOADER_VERSION}/"
bash "${SOURCE_DIR}/tiny-floppy-bootloader-${TINY_FLOPPY_BOOTLOADER_VERSION}/tfb-build.sh" \
"${SOURCE_DIR}/linux-${LINUX_VERSION}/arch/x86/boot/bzImage" \
"${OUT_FILE}"
log "Done! Your floppy image is at ${OUT_FILE}"
log "To try it, run 'sudo bash run-qemu.sh $(basename "${OUT_FILE}")'"

1215
configs/busybox.config Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,49 @@
./generic.txlate
./GNUmakefile.in
./iptables.t
./libipt_DNAT.t
./libipt_LOG.t
./libipt_LOG.txlate
./libipt_MASQUERADE.t
./libipt_MASQUERADE.txlate
./libipt_SNAT.t
./libipt_SNAT.txlate
./libxt_conntrack.c
./libxt_conntrack.man
./libxt_conntrack.t
./libxt_conntrack.txlate
./libxt_CT.c
./libxt_CT.man
./libxt_CT.t
./libxt_DNAT.man
./libxt_DNAT.txlate
./libxt_icmp.h
./libxt_MASQUERADE.man
./libxt_NAT.c
./libxt_NETMAP.man
./libxt_NOTRACK.man
./libxt_NOTRACK.t
./libxt_NOTRACK.txlate
./libxt_REDIRECT.man
./libxt_REDIRECT.t
./libxt_REDIRECT.txlate
./libxt_set.c
./libxt_SET.c
./libxt_set.h
./libxt_set.man
./libxt_SET.man
./libxt_set.t
./libxt_SET.t
./libxt_SNAT.man
./libxt_standard.c
./libxt_standard.t
./libxt_state.man
./libxt_state.t
./libxt_tcp.c
./libxt_tcp.man
./libxt_tcp.t
./libxt_tcp.txlate
./libxt_udp.c
./libxt_udp.man
./libxt_udp.t
./libxt_udp.txlate

2165
configs/linux.config Normal file

File diff suppressed because it is too large Load Diff

49
configs/tfb-build.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/bash -e
# Tiny Linux Bootloader
# (c) 2014- Dr Gareth Owen (www.ghowen.me). All rights reserved.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
export SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
cd "${SCRIPT_DIR}"
INPUT="bflop.asm"
KERN="${1:-bzImage}"
OUTPUT="${2:-boot-disk.img}"
PAD_TO_FLOPPY="${PAD_TO_FLOPPY:-y}"
#size of kern + ramdisk
K_SZ="$(stat -c %s "${KERN}")"
#padding to make it up to a sector
K_PAD="$((512 - "${K_SZ}" % 512))"
nasm -o "${OUTPUT}" "${INPUT}"
cat "${KERN}" >> "${OUTPUT}"
if [[ "${K_PAD}" -lt 512 ]]; then
dd if=/dev/zero bs=1 count="${K_PAD}" >> "${OUTPUT}"
fi
TOTAL="$(stat -c %s "${OUTPUT}")"
if [[ "${TOTAL}" -gt 1474560 ]]; then
echo "Warning: Image exceeds floppy size by $(( TOTAL - 1474560 )) bytes"
exit 1
else
echo "Free bytes on floppy image: $(( 1474560 - TOTAL ))"
fi
if [ "$PAD_TO_FLOPPY" = y ]; then
dd if=/dev/zero bs=1 count="$((1474560 - TOTAL))" >> "${OUTPUT}" 2>/dev/null
fi

14
initramfs-source/bin/cat Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
if [ $# -gt 0 ]; then
while [ $# -gt 0 ]; do
while read -r _LINE; do
echo "${_LINE}"
done < "${1}"
shift
done
else
while read -r _LINE; do
echo "${_LINE}"
done
fi

View File

@ -0,0 +1,6 @@
start 192.168.100.101
end 192.168.100.254
interface eth1
option router 192.168.100.1
option dns 1.1.1.1 1.0.0.1
option lease 3600

135
initramfs-source/init Executable file
View File

@ -0,0 +1,135 @@
#!/bin/busybox sh
## Init script for floppylinux ##
export PATH="/bin:/sbin/:/usr/bin:/usr/sbin"
WAN_IF=eth0
LAN_IF=eth1
LAN_IP=192.168.100.1
LAN_SUBNET=24
WG_IF=wg0
WG_IP=172.16.0.1/24
WG_ROUTED_SUBNET=
## Functions ##
# Log text to console
log () {
echo "[BOOT] $1"
}
# Show boot banner
show_banner () {
echo ""
echo "###################################"
echo "### Itty-Bitty Floppy Router OS ###"
echo "###################################"
echo ""
}
# Show errors
err () {
echo "[WARN] Something went wrong. Dropping you to shell"
$CMD_SHELL
exit 1
}
## Begin booting userspace ##
show_banner
if [ ! -e /bin/sh ]; then
log "Installing BusyBox applets"
/bin/busybox --install -s /bin || err
fi
log "Ensuring necessary directories exist"
for DIR in /proc /sys /dev /run /var/lib/misc; do
[ -e "${DIR}" ] || mkdir -p "${DIR}" || err
done
log "Mounting /proc"
mount -t proc proc /proc || err
log "Mounting /sys"
mount -t sysfs sysfs /sys || err
log "Mounting /dev"
mount -t devtmpfs devtmpfs /dev || err
log "Mounting /dev/pts"
mkdir -p /dev/pts || err
mount -t devpts devpts /dev/pts >/dev/null || err
log "Configuring WAN interface"
ip link set dev "${WAN_IF}" up
udhcpc "${WAN_IF}" >/dev/null
log "Configuring LAN interface"
ip link set dev "${LAN_IF}" up
ip addr add "${LAN_IP}/${LAN_SUBNET}" dev "${LAN_IF}"
if [ -e "/usr/sbin/iptables" ]; then
log "Configuring IPtables"
# Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Allow all related and established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Drop invalid packets
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# Allow communication on loopback interface
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Allow input from LAN
iptables -A INPUT -i "${LAN_IF}" -j ACCEPT
# Allow forwarding from LAN to WAN
iptables -A FORWARD -i "${LAN_IF}" -o "${WAN_IF}" -j ACCEPT
# Masquerade outgoing packets on WAN and Wireguard
iptables -A POSTROUTING -t nat -o "${WAN_IF}" -j MASQUERADE
iptables -A POSTROUTING -t nat -o "${WG_IF}" -j MASQUERADE
fi
read -r $IP_FORWARD < /proc/sys/net/ipv4/ip_forward
if [ "${IP_FORWARD}" != "1" ]; then
log "Enabling IP forwarding"
echo 1 > /proc/sys/net/ipv4/ip_forward
fi
if [ -f "/etc/wireguard/${WG_IF}.conf" ]; then
log "Configuring Wireguard interface"
ip link add ${WG_IF} type wireguard
wg setconf ${WG_IF} /etc/wireguard/${WG_IF}.conf
ip link set dev "${WG_IF}" up
ip addr add "${WG_IP}" dev ${WG_IF}
if [ -n "${WG_ROUTED_SUBNET}" ]; then
ip route add "${WG_ROUTED_SUBNET}" dev ${WG_IF}
fi
fi
if [ -f "/etc/udhcpd.$LAN_IF.conf" ]; then
log "Starting DHCP server"
udhcpd "/etc/udhcpd.$LAN_IF.conf"
fi
log "Starting telnetd"
echo "Floppy Router OS" > /etc/issue
telnetd -l /bin/sh
log "Bootup complete"
log "Starting shell"
while echo -n; do
/bin/sh
done

View File

@ -0,0 +1,53 @@
#!/bin/sh
# udhcpc script
# Store this as executable unix text file: /usr/share/udhcpc/default.script
# Adjusted by Snep
set -e
RESOLV_CONF="/etc/resolv.conf"
if [ $# -lt 1 ]; then
echo "Error: Should be called from udhcpc with one argument"
exit 1
fi
case "$1" in
leasefail)
echo "$0: Aquiring lease failed, reason: $message"
exit 1
;;
deconfig)
echo "$0: Deconfiguring $interface ..."
/sbin/ip addr flush dev $interface
;;
bound)
echo "$0: DHCP lease got on $interface ($ip) ..."
/sbin/ip addr add "${ip}/${subnet}" dev "${interface}"
# Replace existing default route upon new route
if [ -n "$router" ] ; then
for i in $router ; do
/sbin/ip route replace default via "${i}" dev "${interface}"
done
fi
# Configure DNS nameserver and search domain
if [ -n "${dns}" ] || [ -n "${domain}" ]; then
echo -n > $RESOLV_CONF
if [ -n "$domain" ]; then
echo "search ${domain}" >> $RESOLV_CONF
fi
for i in $dns ; do
echo "nameserver ${i}" >> $RESOLV_CONF
done
fi
;;
renew)
echo "$0: DHCP renewal on $interface ($ip) ..."
/sbin/ip addr replace "${ip}/${subnet}" dev "${interface}"
;;
esac
exit 0

17
packages.conf Normal file
View File

@ -0,0 +1,17 @@
MUSL_CROSS_MAKE_VERSION="0.9.10"
MUSL_CROSS_MAKE_URL="https://github.com/richfelker/musl-cross-make/archive/refs/tags/v${MUSL_CROSS_MAKE_VERSION}.tar.gz"
BUSYBOX_VERSION="1.37.0"
BUSYBOX_URL="https://busybox.net/downloads/busybox-${BUSYBOX_VERSION}.tar.bz2"
WIREGUARD_TOOLS_VERSION="13f4ac4cb74b5a833fa7f825ba785b1e5774e84f"
WIREGUARD_TOOLS_URL="https://git.zx2c4.com/wireguard-tools/snapshot/wireguard-tools-${WIREGUARD_TOOLS_VERSION}.tar.xz"
IPTABLES_VERSION="1.8.9"
IPTABLES_URL="https://www.netfilter.org/pub/iptables/iptables-${IPTABLES_VERSION}.tar.xz"
LINUX_VERSION="6.14"
LINUX_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${LINUX_VERSION}.tar.xz"
TINY_FLOPPY_BOOTLOADER_VERSION="master"
TINY_FLOPPY_BOOTLOADER_URL="https://github.com/guineawheek/tiny-floppy-bootloader/archive/refs/heads/master.tar.gz"

11
run-qemu.sh Normal file
View File

@ -0,0 +1,11 @@
#!/bin/bash
# Run QEMU emulating a 486, booting from a floppy specified as first parameter
set -e
qemu-system-i386 \
-m 16m \
-fda "${1}" \
-nographic \
-nic user,model=rtl8139 \
-nic bridge,br=vmbr-lan,model=rtl8139
tput reset