Compare commits
8 Commits
v0.6.0
...
89391e1781
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89391e1781 | ||
|
|
8c853c3605 | ||
|
|
fe28c04c19 | ||
|
|
66be99bef0 | ||
|
|
94b122ac25 | ||
|
|
a07158ed22 | ||
|
|
1cd552d2dc | ||
|
|
3af40cb275 |
14
Cargo.toml
14
Cargo.toml
@@ -16,6 +16,14 @@ path = "src/lib.rs"
|
|||||||
name = "btest"
|
name = "btest"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "btest-client"
|
||||||
|
path = "src/bin/client_only.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "btest-server"
|
||||||
|
path = "src/bin/server_only.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
@@ -38,3 +46,9 @@ opt-level = 3
|
|||||||
lto = true
|
lto = true
|
||||||
strip = true
|
strip = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
# Minimal size profile for embedded/OpenWrt targets
|
||||||
|
[profile.release-small]
|
||||||
|
inherits = "release"
|
||||||
|
opt-level = "z"
|
||||||
|
panic = "abort"
|
||||||
|
|||||||
51
README.md
51
README.md
@@ -42,14 +42,51 @@ On wired gigabit links, expect line-rate performance in both TCP and UDP modes.
|
|||||||
cargo install --path .
|
cargo install --path .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pre-built binary (Linux x86_64)
|
### Pre-built binaries
|
||||||
|
|
||||||
|
Download from [releases](https://git.manko.yoga/manawenuz/btest-rs/releases) or [GitHub releases](https://github.com/manawenuz/btest-rs/releases):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Cross-compile from macOS (requires Docker)
|
# Linux x86_64
|
||||||
scripts/build-linux.sh
|
curl -L <release-url>/btest-linux-x86_64.tar.gz | tar xz
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
|
||||||
# Copy to server
|
# Raspberry Pi 4/5 (64-bit OS)
|
||||||
scp dist/btest root@yourserver:/usr/local/bin/btest
|
curl -L <release-url>/btest-linux-aarch64.tar.gz | tar xz
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
|
||||||
|
# Raspberry Pi 3/Zero 2 (32-bit OS)
|
||||||
|
curl -L <release-url>/btest-linux-armv7.tar.gz | tar xz
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
# Download btest-windows-x86_64.zip from releases
|
||||||
|
```
|
||||||
|
|
||||||
|
### Raspberry Pi
|
||||||
|
|
||||||
|
The static musl binaries run on any Raspberry Pi without dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On the Pi — detect architecture and install
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case $ARCH in
|
||||||
|
aarch64) FILE=btest-linux-aarch64.tar.gz ;;
|
||||||
|
armv7l) FILE=btest-linux-armv7.tar.gz ;;
|
||||||
|
*) echo "Unsupported: $ARCH"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
curl -LO "https://github.com/manawenuz/btest-rs/releases/latest/download/$FILE"
|
||||||
|
tar xzf "$FILE"
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
rm "$FILE"
|
||||||
|
|
||||||
|
# Run as server
|
||||||
|
btest -s -a admin -p password --ecsrp5
|
||||||
|
|
||||||
|
# Or install as systemd service
|
||||||
|
curl -LO https://raw.githubusercontent.com/manawenuz/btest-rs/main/scripts/install-service.sh
|
||||||
|
sudo bash install-service.sh --auth-user admin --auth-pass password
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
@@ -208,7 +245,9 @@ See [KNOWN_ISSUES.md](KNOWN_ISSUES.md) for the full list including:
|
|||||||
- **Windows binaries** — cross-compiled but untested
|
- **Windows binaries** — cross-compiled but untested
|
||||||
- **IPv6 UDP on Linux** — untested, likely works fine
|
- **IPv6 UDP on Linux** — untested, likely works fine
|
||||||
|
|
||||||
Contributions and bug reports welcome: https://git.manko.yoga/manawenuz/btest-rs/issues
|
Contributions and bug reports welcome:
|
||||||
|
- https://github.com/manawenuz/btest-rs/issues
|
||||||
|
- https://git.manko.yoga/manawenuz/btest-rs/issues
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
52
deploy/alpine/APKBUILD
Normal file
52
deploy/alpine/APKBUILD
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Maintainer: Siavash Sameni <manwe at manko dot yoga>
|
||||||
|
pkgname=btest-rs
|
||||||
|
pkgver=0.6.0
|
||||||
|
pkgrel=0
|
||||||
|
pkgdesc="MikroTik Bandwidth Test server and client with EC-SRP5 auth"
|
||||||
|
url="https://github.com/manawenuz/btest-rs"
|
||||||
|
license="MIT AND Apache-2.0"
|
||||||
|
arch="x86_64 aarch64 armv7"
|
||||||
|
makedepends="cargo rust"
|
||||||
|
install="$pkgname.pre-install"
|
||||||
|
source="$pkgname-$pkgver.tar.gz::https://github.com/manawenuz/btest-rs/archive/refs/tags/v$pkgver.tar.gz
|
||||||
|
btest.initd
|
||||||
|
"
|
||||||
|
sha256sums="SKIP
|
||||||
|
SKIP
|
||||||
|
"
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
default_prepare
|
||||||
|
cd "$builddir"
|
||||||
|
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$builddir"
|
||||||
|
export CARGO_TARGET_DIR=target
|
||||||
|
cargo build --frozen --release
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
cd "$builddir"
|
||||||
|
cargo test --frozen --release
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$builddir"
|
||||||
|
|
||||||
|
# binary
|
||||||
|
install -Dm755 "target/release/btest" "$pkgdir/usr/bin/btest"
|
||||||
|
|
||||||
|
# man page
|
||||||
|
install -Dm644 "docs/man/btest.1" "$pkgdir/usr/share/man/man1/btest.1"
|
||||||
|
|
||||||
|
# license
|
||||||
|
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
|
|
||||||
|
# documentation
|
||||||
|
install -Dm644 "README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
|
||||||
|
|
||||||
|
# OpenRC init script
|
||||||
|
install -Dm755 "$srcdir/btest.initd" "$pkgdir/etc/init.d/btest"
|
||||||
|
}
|
||||||
37
deploy/alpine/btest.initd
Executable file
37
deploy/alpine/btest.initd
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/sbin/openrc-run
|
||||||
|
# OpenRC init script for btest-rs
|
||||||
|
# MikroTik Bandwidth Test server
|
||||||
|
|
||||||
|
name="btest"
|
||||||
|
description="MikroTik Bandwidth Test Server (btest-rs)"
|
||||||
|
command="/usr/bin/btest"
|
||||||
|
command_args="-s"
|
||||||
|
command_background=true
|
||||||
|
pidfile="/run/$name.pid"
|
||||||
|
|
||||||
|
# Run as dedicated user if it exists, otherwise root
|
||||||
|
command_user="btest:btest"
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
output_log="/var/log/$name/$name.log"
|
||||||
|
error_log="/var/log/$name/$name.err"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need net
|
||||||
|
after firewall
|
||||||
|
use dns logger
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pre() {
|
||||||
|
# Create log directory
|
||||||
|
checkpath -d -m 0755 -o "$command_user" /var/log/$name
|
||||||
|
|
||||||
|
# Create runtime directory
|
||||||
|
checkpath -d -m 0755 -o "$command_user" /run
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
ebegin "Stopping $name"
|
||||||
|
start-stop-daemon --stop --pidfile "$pidfile" --retry TERM/5/KILL/3
|
||||||
|
eend $?
|
||||||
|
}
|
||||||
118
deploy/alpine/test-alpine.sh
Executable file
118
deploy/alpine/test-alpine.sh
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Test Alpine Linux packaging for btest-rs
|
||||||
|
# Runs inside an Alpine Docker container to build and verify the APK.
|
||||||
|
#
|
||||||
|
# Usage (from repository root):
|
||||||
|
# docker run --rm -v "$PWD":/src alpine:latest /src/deploy/alpine/test-alpine.sh
|
||||||
|
#
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
ALPINE_DIR="/src/deploy/alpine"
|
||||||
|
|
||||||
|
echo "=== Alpine APK packaging test ==="
|
||||||
|
echo "Alpine version: $(cat /etc/alpine-release)"
|
||||||
|
|
||||||
|
# ── Install build dependencies ──────────────────────────────────────
|
||||||
|
echo "--- Installing build dependencies ---"
|
||||||
|
apk update
|
||||||
|
apk add --no-cache \
|
||||||
|
alpine-sdk \
|
||||||
|
rust \
|
||||||
|
cargo \
|
||||||
|
sudo
|
||||||
|
|
||||||
|
# ── Create a non-root build user (abuild refuses to run as root) ──
|
||||||
|
echo "--- Setting up build user ---"
|
||||||
|
adduser -D builder
|
||||||
|
addgroup builder abuild
|
||||||
|
echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
|
|
||||||
|
# ── Prepare build tree ──────────────────────────────────────────────
|
||||||
|
echo "--- Preparing build tree ---"
|
||||||
|
BUILD_DIR="/home/builder/btest-rs"
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
cp "$ALPINE_DIR/APKBUILD" "$BUILD_DIR/"
|
||||||
|
cp "$ALPINE_DIR/btest.initd" "$BUILD_DIR/"
|
||||||
|
|
||||||
|
# Generate signing key (required by abuild)
|
||||||
|
su builder -c "abuild-keygen -a -n -q"
|
||||||
|
sudo cp /home/builder/.abuild/*.rsa.pub /etc/apk/keys/
|
||||||
|
|
||||||
|
# ── Build the package ──────────────────────────────────────────────
|
||||||
|
echo "--- Building APK ---"
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
chown -R builder:builder "$BUILD_DIR"
|
||||||
|
su builder -c "abuild -r"
|
||||||
|
|
||||||
|
echo "--- Build succeeded ---"
|
||||||
|
|
||||||
|
# ── Locate and install the package ──────────────────────────────────
|
||||||
|
echo "--- Installing built APK ---"
|
||||||
|
APK_FILE=$(find /home/builder/packages -name "btest-rs-*.apk" -not -name "*doc*" | head -1)
|
||||||
|
if [ -z "$APK_FILE" ]; then
|
||||||
|
echo "FAIL: APK file not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Found APK: $APK_FILE"
|
||||||
|
apk add --allow-untrusted "$APK_FILE"
|
||||||
|
|
||||||
|
# ── Verify installation ────────────────────────────────────────────
|
||||||
|
echo "--- Verifying installation ---"
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
# Binary exists and is executable
|
||||||
|
if command -v btest >/dev/null 2>&1; then
|
||||||
|
echo "PASS: btest binary installed"
|
||||||
|
else
|
||||||
|
echo "FAIL: btest binary not found in PATH"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Binary runs (show version / help)
|
||||||
|
if btest --help >/dev/null 2>&1; then
|
||||||
|
echo "PASS: btest --help exits successfully"
|
||||||
|
else
|
||||||
|
echo "FAIL: btest --help failed"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Man page installed
|
||||||
|
if [ -f /usr/share/man/man1/btest.1 ]; then
|
||||||
|
echo "PASS: man page installed"
|
||||||
|
else
|
||||||
|
echo "FAIL: man page not found"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# License installed
|
||||||
|
if [ -f /usr/share/licenses/btest-rs/LICENSE ]; then
|
||||||
|
echo "PASS: LICENSE installed"
|
||||||
|
else
|
||||||
|
echo "FAIL: LICENSE not found"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# OpenRC init script installed
|
||||||
|
if [ -f /etc/init.d/btest ]; then
|
||||||
|
echo "PASS: OpenRC init script installed"
|
||||||
|
else
|
||||||
|
echo "FAIL: OpenRC init script not found"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Init script is executable
|
||||||
|
if [ -x /etc/init.d/btest ]; then
|
||||||
|
echo "PASS: init script is executable"
|
||||||
|
else
|
||||||
|
echo "FAIL: init script is not executable"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Summary ─────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
if [ "$FAIL" -eq 0 ]; then
|
||||||
|
echo "=== All Alpine packaging tests PASSED ==="
|
||||||
|
else
|
||||||
|
echo "=== Some Alpine packaging tests FAILED ==="
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
15
deploy/aur/.SRCINFO
Normal file
15
deploy/aur/.SRCINFO
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
pkgbase = btest-rs
|
||||||
|
pkgdesc = MikroTik Bandwidth Test (btest) server and client with EC-SRP5 auth
|
||||||
|
pkgver = 0.6.0
|
||||||
|
pkgrel = 1
|
||||||
|
url = https://github.com/manawenuz/btest-rs
|
||||||
|
arch = x86_64
|
||||||
|
arch = aarch64
|
||||||
|
arch = armv7h
|
||||||
|
license = MIT
|
||||||
|
license = Apache-2.0
|
||||||
|
makedepends = cargo
|
||||||
|
source = btest-rs-0.6.0.tar.gz::https://github.com/manawenuz/btest-rs/archive/refs/tags/v0.6.0.tar.gz
|
||||||
|
sha256sums = SKIP
|
||||||
|
|
||||||
|
pkgname = btest-rs
|
||||||
58
deploy/aur/PKGBUILD
Normal file
58
deploy/aur/PKGBUILD
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Maintainer: Siavash Sameni <manwe at manko dot yoga>
|
||||||
|
pkgname=btest-rs
|
||||||
|
pkgver=0.6.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="MikroTik Bandwidth Test (btest) server and client with EC-SRP5 auth"
|
||||||
|
arch=('x86_64' 'aarch64' 'armv7h')
|
||||||
|
url="https://github.com/manawenuz/btest-rs"
|
||||||
|
license=('MIT' 'Apache-2.0')
|
||||||
|
depends=()
|
||||||
|
makedepends=('cargo')
|
||||||
|
source=("$pkgname-$pkgver.tar.gz::https://github.com/manawenuz/btest-rs/archive/refs/tags/v$pkgver.tar.gz")
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
export CARGO_TARGET_DIR=target
|
||||||
|
cargo build --frozen --release
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
install -Dm755 "target/release/btest" "$pkgdir/usr/bin/btest"
|
||||||
|
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
|
install -Dm644 "docs/man/btest.1" "$pkgdir/usr/share/man/man1/btest.1"
|
||||||
|
install -Dm644 "README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
|
||||||
|
|
||||||
|
# systemd service
|
||||||
|
install -Dm644 /dev/stdin "$pkgdir/usr/lib/systemd/system/btest.service" <<EOF
|
||||||
|
[Unit]
|
||||||
|
Description=MikroTik Bandwidth Test Server (btest-rs)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/btest -s
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
DynamicUser=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
}
|
||||||
40
deploy/aur/test-aur.sh
Executable file
40
deploy/aur/test-aur.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Test the PKGBUILD in a Docker Arch Linux container.
|
||||||
|
# Usage: ./deploy/aur/test-aur.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/../.."
|
||||||
|
|
||||||
|
echo "=== Testing AUR PKGBUILD in Arch Linux container ==="
|
||||||
|
|
||||||
|
docker run --rm -v "$(pwd):/src:ro" archlinux:latest bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Install base-devel and rust
|
||||||
|
pacman -Syu --noconfirm base-devel rustup git
|
||||||
|
rustup default stable
|
||||||
|
|
||||||
|
# Create build user (makepkg refuses to run as root)
|
||||||
|
useradd -m builder
|
||||||
|
echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||||
|
|
||||||
|
# Copy source and PKGBUILD
|
||||||
|
su builder -c "
|
||||||
|
mkdir -p /tmp/build && cd /tmp/build
|
||||||
|
cp /src/deploy/aur/PKGBUILD .
|
||||||
|
|
||||||
|
# Build the package
|
||||||
|
makepkg -si --noconfirm
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
echo ''
|
||||||
|
echo '=== Installed ==='
|
||||||
|
btest --version
|
||||||
|
btest --help | head -5
|
||||||
|
echo ''
|
||||||
|
echo '=== Files ==='
|
||||||
|
pacman -Ql btest-rs
|
||||||
|
echo ''
|
||||||
|
echo '=== SUCCESS ==='
|
||||||
|
"
|
||||||
|
'
|
||||||
208
deploy/deb/build-deb.sh
Executable file
208
deploy/deb/build-deb.sh
Executable file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build-deb.sh -- Build a Debian/Ubuntu .deb package for btest-rs
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./deploy/deb/build-deb.sh # uses dist/btest or target/release/btest
|
||||||
|
# BTEST_BIN=path/to/btest ./deploy/deb/build-deb.sh
|
||||||
|
#
|
||||||
|
# Requirements: dpkg-deb, gzip (standard on Debian/Ubuntu build hosts)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Package metadata
|
||||||
|
###############################################################################
|
||||||
|
PKG_NAME="btest-rs"
|
||||||
|
PKG_VERSION="0.6.0"
|
||||||
|
PKG_ARCH="amd64"
|
||||||
|
PKG_MAINTAINER="Siavash Sameni <manwe@manko.yoga>"
|
||||||
|
PKG_DESCRIPTION="MikroTik Bandwidth Test (btest) server and client with EC-SRP5 auth"
|
||||||
|
PKG_HOMEPAGE="https://github.com/manawenuz/btest-rs"
|
||||||
|
PKG_LICENSE="MIT AND Apache-2.0"
|
||||||
|
PKG_SECTION="net"
|
||||||
|
PKG_PRIORITY="optional"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Paths
|
||||||
|
###############################################################################
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
# Locate the pre-built binary
|
||||||
|
if [[ -n "${BTEST_BIN:-}" ]]; then
|
||||||
|
: # caller provided an explicit path
|
||||||
|
elif [[ -f "$REPO_ROOT/dist/btest" ]]; then
|
||||||
|
BTEST_BIN="$REPO_ROOT/dist/btest"
|
||||||
|
elif [[ -f "$REPO_ROOT/target/release/btest" ]]; then
|
||||||
|
BTEST_BIN="$REPO_ROOT/target/release/btest"
|
||||||
|
else
|
||||||
|
echo "Error: cannot find btest binary."
|
||||||
|
echo " Build first (cargo build --release) or set BTEST_BIN=path/to/btest"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify the binary exists and is executable
|
||||||
|
if [[ ! -f "$BTEST_BIN" ]]; then
|
||||||
|
echo "Error: $BTEST_BIN does not exist."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Using binary: $BTEST_BIN"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Prepare staging tree
|
||||||
|
###############################################################################
|
||||||
|
DEB_FILE="${PKG_NAME}_${PKG_VERSION}_${PKG_ARCH}.deb"
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
echo "==> Staging in $STAGE"
|
||||||
|
|
||||||
|
# Binary
|
||||||
|
install -Dm755 "$BTEST_BIN" "$STAGE/usr/bin/btest"
|
||||||
|
|
||||||
|
# Man page
|
||||||
|
if [[ -f "$REPO_ROOT/docs/man/btest.1" ]]; then
|
||||||
|
install -Dm644 "$REPO_ROOT/docs/man/btest.1" "$STAGE/usr/share/man/man1/btest.1"
|
||||||
|
gzip -9n "$STAGE/usr/share/man/man1/btest.1"
|
||||||
|
else
|
||||||
|
echo "Warning: docs/man/btest.1 not found -- skipping man page"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# systemd service unit
|
||||||
|
install -d "$STAGE/usr/lib/systemd/system"
|
||||||
|
cat > "$STAGE/usr/lib/systemd/system/btest.service" <<'UNIT'
|
||||||
|
[Unit]
|
||||||
|
Description=MikroTik Bandwidth Test Server (btest-rs)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/btest -s
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
DynamicUser=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
UNIT
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
install -Dm644 "$REPO_ROOT/README.md" "$STAGE/usr/share/doc/$PKG_NAME/README.md"
|
||||||
|
|
||||||
|
# License
|
||||||
|
install -Dm644 "$REPO_ROOT/LICENSE" "$STAGE/usr/share/licenses/$PKG_NAME/LICENSE"
|
||||||
|
|
||||||
|
# Debian copyright file (policy-compliant copy in /usr/share/doc)
|
||||||
|
install -d "$STAGE/usr/share/doc/$PKG_NAME"
|
||||||
|
cat > "$STAGE/usr/share/doc/$PKG_NAME/copyright" <<COPY
|
||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: $PKG_NAME
|
||||||
|
Upstream-Contact: $PKG_MAINTAINER
|
||||||
|
Source: $PKG_HOMEPAGE
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2024-2026 Siavash Sameni
|
||||||
|
License: MIT AND Apache-2.0
|
||||||
|
COPY
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Calculate installed size (in KiB, as Debian policy requires)
|
||||||
|
###############################################################################
|
||||||
|
INSTALLED_SIZE=$(du -sk "$STAGE" | cut -f1)
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# DEBIAN/control
|
||||||
|
###############################################################################
|
||||||
|
install -d "$STAGE/DEBIAN"
|
||||||
|
cat > "$STAGE/DEBIAN/control" <<CTRL
|
||||||
|
Package: $PKG_NAME
|
||||||
|
Version: $PKG_VERSION
|
||||||
|
Architecture: $PKG_ARCH
|
||||||
|
Maintainer: $PKG_MAINTAINER
|
||||||
|
Installed-Size: $INSTALLED_SIZE
|
||||||
|
Section: $PKG_SECTION
|
||||||
|
Priority: $PKG_PRIORITY
|
||||||
|
Homepage: $PKG_HOMEPAGE
|
||||||
|
Description: $PKG_DESCRIPTION
|
||||||
|
A high-performance Rust implementation of the MikroTik Bandwidth Test
|
||||||
|
protocol, supporting both server and client modes with EC-SRP5
|
||||||
|
authentication. Supports TCP/UDP throughput testing and is fully
|
||||||
|
compatible with RouterOS btest clients.
|
||||||
|
CTRL
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# DEBIAN/conffiles (mark the systemd unit as a conffile)
|
||||||
|
###############################################################################
|
||||||
|
cat > "$STAGE/DEBIAN/conffiles" <<'CF'
|
||||||
|
/usr/lib/systemd/system/btest.service
|
||||||
|
CF
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Maintainer scripts
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# postinst -- reload systemd after install
|
||||||
|
cat > "$STAGE/DEBIAN/postinst" <<'POST'
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
if [ "$1" = "configure" ]; then
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
systemctl daemon-reload || true
|
||||||
|
echo ""
|
||||||
|
echo "btest-rs installed. To start the server:"
|
||||||
|
echo " sudo systemctl enable --now btest.service"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
POST
|
||||||
|
chmod 755 "$STAGE/DEBIAN/postinst"
|
||||||
|
|
||||||
|
# prerm -- stop service before removal
|
||||||
|
cat > "$STAGE/DEBIAN/prerm" <<'PRERM'
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
if [ "$1" = "remove" ] || [ "$1" = "deconfigure" ]; then
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
systemctl stop btest.service 2>/dev/null || true
|
||||||
|
systemctl disable btest.service 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
PRERM
|
||||||
|
chmod 755 "$STAGE/DEBIAN/prerm"
|
||||||
|
|
||||||
|
# postrm -- clean up after removal
|
||||||
|
cat > "$STAGE/DEBIAN/postrm" <<'POSTRM'
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
if [ "$1" = "purge" ] || [ "$1" = "remove" ]; then
|
||||||
|
if command -v systemctl >/dev/null 2>&1; then
|
||||||
|
systemctl daemon-reload || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
POSTRM
|
||||||
|
chmod 755 "$STAGE/DEBIAN/postrm"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Build .deb
|
||||||
|
###############################################################################
|
||||||
|
OUTPUT_DIR="${OUTPUT_DIR:-$REPO_ROOT/dist}"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
echo "==> Building $DEB_FILE ..."
|
||||||
|
dpkg-deb --root-owner-group --build "$STAGE" "$OUTPUT_DIR/$DEB_FILE"
|
||||||
|
|
||||||
|
echo "==> Package ready: $OUTPUT_DIR/$DEB_FILE"
|
||||||
|
echo ""
|
||||||
|
dpkg-deb --info "$OUTPUT_DIR/$DEB_FILE"
|
||||||
|
echo ""
|
||||||
|
dpkg-deb --contents "$OUTPUT_DIR/$DEB_FILE"
|
||||||
104
deploy/deb/test-deb.sh
Executable file
104
deploy/deb/test-deb.sh
Executable file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# test-deb.sh -- Smoke-test a btest-rs .deb inside an Ubuntu Docker container
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./deploy/deb/test-deb.sh # auto-finds dist/*.deb
|
||||||
|
# ./deploy/deb/test-deb.sh path/to/btest-rs_*.deb
|
||||||
|
#
|
||||||
|
# Requirements: docker
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
IMAGE="${TEST_IMAGE:-ubuntu:24.04}"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Locate the .deb
|
||||||
|
###############################################################################
|
||||||
|
if [[ -n "${1:-}" ]]; then
|
||||||
|
DEB_PATH="$1"
|
||||||
|
else
|
||||||
|
DEB_PATH="$(ls -1t "$REPO_ROOT"/dist/btest-rs_*.deb 2>/dev/null | head -1 || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$DEB_PATH" || ! -f "$DEB_PATH" ]]; then
|
||||||
|
echo "Error: no .deb file found."
|
||||||
|
echo " Build first: ./deploy/deb/build-deb.sh"
|
||||||
|
echo " Or pass path: $0 path/to/btest-rs_*.deb"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEB_FILE="$(basename "$DEB_PATH")"
|
||||||
|
DEB_DIR="$(cd "$(dirname "$DEB_PATH")" && pwd)"
|
||||||
|
|
||||||
|
echo "==> Testing $DEB_FILE in $IMAGE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Run tests inside a disposable container
|
||||||
|
###############################################################################
|
||||||
|
docker run --rm \
|
||||||
|
-v "$DEB_DIR/$DEB_FILE:/tmp/$DEB_FILE:ro" \
|
||||||
|
"$IMAGE" \
|
||||||
|
bash -euxc "
|
||||||
|
###################################################################
|
||||||
|
# 1. Install the .deb
|
||||||
|
###################################################################
|
||||||
|
apt-get update -qq
|
||||||
|
dpkg -i /tmp/$DEB_FILE || apt-get install -f -y # resolve deps if any
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
# 2. Verify files are in place
|
||||||
|
###################################################################
|
||||||
|
echo '--- Checking installed files ---'
|
||||||
|
test -x /usr/bin/btest
|
||||||
|
test -f /usr/lib/systemd/system/btest.service
|
||||||
|
test -f /usr/share/doc/btest-rs/README.md
|
||||||
|
test -f /usr/share/licenses/btest-rs/LICENSE
|
||||||
|
|
||||||
|
# Man page (may be gzipped)
|
||||||
|
test -f /usr/share/man/man1/btest.1.gz || test -f /usr/share/man/man1/btest.1
|
||||||
|
echo 'All expected files present.'
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
# 3. btest --version
|
||||||
|
###################################################################
|
||||||
|
echo ''
|
||||||
|
echo '--- btest --version ---'
|
||||||
|
btest --version
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
# 4. Quick loopback server+client test
|
||||||
|
###################################################################
|
||||||
|
echo ''
|
||||||
|
echo '--- Loopback smoke test ---'
|
||||||
|
|
||||||
|
# Start server in background
|
||||||
|
btest -s &
|
||||||
|
SERVER_PID=\$!
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Run a short TCP test against localhost
|
||||||
|
if btest -c 127.0.0.1 -d 2 2>&1; then
|
||||||
|
echo 'Loopback TCP test passed.'
|
||||||
|
else
|
||||||
|
echo 'Warning: loopback test returned non-zero (may be expected in container).'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tear down
|
||||||
|
kill \$SERVER_PID 2>/dev/null || true
|
||||||
|
wait \$SERVER_PID 2>/dev/null || true
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
# 5. Package metadata sanity
|
||||||
|
###################################################################
|
||||||
|
echo ''
|
||||||
|
echo '--- dpkg metadata ---'
|
||||||
|
dpkg -s btest-rs | head -20
|
||||||
|
|
||||||
|
echo ''
|
||||||
|
echo '=== All tests passed ==='
|
||||||
|
"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> .deb smoke test completed successfully."
|
||||||
57
deploy/openwrt/Makefile
Normal file
57
deploy/openwrt/Makefile
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# OpenWrt package Makefile for btest-rs
|
||||||
|
#
|
||||||
|
# To build:
|
||||||
|
# 1. Clone the OpenWrt SDK for your target
|
||||||
|
# 2. Copy this directory to package/btest-rs/ in the SDK
|
||||||
|
# 3. Run: make package/btest-rs/compile V=s
|
||||||
|
#
|
||||||
|
# Or use the pre-built binary approach (see build-ipk.sh)
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=btest-rs
|
||||||
|
PKG_VERSION:=0.6.0
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
|
PKG_SOURCE_URL:=https://github.com/manawenuz/btest-rs/archive/refs/tags/v$(PKG_VERSION).tar.gz
|
||||||
|
PKG_HASH:=skip
|
||||||
|
|
||||||
|
PKG_BUILD_DEPENDS:=rust/host
|
||||||
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/btest-rs
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
TITLE:=MikroTik Bandwidth Test server and client
|
||||||
|
URL:=https://github.com/manawenuz/btest-rs
|
||||||
|
DEPENDS:=
|
||||||
|
PKGARCH:=$(ARCH)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/btest-rs/description
|
||||||
|
A Rust reimplementation of the MikroTik Bandwidth Test (btest) protocol.
|
||||||
|
Supports TCP/UDP, IPv4/IPv6, EC-SRP5 and MD5 authentication,
|
||||||
|
multi-connection, syslog, CSV output, and CPU monitoring.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
cd $(PKG_BUILD_DIR) && \
|
||||||
|
CARGO_TARGET_DIR=$(PKG_BUILD_DIR)/target \
|
||||||
|
cargo build --release --target $(RUSTC_TARGET)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/btest-rs/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET)/release/btest $(1)/usr/bin/btest
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/btest.init $(1)/etc/init.d/btest
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_CONF) ./files/btest.config $(1)/etc/config/btest
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,btest-rs))
|
||||||
117
deploy/openwrt/build-ipk.sh
Executable file
117
deploy/openwrt/build-ipk.sh
Executable file
@@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build an OpenWrt .ipk package from a pre-built static binary.
|
||||||
|
# No OpenWrt SDK needed — just packages the binary with metadata.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./deploy/openwrt/build-ipk.sh <arch> [binary-path]
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# ./deploy/openwrt/build-ipk.sh x86_64 dist/btest # from cross-compiled binary
|
||||||
|
# ./deploy/openwrt/build-ipk.sh aarch64 dist/btest # for RPi/ARM64 routers
|
||||||
|
# ./deploy/openwrt/build-ipk.sh mipsel target/release/btest # for MIPS little-endian
|
||||||
|
#
|
||||||
|
# Supported architectures: x86_64, aarch64, arm_cortex-a7, mipsel_24kc, mips_24kc
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/../.."
|
||||||
|
|
||||||
|
ARCH="${1:?Usage: $0 <arch> [binary-path]}"
|
||||||
|
BINARY="${2:-dist/btest}"
|
||||||
|
VERSION="0.6.0"
|
||||||
|
PKG_NAME="btest-rs"
|
||||||
|
OUTPUT_DIR="dist"
|
||||||
|
|
||||||
|
if [ ! -f "$BINARY" ]; then
|
||||||
|
echo "Error: binary not found at $BINARY"
|
||||||
|
echo "Build it first: cargo build --release --target <target>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
WORKDIR=$(mktemp -d)
|
||||||
|
trap "rm -rf $WORKDIR" EXIT
|
||||||
|
|
||||||
|
echo "=== Building ${PKG_NAME}_${VERSION}_${ARCH}.ipk ==="
|
||||||
|
|
||||||
|
# Create package structure
|
||||||
|
mkdir -p "$WORKDIR/data/usr/bin"
|
||||||
|
mkdir -p "$WORKDIR/data/etc/init.d"
|
||||||
|
mkdir -p "$WORKDIR/data/etc/config"
|
||||||
|
mkdir -p "$WORKDIR/control"
|
||||||
|
|
||||||
|
# Install files
|
||||||
|
cp "$BINARY" "$WORKDIR/data/usr/bin/btest"
|
||||||
|
chmod 755 "$WORKDIR/data/usr/bin/btest"
|
||||||
|
cp deploy/openwrt/files/btest.init "$WORKDIR/data/etc/init.d/btest"
|
||||||
|
chmod 755 "$WORKDIR/data/etc/init.d/btest"
|
||||||
|
cp deploy/openwrt/files/btest.config "$WORKDIR/data/etc/config/btest"
|
||||||
|
|
||||||
|
# Calculate installed size
|
||||||
|
INSTALLED_SIZE=$(du -sk "$WORKDIR/data" | awk '{print $1}')
|
||||||
|
|
||||||
|
# Control file
|
||||||
|
cat > "$WORKDIR/control/control" << EOF
|
||||||
|
Package: ${PKG_NAME}
|
||||||
|
Version: ${VERSION}-1
|
||||||
|
Depends: libc
|
||||||
|
Source: https://github.com/manawenuz/btest-rs
|
||||||
|
License: MIT AND Apache-2.0
|
||||||
|
Section: net
|
||||||
|
SourceName: ${PKG_NAME}
|
||||||
|
Maintainer: Siavash Sameni <manwe@manko.yoga>
|
||||||
|
Architecture: ${ARCH}
|
||||||
|
Installed-Size: ${INSTALLED_SIZE}
|
||||||
|
Description: MikroTik Bandwidth Test server and client
|
||||||
|
A Rust reimplementation of the MikroTik btest protocol.
|
||||||
|
Supports TCP/UDP, EC-SRP5 and MD5 auth, IPv4/IPv6.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Post-install script
|
||||||
|
cat > "$WORKDIR/control/postinst" << 'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
[ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0
|
||||||
|
/etc/init.d/btest enable 2>/dev/null || true
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod 755 "$WORKDIR/control/postinst"
|
||||||
|
|
||||||
|
# Pre-remove script
|
||||||
|
cat > "$WORKDIR/control/prerm" << 'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
/etc/init.d/btest stop 2>/dev/null || true
|
||||||
|
/etc/init.d/btest disable 2>/dev/null || true
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
chmod 755 "$WORKDIR/control/prerm"
|
||||||
|
|
||||||
|
# Conffiles
|
||||||
|
cat > "$WORKDIR/control/conffiles" << EOF
|
||||||
|
/etc/config/btest
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Build the .ipk (it's just a tar.gz of tar.gz's)
|
||||||
|
cd "$WORKDIR"
|
||||||
|
|
||||||
|
# Create data.tar.gz
|
||||||
|
(cd data && tar czf ../data.tar.gz .)
|
||||||
|
|
||||||
|
# Create control.tar.gz
|
||||||
|
(cd control && tar czf ../control.tar.gz .)
|
||||||
|
|
||||||
|
# Create debian-binary
|
||||||
|
echo "2.0" > debian-binary
|
||||||
|
|
||||||
|
# Package it all
|
||||||
|
tar czf "${PKG_NAME}_${VERSION}-1_${ARCH}.ipk" debian-binary control.tar.gz data.tar.gz
|
||||||
|
|
||||||
|
cd -
|
||||||
|
cp "$WORKDIR/${PKG_NAME}_${VERSION}-1_${ARCH}.ipk" "$OUTPUT_DIR/"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Package: $OUTPUT_DIR/${PKG_NAME}_${VERSION}-1_${ARCH}.ipk"
|
||||||
|
ls -lh "$OUTPUT_DIR/${PKG_NAME}_${VERSION}-1_${ARCH}.ipk"
|
||||||
|
echo ""
|
||||||
|
echo "Install on OpenWrt:"
|
||||||
|
echo " scp $OUTPUT_DIR/${PKG_NAME}_${VERSION}-1_${ARCH}.ipk root@router:/tmp/"
|
||||||
|
echo " ssh root@router 'opkg install /tmp/${PKG_NAME}_${VERSION}-1_${ARCH}.ipk'"
|
||||||
|
echo " ssh root@router '/etc/init.d/btest enable && /etc/init.d/btest start'"
|
||||||
7
deploy/openwrt/files/btest.config
Normal file
7
deploy/openwrt/files/btest.config
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
config server
|
||||||
|
option enabled '0'
|
||||||
|
option port '2000'
|
||||||
|
option auth_user ''
|
||||||
|
option auth_pass ''
|
||||||
|
option ecsrp5 '0'
|
||||||
|
option syslog ''
|
||||||
34
deploy/openwrt/files/btest.init
Executable file
34
deploy/openwrt/files/btest.init
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# btest-rs OpenWrt init script
|
||||||
|
|
||||||
|
START=90
|
||||||
|
STOP=10
|
||||||
|
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local enabled port auth_user auth_pass ecsrp5 syslog
|
||||||
|
|
||||||
|
config_load btest
|
||||||
|
config_get_bool enabled server enabled 0
|
||||||
|
[ "$enabled" -eq 0 ] && return
|
||||||
|
|
||||||
|
config_get port server port 2000
|
||||||
|
config_get auth_user server auth_user ''
|
||||||
|
config_get auth_pass server auth_pass ''
|
||||||
|
config_get_bool ecsrp5 server ecsrp5 0
|
||||||
|
config_get syslog server syslog ''
|
||||||
|
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param command /usr/bin/btest -s -P "$port"
|
||||||
|
|
||||||
|
[ -n "$auth_user" ] && procd_append_param command -a "$auth_user"
|
||||||
|
[ -n "$auth_pass" ] && procd_append_param command -p "$auth_pass"
|
||||||
|
[ "$ecsrp5" -eq 1 ] && procd_append_param command --ecsrp5
|
||||||
|
[ -n "$syslog" ] && procd_append_param command --syslog "$syslog"
|
||||||
|
|
||||||
|
procd_set_param respawn
|
||||||
|
procd_set_param stdout 1
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
73
deploy/rpm/btest-rs.spec
Normal file
73
deploy/rpm/btest-rs.spec
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
Name: btest-rs
|
||||||
|
Version: 0.6.0
|
||||||
|
Release: 1%{?dist}
|
||||||
|
Summary: MikroTik Bandwidth Test (btest) server and client with EC-SRP5 auth
|
||||||
|
|
||||||
|
License: MIT AND Apache-2.0
|
||||||
|
URL: https://github.com/manawenuz/btest-rs
|
||||||
|
Source0: https://github.com/manawenuz/btest-rs/archive/refs/tags/v%{version}.tar.gz
|
||||||
|
|
||||||
|
BuildRequires: cargo
|
||||||
|
BuildRequires: rust
|
||||||
|
ExclusiveArch: x86_64 aarch64
|
||||||
|
|
||||||
|
%description
|
||||||
|
A Rust reimplementation of the MikroTik Bandwidth Test (btest) protocol,
|
||||||
|
providing both server and client functionality with EC-SRP5 authentication.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%autosetup -n %{name}-%{version}
|
||||||
|
|
||||||
|
%build
|
||||||
|
export CARGO_TARGET_DIR=target
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -Dm755 target/release/btest %{buildroot}%{_bindir}/btest
|
||||||
|
install -Dm644 docs/man/btest.1 %{buildroot}%{_mandir}/man1/btest.1
|
||||||
|
install -Dm644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE
|
||||||
|
|
||||||
|
# systemd service unit
|
||||||
|
install -d %{buildroot}%{_unitdir}
|
||||||
|
cat > %{buildroot}%{_unitdir}/btest.service << 'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=MikroTik Bandwidth Test Server (btest-rs)
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/bin/btest -s
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
DynamicUser=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||||
|
LimitNOFILE=65535
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
%{_bindir}/btest
|
||||||
|
%{_mandir}/man1/btest.1*
|
||||||
|
%{_unitdir}/btest.service
|
||||||
|
|
||||||
|
%post
|
||||||
|
%systemd_post btest.service
|
||||||
|
|
||||||
|
%preun
|
||||||
|
%systemd_preun btest.service
|
||||||
|
|
||||||
|
%postun
|
||||||
|
%systemd_postun_with_restart btest.service
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
* Mon Mar 30 2026 Siavash Sameni <manwe@manko.yoga> - 0.6.0-1
|
||||||
|
- Initial RPM package
|
||||||
30
deploy/rpm/build-rpm.sh
Executable file
30
deploy/rpm/build-rpm.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# build-rpm.sh — Build the btest-rs RPM package
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SPEC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
SPEC_FILE="${SPEC_DIR}/btest-rs.spec"
|
||||||
|
VERSION="0.6.0"
|
||||||
|
TARBALL="v${VERSION}.tar.gz"
|
||||||
|
SOURCE_URL="https://github.com/manawenuz/btest-rs/archive/refs/tags/${TARBALL}"
|
||||||
|
|
||||||
|
echo "==> Setting up rpmbuild tree"
|
||||||
|
mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
|
||||||
|
|
||||||
|
echo "==> Downloading source tarball"
|
||||||
|
if [ ! -f ~/rpmbuild/SOURCES/"${TARBALL}" ]; then
|
||||||
|
curl -fSL -o ~/rpmbuild/SOURCES/"${TARBALL}" "${SOURCE_URL}"
|
||||||
|
else
|
||||||
|
echo " (already present, skipping download)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Copying spec file"
|
||||||
|
cp "${SPEC_FILE}" ~/rpmbuild/SPECS/btest-rs.spec
|
||||||
|
|
||||||
|
echo "==> Building RPM"
|
||||||
|
rpmbuild -ba ~/rpmbuild/SPECS/btest-rs.spec
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> Build complete. Packages:"
|
||||||
|
find ~/rpmbuild/RPMS -name '*.rpm' -print
|
||||||
|
find ~/rpmbuild/SRPMS -name '*.rpm' -print
|
||||||
75
deploy/rpm/test-rpm.sh
Executable file
75
deploy/rpm/test-rpm.sh
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# test-rpm.sh — Test the btest-rs RPM build inside a Fedora container
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||||
|
|
||||||
|
IMAGE="fedora:latest"
|
||||||
|
|
||||||
|
echo "==> Testing RPM build in ${IMAGE}"
|
||||||
|
docker run --rm \
|
||||||
|
-v "${REPO_ROOT}:/workspace:ro" \
|
||||||
|
"${IMAGE}" \
|
||||||
|
bash -euxc '
|
||||||
|
# ── Install build dependencies ──
|
||||||
|
dnf install -y rpm-build rpmdevtools curl gcc make \
|
||||||
|
systemd-rpm-macros
|
||||||
|
|
||||||
|
# Install Rust toolchain
|
||||||
|
curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs \
|
||||||
|
| sh -s -- -y --profile minimal
|
||||||
|
source "$HOME/.cargo/env"
|
||||||
|
|
||||||
|
# ── Set up rpmbuild tree ──
|
||||||
|
rpmdev-setuptree
|
||||||
|
|
||||||
|
VERSION="0.6.0"
|
||||||
|
TARBALL="v${VERSION}.tar.gz"
|
||||||
|
|
||||||
|
# Copy spec
|
||||||
|
cp /workspace/deploy/rpm/btest-rs.spec ~/rpmbuild/SPECS/
|
||||||
|
|
||||||
|
# Create source tarball from workspace
|
||||||
|
# rpmbuild expects btest-rs-VERSION/ top-level directory
|
||||||
|
mkdir -p /tmp/btest-rs-${VERSION}
|
||||||
|
cp -a /workspace/. /tmp/btest-rs-${VERSION}/
|
||||||
|
tar czf ~/rpmbuild/SOURCES/${TARBALL} -C /tmp btest-rs-${VERSION}
|
||||||
|
|
||||||
|
# ── Build RPM ──
|
||||||
|
rpmbuild -ba ~/rpmbuild/SPECS/btest-rs.spec
|
||||||
|
|
||||||
|
# ── Install the RPM ──
|
||||||
|
RPM=$(find ~/rpmbuild/RPMS -name "btest-rs-*.rpm" | head -1)
|
||||||
|
echo "Installing: ${RPM}"
|
||||||
|
dnf install -y "${RPM}"
|
||||||
|
|
||||||
|
# ── Verify installation ──
|
||||||
|
echo "--- btest --version ---"
|
||||||
|
btest --version
|
||||||
|
|
||||||
|
echo "--- Checking systemd unit ---"
|
||||||
|
systemctl cat btest.service || true
|
||||||
|
|
||||||
|
echo "--- Checking man page ---"
|
||||||
|
test -f /usr/share/man/man1/btest.1* && echo "man page OK" || echo "man page MISSING"
|
||||||
|
|
||||||
|
echo "--- Checking license ---"
|
||||||
|
test -f /usr/share/licenses/btest-rs/LICENSE && echo "license OK" || echo "license MISSING"
|
||||||
|
|
||||||
|
# ── Loopback bandwidth test ──
|
||||||
|
echo "--- Starting loopback test ---"
|
||||||
|
btest -s &
|
||||||
|
SERVER_PID=$!
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
btest -c 127.0.0.1 --duration 3 && echo "Loopback test PASSED" \
|
||||||
|
|| echo "Loopback test FAILED (exit $?)"
|
||||||
|
|
||||||
|
kill "${SERVER_PID}" 2>/dev/null || true
|
||||||
|
wait "${SERVER_PID}" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "==> All RPM tests completed."
|
||||||
|
'
|
||||||
|
|
||||||
|
echo "==> Fedora container test finished."
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
btest-server:
|
btest-server:
|
||||||
build: .
|
build: .
|
||||||
image: git.manko.yoga/manawenuz/btest-rs:latest
|
image: ghcr.io/manawenuz/btest-rs:latest
|
||||||
container_name: btest-server
|
container_name: btest-server
|
||||||
ports:
|
ports:
|
||||||
- "2000:2000/tcp"
|
- "2000:2000/tcp"
|
||||||
@@ -13,7 +13,7 @@ services:
|
|||||||
# Server with authentication enabled
|
# Server with authentication enabled
|
||||||
btest-server-auth:
|
btest-server-auth:
|
||||||
build: .
|
build: .
|
||||||
image: git.manko.yoga/manawenuz/btest-rs:latest
|
image: ghcr.io/manawenuz/btest-rs:latest
|
||||||
container_name: btest-server-auth
|
container_name: btest-server-auth
|
||||||
ports:
|
ports:
|
||||||
- "2010:2000/tcp"
|
- "2010:2000/tcp"
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
# Docker and Deployment Guide
|
# Docker and Deployment Guide
|
||||||
|
|
||||||
## Container Registry
|
## Container Registries
|
||||||
|
|
||||||
Images are published to:
|
Images are published to:
|
||||||
|
|
||||||
```
|
```
|
||||||
git.manko.yoga/manawenuz/btest-rs
|
git.manko.yoga/manawenuz/btest-rs # Gitea registry
|
||||||
|
ghcr.io/manawenuz/btest-rs # GitHub Container Registry
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@@ -87,14 +88,14 @@ docker run --rm -it btest-rs -c 192.168.88.1 -r -a admin -p password
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Pull from Gitea registry
|
# Pull from Gitea registry
|
||||||
docker pull git.manko.yoga/manawenuz/btest-rs:latest
|
docker pull ghcr.io/manawenuz/btest-rs:latest
|
||||||
|
|
||||||
# Run server
|
# Run server
|
||||||
docker run --rm -it \
|
docker run --rm -it \
|
||||||
-p 2000:2000/tcp \
|
-p 2000:2000/tcp \
|
||||||
-p 2001-2100:2001-2100/udp \
|
-p 2001-2100:2001-2100/udp \
|
||||||
-p 2257-2356:2257-2356/udp \
|
-p 2257-2356:2257-2356/udp \
|
||||||
git.manko.yoga/manawenuz/btest-rs:latest -s -v
|
ghcr.io/manawenuz/btest-rs:latest -s -v
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Compose
|
## Docker Compose
|
||||||
@@ -185,7 +186,7 @@ docker build -t btest-rs .
|
|||||||
|
|
||||||
# With custom tag
|
# With custom tag
|
||||||
docker build -t git.manko.yoga/manawenuz/btest-rs:latest .
|
docker build -t git.manko.yoga/manawenuz/btest-rs:latest .
|
||||||
docker build -t git.manko.yoga/manawenuz/btest-rs:0.5.0 .
|
docker build -t git.manko.yoga/manawenuz/btest-rs:0.6.0 .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multi-platform build
|
### Multi-platform build
|
||||||
@@ -193,7 +194,7 @@ docker build -t git.manko.yoga/manawenuz/btest-rs:0.5.0 .
|
|||||||
```bash
|
```bash
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
--platform linux/amd64,linux/arm64 \
|
--platform linux/amd64,linux/arm64 \
|
||||||
-t git.manko.yoga/manawenuz/btest-rs:latest \
|
-t ghcr.io/manawenuz/btest-rs:latest \
|
||||||
--push .
|
--push .
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -208,9 +209,9 @@ docker build -t git.manko.yoga/manawenuz/btest-rs:latest .
|
|||||||
docker push git.manko.yoga/manawenuz/btest-rs:latest
|
docker push git.manko.yoga/manawenuz/btest-rs:latest
|
||||||
|
|
||||||
# Also tag with version
|
# Also tag with version
|
||||||
docker tag git.manko.yoga/manawenuz/btest-rs:latest \
|
docker tag ghcr.io/manawenuz/btest-rs:latest \
|
||||||
git.manko.yoga/manawenuz/btest-rs:0.5.0
|
git.manko.yoga/manawenuz/btest-rs:0.6.0
|
||||||
docker push git.manko.yoga/manawenuz/btest-rs:0.5.0
|
docker push git.manko.yoga/manawenuz/btest-rs:0.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deployment Options
|
## Deployment Options
|
||||||
@@ -223,7 +224,7 @@ docker run -d --name btest-server \
|
|||||||
-p 2000:2000/tcp \
|
-p 2000:2000/tcp \
|
||||||
-p 2001-2100:2001-2100/udp \
|
-p 2001-2100:2001-2100/udp \
|
||||||
-p 2257-2356:2257-2356/udp \
|
-p 2257-2356:2257-2356/udp \
|
||||||
git.manko.yoga/manawenuz/btest-rs:latest \
|
ghcr.io/manawenuz/btest-rs:latest \
|
||||||
-s -a admin -p password --ecsrp5 -v
|
-s -a admin -p password --ecsrp5 -v
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
50
scripts/push-docker-all.sh
Executable file
50
scripts/push-docker-all.sh
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build and push Docker image to both Gitea and GitHub Container Registry.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# docker login git.manko.yoga (Gitea — your username + token)
|
||||||
|
# docker login ghcr.io (GitHub — your username + PAT with packages:write)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/push-docker-all.sh v0.6.0
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
if [[ -f .env ]]; then
|
||||||
|
set -a; source .env; set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAG="${1:?Usage: $0 <tag> (e.g. v0.6.0)}"
|
||||||
|
|
||||||
|
GITEA_IMAGE="git.manko.yoga/manawenuz/btest-rs"
|
||||||
|
GHCR_IMAGE="ghcr.io/manawenuz/btest-rs"
|
||||||
|
|
||||||
|
echo "=== Building Docker image ==="
|
||||||
|
docker build \
|
||||||
|
-t "${GITEA_IMAGE}:${TAG}" \
|
||||||
|
-t "${GITEA_IMAGE}:latest" \
|
||||||
|
-t "${GHCR_IMAGE}:${TAG}" \
|
||||||
|
-t "${GHCR_IMAGE}:latest" \
|
||||||
|
.
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Pushing to Gitea ==="
|
||||||
|
docker push "${GITEA_IMAGE}:${TAG}"
|
||||||
|
docker push "${GITEA_IMAGE}:latest"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Pushing to GitHub Container Registry ==="
|
||||||
|
docker push "${GHCR_IMAGE}:${TAG}"
|
||||||
|
docker push "${GHCR_IMAGE}:latest"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done! Images pushed:"
|
||||||
|
echo " ${GITEA_IMAGE}:${TAG}"
|
||||||
|
echo " ${GITEA_IMAGE}:latest"
|
||||||
|
echo " ${GHCR_IMAGE}:${TAG}"
|
||||||
|
echo " ${GHCR_IMAGE}:latest"
|
||||||
|
echo ""
|
||||||
|
echo "Pull with:"
|
||||||
|
echo " docker pull ${GHCR_IMAGE}:${TAG}"
|
||||||
|
echo " docker run --rm -p 2000:2000 -p 2001-2100:2001-2100/udp ${GHCR_IMAGE}:${TAG} -s -v"
|
||||||
120
scripts/sync-github-release.sh
Executable file
120
scripts/sync-github-release.sh
Executable file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Sync a release from Gitea to GitHub.
|
||||||
|
# Downloads all binaries from Gitea release, creates GitHub release, uploads them.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# gh auth login (GitHub CLI authenticated)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/sync-github-release.sh v0.6.0
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
if [[ -f .env ]]; then
|
||||||
|
set -a; source .env; set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAG="${1:?Usage: $0 <tag> (e.g. v0.6.0)}"
|
||||||
|
GITEA_URL="https://git.manko.yoga"
|
||||||
|
GITEA_REPO="manawenuz/btest-rs"
|
||||||
|
GITHUB_REPO="manawenuz/btest-rs"
|
||||||
|
|
||||||
|
echo "=== Downloading assets from Gitea release ${TAG} ==="
|
||||||
|
mkdir -p /tmp/btest-release-${TAG}
|
||||||
|
cd /tmp/btest-release-${TAG}
|
||||||
|
rm -f *.tar.gz *.zip *.txt
|
||||||
|
|
||||||
|
# Get asset list from Gitea API
|
||||||
|
ASSETS=$(curl -sf "${GITEA_URL}/api/v1/repos/${GITEA_REPO}/releases/tags/${TAG}" | \
|
||||||
|
python3 -c "import sys,json; [print(a['browser_download_url']) for a in json.load(sys.stdin).get('assets',[])]")
|
||||||
|
|
||||||
|
if [ -z "$ASSETS" ]; then
|
||||||
|
echo "No assets found for ${TAG} on Gitea. Check if the release exists."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for url in $ASSETS; do
|
||||||
|
FILENAME=$(basename "$url")
|
||||||
|
echo " Downloading: $FILENAME"
|
||||||
|
curl -sLO "$url"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Merge all separate .sha256 files into checksums-sha256.txt
|
||||||
|
# and remove the individual .sha256 files
|
||||||
|
echo ""
|
||||||
|
echo "=== Merging checksums ==="
|
||||||
|
for sha_file in *.sha256; do
|
||||||
|
[ -f "$sha_file" ] || continue
|
||||||
|
echo " Merging: $sha_file"
|
||||||
|
cat "$sha_file" >> checksums-sha256.txt
|
||||||
|
rm "$sha_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Add checksums for any files not yet in checksums-sha256.txt
|
||||||
|
for f in *.tar.gz *.zip; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
if ! grep -q "$f" checksums-sha256.txt 2>/dev/null; then
|
||||||
|
echo " Adding checksum for: $f"
|
||||||
|
shasum -a 256 "$f" >> checksums-sha256.txt
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Sort and deduplicate
|
||||||
|
sort -u -k2 checksums-sha256.txt > checksums-sha256.tmp && mv checksums-sha256.tmp checksums-sha256.txt
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Checksums:"
|
||||||
|
cat checksums-sha256.txt
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Files to upload:"
|
||||||
|
ls -lh *.tar.gz *.zip checksums-sha256.txt 2>/dev/null
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Creating GitHub release ${TAG} ==="
|
||||||
|
gh release create "${TAG}" \
|
||||||
|
--repo "${GITHUB_REPO}" \
|
||||||
|
--title "btest-rs ${TAG}" \
|
||||||
|
--notes "## Downloads
|
||||||
|
|
||||||
|
| Platform | Architecture | File |
|
||||||
|
|----------|-------------|------|
|
||||||
|
| Linux | x86_64 | btest-linux-x86_64.tar.gz |
|
||||||
|
| Linux | aarch64 (RPi 64-bit) | btest-linux-aarch64.tar.gz |
|
||||||
|
| Linux | armv7 (RPi 32-bit) | btest-linux-armv7.tar.gz |
|
||||||
|
| Windows | x86_64 | btest-windows-x86_64.zip |
|
||||||
|
| macOS | aarch64 (Apple Silicon) | btest-darwin-aarch64.tar.gz |
|
||||||
|
| Docker | x86_64 | \`docker pull ghcr.io/manawenuz/btest-rs:${TAG}\` |
|
||||||
|
|
||||||
|
### Quick Install (Linux)
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
curl -LO https://github.com/${GITHUB_REPO}/releases/download/${TAG}/btest-linux-x86_64.tar.gz
|
||||||
|
tar xzf btest-linux-x86_64.tar.gz
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### Raspberry Pi
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
# 64-bit
|
||||||
|
curl -LO https://github.com/${GITHUB_REPO}/releases/download/${TAG}/btest-linux-aarch64.tar.gz
|
||||||
|
tar xzf btest-linux-aarch64.tar.gz
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
|
||||||
|
# 32-bit
|
||||||
|
curl -LO https://github.com/${GITHUB_REPO}/releases/download/${TAG}/btest-linux-armv7.tar.gz
|
||||||
|
tar xzf btest-linux-armv7.tar.gz
|
||||||
|
sudo mv btest /usr/local/bin/
|
||||||
|
\`\`\`
|
||||||
|
" \
|
||||||
|
./*.tar.gz ./*.zip ./*.txt 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Done! ==="
|
||||||
|
echo "https://github.com/${GITHUB_REPO}/releases/tag/${TAG}"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
cd -
|
||||||
|
rm -rf /tmp/btest-release-${TAG}
|
||||||
64
scripts/test-aur-remote.sh
Executable file
64
scripts/test-aur-remote.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Test the AUR package on a remote x86_64 Linux server using Docker.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/test-aur-remote.sh [user@host]
|
||||||
|
#
|
||||||
|
# Spins up an Arch container, installs btest-rs via yay (like a real user),
|
||||||
|
# runs loopback tests, cleans up.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REMOTE="${1:-}"
|
||||||
|
|
||||||
|
TEST_SCRIPT='
|
||||||
|
docker run --rm archlinux:latest bash -c "
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
echo \"[1/4] Installing yay...\"
|
||||||
|
pacman -Syu --noconfirm base-devel git sudo >/dev/null 2>&1
|
||||||
|
useradd -m builder
|
||||||
|
echo \"builder ALL=(ALL) NOPASSWD: ALL\" >> /etc/sudoers
|
||||||
|
su builder -c \"
|
||||||
|
cd /tmp
|
||||||
|
git clone https://aur.archlinux.org/yay-bin.git 2>/dev/null
|
||||||
|
cd yay-bin
|
||||||
|
makepkg -si --noconfirm 2>&1 | tail -3
|
||||||
|
\"
|
||||||
|
|
||||||
|
echo \"[2/4] Installing btest-rs from AUR via yay...\"
|
||||||
|
su builder -c \"yay -S btest-rs --noconfirm 2>&1 | tail -10\"
|
||||||
|
|
||||||
|
echo \"\"
|
||||||
|
echo \"[3/4] Verify installation...\"
|
||||||
|
btest --version
|
||||||
|
which btest
|
||||||
|
man -w btest.1 2>/dev/null && echo \"Man page: installed\" || echo \"Man page: not found\"
|
||||||
|
systemctl cat btest.service 2>/dev/null | head -3 && echo \"Systemd unit: installed\" || echo \"Systemd unit: not found\"
|
||||||
|
|
||||||
|
echo \"\"
|
||||||
|
echo \"[4/4] Loopback tests...\"
|
||||||
|
|
||||||
|
echo \"--- TCP (3s) ---\"
|
||||||
|
btest -s -P 19876 &
|
||||||
|
sleep 2
|
||||||
|
btest -c 127.0.0.1 -P 19876 -r -d 3
|
||||||
|
kill %1 2>/dev/null; wait 2>/dev/null || true
|
||||||
|
|
||||||
|
echo \"--- UDP (3s) ---\"
|
||||||
|
btest -s -P 19877 &
|
||||||
|
sleep 2
|
||||||
|
btest -c 127.0.0.1 -P 19877 -r -u -d 3
|
||||||
|
kill %1 2>/dev/null; wait 2>/dev/null || true
|
||||||
|
|
||||||
|
echo \"\"
|
||||||
|
echo \"=== ALL TESTS PASSED ===\"
|
||||||
|
"
|
||||||
|
'
|
||||||
|
|
||||||
|
if [ -n "$REMOTE" ]; then
|
||||||
|
echo "=== Testing AUR package on $REMOTE ==="
|
||||||
|
ssh "$REMOTE" "$TEST_SCRIPT"
|
||||||
|
else
|
||||||
|
echo "=== Testing AUR package locally ==="
|
||||||
|
eval "$TEST_SCRIPT"
|
||||||
|
fi
|
||||||
@@ -80,6 +80,34 @@ pub fn calc_send_interval(tx_speed_bps: u32, tx_size: u16) -> Option<Duration> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Advance `next_send` by one interval and clamp drift.
|
||||||
|
///
|
||||||
|
/// When the sender falls behind (e.g., the write blocked longer than the
|
||||||
|
/// inter-packet interval), `next_send` accumulates a debt. Once the path
|
||||||
|
/// clears, the loop would fire packets with *no* delay until the debt is
|
||||||
|
/// repaid, producing a burst that overshoots the target rate.
|
||||||
|
///
|
||||||
|
/// This helper resets `next_send` to `now` whenever it has drifted more
|
||||||
|
/// than 2x the interval behind the current wall-clock time, bounding the
|
||||||
|
/// maximum burst to at most one extra interval's worth of packets.
|
||||||
|
pub fn advance_next_send(
|
||||||
|
next_send: &mut std::time::Instant,
|
||||||
|
iv: Duration,
|
||||||
|
now: std::time::Instant,
|
||||||
|
) -> Option<Duration> {
|
||||||
|
*next_send += iv;
|
||||||
|
// If we have fallen more than 2x the interval behind, reset to now
|
||||||
|
// to prevent a compensating burst.
|
||||||
|
if *next_send + iv < now {
|
||||||
|
*next_send = now;
|
||||||
|
}
|
||||||
|
if *next_send > now {
|
||||||
|
Some(*next_send - now)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Format a bandwidth value in human-readable form.
|
/// Format a bandwidth value in human-readable form.
|
||||||
pub fn format_bandwidth(bits_per_sec: f64) -> String {
|
pub fn format_bandwidth(bits_per_sec: f64) -> String {
|
||||||
if bits_per_sec >= 1_000_000_000.0 {
|
if bits_per_sec >= 1_000_000_000.0 {
|
||||||
|
|||||||
127
src/bin/client_only.rs
Normal file
127
src/bin/client_only.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
//! btest-client: minimal bandwidth test client for embedded/OpenWrt systems.
|
||||||
|
//!
|
||||||
|
//! Stripped-down client that connects to MikroTik btest servers.
|
||||||
|
//! No server mode, no syslog, smaller binary footprint.
|
||||||
|
//!
|
||||||
|
//! Build: cargo build --profile release-small --bin btest-client
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(name = "btest-client", about = "MikroTik Bandwidth Test client", version)]
|
||||||
|
struct Cli {
|
||||||
|
/// Server address to connect to
|
||||||
|
#[arg(short = 'c', long = "client", required = true)]
|
||||||
|
host: String,
|
||||||
|
|
||||||
|
/// Transmit data (upload)
|
||||||
|
#[arg(short = 't', long = "transmit")]
|
||||||
|
transmit: bool,
|
||||||
|
|
||||||
|
/// Receive data (download)
|
||||||
|
#[arg(short = 'r', long = "receive")]
|
||||||
|
receive: bool,
|
||||||
|
|
||||||
|
/// Use UDP
|
||||||
|
#[arg(short = 'u', long = "udp")]
|
||||||
|
udp: bool,
|
||||||
|
|
||||||
|
/// Bandwidth limit (e.g., 100M)
|
||||||
|
#[arg(short = 'b', long = "bandwidth")]
|
||||||
|
bandwidth: Option<String>,
|
||||||
|
|
||||||
|
/// Port
|
||||||
|
#[arg(short = 'P', long = "port", default_value_t = 2000)]
|
||||||
|
port: u16,
|
||||||
|
|
||||||
|
/// Username
|
||||||
|
#[arg(short = 'a', long = "authuser")]
|
||||||
|
auth_user: Option<String>,
|
||||||
|
|
||||||
|
/// Password
|
||||||
|
#[arg(short = 'p', long = "authpass")]
|
||||||
|
auth_pass: Option<String>,
|
||||||
|
|
||||||
|
/// NAT mode
|
||||||
|
#[arg(short = 'n', long = "nat")]
|
||||||
|
nat: bool,
|
||||||
|
|
||||||
|
/// Duration in seconds (0=unlimited)
|
||||||
|
#[arg(short = 'd', long = "duration", default_value_t = 0)]
|
||||||
|
duration: u64,
|
||||||
|
|
||||||
|
/// Verbose
|
||||||
|
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
|
||||||
|
verbose: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let filter = match cli.verbose {
|
||||||
|
0 => "info",
|
||||||
|
1 => "debug",
|
||||||
|
_ => "trace",
|
||||||
|
};
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(filter)),
|
||||||
|
)
|
||||||
|
.with_target(false)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
btest_rs::cpu::start_sampler();
|
||||||
|
|
||||||
|
if !cli.transmit && !cli.receive {
|
||||||
|
eprintln!("Error: specify -t (transmit) and/or -r (receive)");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let direction = match (cli.transmit, cli.receive) {
|
||||||
|
(true, false) => btest_rs::protocol::CMD_DIR_RX,
|
||||||
|
(false, true) => btest_rs::protocol::CMD_DIR_TX,
|
||||||
|
(true, true) => btest_rs::protocol::CMD_DIR_BOTH,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bw = match &cli.bandwidth {
|
||||||
|
Some(b) => btest_rs::bandwidth::parse_bandwidth(b)?,
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (tx_speed, rx_speed) = match direction {
|
||||||
|
btest_rs::protocol::CMD_DIR_TX => (bw, 0),
|
||||||
|
btest_rs::protocol::CMD_DIR_RX => (0, bw),
|
||||||
|
_ => (bw, bw),
|
||||||
|
};
|
||||||
|
|
||||||
|
let state = btest_rs::bandwidth::BandwidthState::new();
|
||||||
|
let state_clone = state.clone();
|
||||||
|
|
||||||
|
let host = cli.host.clone();
|
||||||
|
let client_fut = btest_rs::client::run_client(
|
||||||
|
&host, cli.port, direction, cli.udp,
|
||||||
|
tx_speed, rx_speed,
|
||||||
|
cli.auth_user, cli.auth_pass, cli.nat,
|
||||||
|
state_clone,
|
||||||
|
);
|
||||||
|
|
||||||
|
if cli.duration > 0 {
|
||||||
|
match tokio::time::timeout(
|
||||||
|
std::time::Duration::from_secs(cli.duration),
|
||||||
|
client_fut,
|
||||||
|
).await {
|
||||||
|
Ok(r) => { let _ = r?; }
|
||||||
|
Err(_) => {
|
||||||
|
state.running.store(false, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let _ = client_fut.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
62
src/bin/server_only.rs
Normal file
62
src/bin/server_only.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//! btest-server: minimal bandwidth test server for embedded/OpenWrt systems.
|
||||||
|
//!
|
||||||
|
//! Stripped-down server that accepts MikroTik client connections.
|
||||||
|
//! No client mode, no syslog, no CSV, smaller binary footprint.
|
||||||
|
//!
|
||||||
|
//! Build: cargo build --profile release-small --bin btest-server
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(name = "btest-server", about = "MikroTik Bandwidth Test server", version)]
|
||||||
|
struct Cli {
|
||||||
|
/// Port
|
||||||
|
#[arg(short = 'P', long = "port", default_value_t = 2000)]
|
||||||
|
port: u16,
|
||||||
|
|
||||||
|
/// IPv4 listen address
|
||||||
|
#[arg(long = "listen", default_value = "0.0.0.0")]
|
||||||
|
listen_addr: String,
|
||||||
|
|
||||||
|
/// Username
|
||||||
|
#[arg(short = 'a', long = "authuser")]
|
||||||
|
auth_user: Option<String>,
|
||||||
|
|
||||||
|
/// Password
|
||||||
|
#[arg(short = 'p', long = "authpass")]
|
||||||
|
auth_pass: Option<String>,
|
||||||
|
|
||||||
|
/// Use EC-SRP5 authentication
|
||||||
|
#[arg(long = "ecsrp5")]
|
||||||
|
ecsrp5: bool,
|
||||||
|
|
||||||
|
/// Verbose
|
||||||
|
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
|
||||||
|
verbose: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let filter = match cli.verbose {
|
||||||
|
0 => "info",
|
||||||
|
1 => "debug",
|
||||||
|
_ => "trace",
|
||||||
|
};
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(
|
||||||
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new(filter)),
|
||||||
|
)
|
||||||
|
.with_target(false)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
btest_rs::cpu::start_sampler();
|
||||||
|
|
||||||
|
let v4 = if cli.listen_addr.eq_ignore_ascii_case("none") { None } else { Some(cli.listen_addr) };
|
||||||
|
|
||||||
|
tracing::info!("btest-server starting on port {}", cli.port);
|
||||||
|
btest_rs::server::run_server(cli.port, cli.auth_user, cli.auth_pass, cli.ecsrp5, v4, None).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -167,10 +167,9 @@ async fn tcp_client_tx_loop(
|
|||||||
|
|
||||||
match interval {
|
match interval {
|
||||||
Some(iv) => {
|
Some(iv) => {
|
||||||
next_send += iv;
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if next_send > now {
|
if let Some(delay) = bandwidth::advance_next_send(&mut next_send, iv, now) {
|
||||||
tokio::time::sleep(next_send - now).await;
|
tokio::time::sleep(delay).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -317,10 +316,9 @@ async fn udp_client_tx_loop(
|
|||||||
|
|
||||||
match interval {
|
match interval {
|
||||||
Some(iv) => {
|
Some(iv) => {
|
||||||
next_send += iv;
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if next_send > now {
|
if let Some(delay) = bandwidth::advance_next_send(&mut next_send, iv, now) {
|
||||||
tokio::time::sleep(next_send - now).await;
|
tokio::time::sleep(delay).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
86
src/cpu.rs
86
src/cpu.rs
@@ -1,7 +1,7 @@
|
|||||||
//! Lightweight CPU usage measurement.
|
//! Lightweight CPU usage measurement.
|
||||||
//!
|
//!
|
||||||
//! Returns the system-wide CPU usage as a percentage (0-100).
|
//! Returns the system-wide CPU usage as a percentage (0-100).
|
||||||
//! Works on macOS and Linux without external dependencies.
|
//! Works on macOS, Linux, Windows, and FreeBSD without external dependencies.
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicU8, Ordering};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -93,7 +93,82 @@ fn get_cpu_times() -> (u64, u64) {
|
|||||||
(0, 0)
|
(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
#[cfg(target_os = "windows")]
|
||||||
|
fn get_cpu_times() -> (u64, u64) {
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
|
struct FILETIME {
|
||||||
|
dwLowDateTime: u32,
|
||||||
|
dwHighDateTime: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FILETIME {
|
||||||
|
fn to_u64(&self) -> u64 {
|
||||||
|
(self.dwHighDateTime as u64) << 32 | self.dwLowDateTime as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "system" {
|
||||||
|
fn GetSystemTimes(
|
||||||
|
lpIdleTime: *mut FILETIME,
|
||||||
|
lpKernelTime: *mut FILETIME,
|
||||||
|
lpUserTime: *mut FILETIME,
|
||||||
|
) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut idle = FILETIME::default();
|
||||||
|
let mut kernel = FILETIME::default();
|
||||||
|
let mut user = FILETIME::default();
|
||||||
|
|
||||||
|
// SAFETY: We pass valid pointers to stack-allocated FILETIME structs.
|
||||||
|
// GetSystemTimes is a well-documented Win32 API that writes into these
|
||||||
|
// output parameters. A non-zero return value indicates success.
|
||||||
|
let ret = unsafe { GetSystemTimes(&mut idle, &mut kernel, &mut user) };
|
||||||
|
|
||||||
|
if ret != 0 {
|
||||||
|
let idle_ticks = idle.to_u64();
|
||||||
|
// Kernel time includes idle time on Windows, so total = kernel + user.
|
||||||
|
let total_ticks = kernel.to_u64() + user.to_u64();
|
||||||
|
(total_ticks, idle_ticks)
|
||||||
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "freebsd")]
|
||||||
|
fn get_cpu_times() -> (u64, u64) {
|
||||||
|
// kern.cp_time returns: user nice system interrupt idle
|
||||||
|
if let Ok(output) = std::process::Command::new("sysctl")
|
||||||
|
.arg("-n")
|
||||||
|
.arg("kern.cp_time")
|
||||||
|
.output()
|
||||||
|
{
|
||||||
|
if output.status.success() {
|
||||||
|
let text = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let parts: Vec<u64> = text
|
||||||
|
.split_whitespace()
|
||||||
|
.filter_map(|s| s.parse().ok())
|
||||||
|
.collect();
|
||||||
|
if parts.len() >= 5 {
|
||||||
|
let user = parts[0];
|
||||||
|
let nice = parts[1];
|
||||||
|
let system = parts[2];
|
||||||
|
let interrupt = parts[3];
|
||||||
|
let idle = parts[4];
|
||||||
|
let total = user + nice + system + interrupt + idle;
|
||||||
|
return (total, idle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "windows",
|
||||||
|
target_os = "freebsd",
|
||||||
|
)))]
|
||||||
fn get_cpu_times() -> (u64, u64) {
|
fn get_cpu_times() -> (u64, u64) {
|
||||||
(0, 0) // Unsupported platform
|
(0, 0) // Unsupported platform
|
||||||
}
|
}
|
||||||
@@ -116,7 +191,12 @@ mod tests {
|
|||||||
fn test_cpu_times_returns_nonzero() {
|
fn test_cpu_times_returns_nonzero() {
|
||||||
let (total, idle) = get_cpu_times();
|
let (total, idle) = get_cpu_times();
|
||||||
// On supported platforms, total should be > 0
|
// On supported platforms, total should be > 0
|
||||||
if cfg!(any(target_os = "linux", target_os = "macos")) {
|
if cfg!(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "windows",
|
||||||
|
target_os = "freebsd",
|
||||||
|
)) {
|
||||||
assert!(total > 0, "CPU total ticks should be > 0");
|
assert!(total > 0, "CPU total ticks should be > 0");
|
||||||
assert!(idle <= total, "idle should be <= total");
|
assert!(idle <= total, "idle should be <= total");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -565,10 +565,9 @@ async fn tcp_tx_loop_inner(
|
|||||||
|
|
||||||
match interval {
|
match interval {
|
||||||
Some(iv) => {
|
Some(iv) => {
|
||||||
next_send += iv;
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if next_send > now {
|
if let Some(delay) = bandwidth::advance_next_send(&mut next_send, iv, now) {
|
||||||
tokio::time::sleep(next_send - now).await;
|
tokio::time::sleep(delay).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -805,10 +804,9 @@ async fn udp_tx_loop(
|
|||||||
|
|
||||||
match interval {
|
match interval {
|
||||||
Some(iv) => {
|
Some(iv) => {
|
||||||
next_send += iv;
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if next_send > now {
|
if let Some(delay) = bandwidth::advance_next_send(&mut next_send, iv, now) {
|
||||||
tokio::time::sleep(next_send - now).await;
|
tokio::time::sleep(delay).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|||||||
Reference in New Issue
Block a user