commit a206c04ba9cffffea51e5418d11b54a49ae9d377 Author: Yuriy Mayatnikov Date: Sat Jan 31 14:56:07 2026 +0300 Add TCP BBR setup script with README and LICENSE Bash script to automatically enable TCP BBR congestion control on Linux servers. Supports Debian/Ubuntu/CentOS/RHEL/Alpine/Arch, includes --check and --dry-run modes, sysctl backup, and verification. Co-Authored-By: Claude Opus 4.5 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..72040b0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 bbr-setup contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..568a020 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# bbr-setup + +Bash script that enables [TCP BBR](https://github.com/google/bbr) congestion control on Linux servers with a single command. + +## Quick start + +```bash +curl -fsSL https://raw.githubusercontent.com/user/bbr-setup/main/setup.sh | sudo bash +``` + +Or clone and run locally: + +```bash +git clone https://github.com/user/bbr-setup.git +cd bbr-setup +sudo bash setup.sh +``` + +## What it does + +1. Verifies root permissions, distro, and kernel version (4.9+ required) +2. Checks that the `tcp_bbr` module is available +3. Backs up existing sysctl configuration +4. Writes BBR parameters to `/etc/sysctl.d/99-bbr.conf` (or `/etc/sysctl.conf` as fallback) +5. Applies and verifies the new settings + +Parameters applied: + +``` +net.core.default_qdisc = fq +net.ipv4.tcp_congestion_control = bbr +``` + +## Usage + +``` +bbr-setup 1.0.0 — enable TCP BBR congestion control + +Usage: setup.sh [OPTIONS] + +Options: + --check Show current BBR status and exit + --dry-run Show what would be done without making changes + -h, --help Show this help message + -v, --version Show version +``` + +### Examples + +Apply BBR: + +```bash +sudo bash setup.sh +``` + +Check current status without changing anything: + +```bash +sudo bash setup.sh --check +``` + +Preview what would be changed: + +```bash +sudo bash setup.sh --dry-run +``` + +## Supported distributions + +- Debian / Ubuntu +- CentOS / RHEL / Fedora +- Alpine Linux +- Arch Linux +- Any Linux with kernel 4.9+ and the `tcp_bbr` module + +## Exit codes + +| Code | Meaning | +|------|---------| +| `0` | Success or BBR already enabled | +| `1` | Error (missing root, old kernel, module unavailable, verification failed) | + +## License + +[MIT](LICENSE) diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..0a50d8a --- /dev/null +++ b/setup.sh @@ -0,0 +1,328 @@ +#!/usr/bin/env bash +set -euo pipefail + +# --------------------------------------------------------------------------- +# TCP BBR Setup Script +# Automatically enables BBR congestion control on Linux servers. +# --------------------------------------------------------------------------- + +readonly VERSION="1.0.0" +readonly SYSCTL_DROP="/etc/sysctl.d/99-bbr.conf" +readonly SYSCTL_MAIN="/etc/sysctl.conf" +readonly MIN_KERNEL_MAJOR=4 +readonly MIN_KERNEL_MINOR=9 + +# ── Modes ────────────────────────────────────────────────────────────────── +DRY_RUN=false +CHECK_ONLY=false + +# ── Colors ───────────────────────────────────────────────────────────────── +if [[ -t 1 ]]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[0;33m' + CYAN='\033[0;36m' + BOLD='\033[1m' + RESET='\033[0m' +else + RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET='' +fi + +# ── Logging ──────────────────────────────────────────────────────────────── +_ts() { date '+%H:%M:%S'; } + +log_info() { printf "${GREEN}[%s] [INFO]${RESET} %s\n" "$(_ts)" "$*"; } +log_warn() { printf "${YELLOW}[%s] [WARN]${RESET} %s\n" "$(_ts)" "$*"; } +log_error() { printf "${RED}[%s] [ERROR]${RESET} %s\n" "$(_ts)" "$*" >&2; } + +die() { log_error "$*"; exit 1; } + +# ── Usage ────────────────────────────────────────────────────────────────── +usage() { + cat < MIN_KERNEL_MAJOR )) || { (( major == MIN_KERNEL_MAJOR )) && (( minor >= MIN_KERNEL_MINOR )); }; then + log_info "Kernel ${kver} meets minimum requirement (${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}+)" + else + die "Kernel ${kver} is too old. BBR requires ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}+" + fi +} + +# ── Check BBR module availability ───────────────────────────────────────── +check_bbr_module() { + if lsmod | grep -q tcp_bbr; then + log_info "Module tcp_bbr is loaded" + return 0 + fi + + if modprobe -n tcp_bbr 2>/dev/null; then + log_info "Module tcp_bbr is available (not yet loaded)" + return 0 + fi + + # On newer kernels BBR can be built-in (not a module) + if [[ -f /proc/config.gz ]]; then + if zcat /proc/config.gz 2>/dev/null | grep -q 'CONFIG_TCP_CONG_BBR=y'; then + log_info "BBR is built into the kernel" + return 0 + fi + fi + + if sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -q bbr; then + log_info "BBR is listed as available congestion control" + return 0 + fi + + die "tcp_bbr module is not available on this system" +} + +# ── Read current state ──────────────────────────────────────────────────── +get_current_cc() { sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "unknown"; } +get_current_qdisc() { sysctl -n net.core.default_qdisc 2>/dev/null || echo "unknown"; } + +show_status() { + local cc qdisc + cc=$(get_current_cc) + qdisc=$(get_current_qdisc) + + log_info "Current congestion control : ${BOLD}${cc}${RESET}" + log_info "Current default qdisc : ${BOLD}${qdisc}${RESET}" + + if [[ "$cc" == "bbr" && ( "$qdisc" == "fq" || "$qdisc" == "fq_codel" ) ]]; then + return 0 # already configured + fi + return 1 # needs configuration +} + +# ── Determine sysctl target file ───────────────────────────────────────── +pick_sysctl_file() { + if [[ -d /etc/sysctl.d ]]; then + echo "${SYSCTL_DROP}" + else + echo "${SYSCTL_MAIN}" + fi +} + +# ── Backup ──────────────────────────────────────────────────────────────── +backup_sysctl() { + local target="$1" + if [[ -f "$target" ]]; then + local bak="${target}.bak.$(date '+%Y%m%d_%H%M%S')" + cp -a "$target" "$bak" + log_info "Backup created: ${bak}" + echo "$bak" + fi +} + +# ── Write sysctl parameters ────────────────────────────────────────────── +write_params() { + local target="$1" + local needs_cc=true + local needs_qdisc=true + + if [[ -f "$target" ]]; then + grep -q '^net.core.default_qdisc\s*=\s*fq' "$target" && needs_qdisc=false + grep -q '^net.ipv4.tcp_congestion_control\s*=\s*bbr' "$target" && needs_cc=false + fi + + # Also check the main sysctl.conf if we're writing to a drop-in + if [[ "$target" != "$SYSCTL_MAIN" && -f "$SYSCTL_MAIN" ]]; then + grep -q '^net.core.default_qdisc\s*=\s*fq' "$SYSCTL_MAIN" && needs_qdisc=false + grep -q '^net.ipv4.tcp_congestion_control\s*=\s*bbr' "$SYSCTL_MAIN" && needs_cc=false + fi + + if ! $needs_cc && ! $needs_qdisc; then + log_info "Parameters already present in ${target}" + return 0 + fi + + { + echo "" + echo "# TCP BBR — added by bbr-setup ${VERSION} on $(date '+%Y-%m-%d %H:%M:%S')" + $needs_qdisc && echo "net.core.default_qdisc = fq" + $needs_cc && echo "net.ipv4.tcp_congestion_control = bbr" + } >> "$target" + + log_info "Parameters written to ${target}" +} + +# ── Apply & verify ──────────────────────────────────────────────────────── +apply_sysctl() { + local target="$1" + + # Load the bbr module if it isn't loaded yet + if ! lsmod | grep -q tcp_bbr; then + modprobe tcp_bbr 2>/dev/null || true + fi + + sysctl -p "$target" >/dev/null 2>&1 + + # Also reload main if we used a drop-in + if [[ "$target" != "$SYSCTL_MAIN" && -f "$SYSCTL_MAIN" ]]; then + sysctl -p "$SYSCTL_MAIN" >/dev/null 2>&1 || true + fi + + local new_cc new_qdisc + new_cc=$(get_current_cc) + new_qdisc=$(get_current_qdisc) + + if [[ "$new_cc" == "bbr" ]]; then + log_info "Verification passed: congestion control = ${BOLD}${new_cc}${RESET}" + else + die "Verification failed: congestion control is '${new_cc}', expected 'bbr'" + fi + + if [[ "$new_qdisc" == "fq" || "$new_qdisc" == "fq_codel" ]]; then + log_info "Verification passed: default qdisc = ${BOLD}${new_qdisc}${RESET}" + else + log_warn "Qdisc is '${new_qdisc}' (expected 'fq'). This may still work but fq is recommended." + fi +} + +# ── Summary ─────────────────────────────────────────────────────────────── +print_summary() { + local old_cc="$1" old_qdisc="$2" new_cc="$3" new_qdisc="$4" backup_path="${5:-}" + + echo "" + printf "${BOLD}%-30s %-14s %-14s${RESET}\n" "" "Before" "After" + printf "%-30s %-14s %-14s\n" "Congestion control" "$old_cc" "$new_cc" + printf "%-30s %-14s %-14s\n" "Default qdisc" "$old_qdisc" "$new_qdisc" + echo "" + + if [[ -n "$backup_path" ]]; then + log_info "Backup saved to: ${backup_path}" + fi +} + +# ══════════════════════════════════════════════════════════════════════════ +# Main +# ══════════════════════════════════════════════════════════════════════════ +main() { + parse_args "$@" + + echo "" + printf "${CYAN}${BOLD} TCP BBR Setup ${VERSION}${RESET}\n" + echo "" + + # ── Root check ───────────────────────────────────────────────────── + if [[ $EUID -ne 0 ]]; then + die "This script must be run as root (use sudo)" + fi + + # ── Distro detection ─────────────────────────────────────────────── + local distro + distro=$(detect_distro) + log_info "Detected distro: ${BOLD}${distro}${RESET}" + + # ── Kernel check ─────────────────────────────────────────────────── + check_kernel + + # ── BBR module check ─────────────────────────────────────────────── + check_bbr_module + + # ── Current state ────────────────────────────────────────────────── + local old_cc old_qdisc + old_cc=$(get_current_cc) + old_qdisc=$(get_current_qdisc) + + if show_status; then + log_info "${GREEN}BBR is already enabled. Nothing to do.${RESET}" + print_summary "$old_cc" "$old_qdisc" "$old_cc" "$old_qdisc" + exit 0 + fi + + # ── Check-only mode ──────────────────────────────────────────────── + if $CHECK_ONLY; then + log_warn "BBR is NOT currently enabled" + exit 1 + fi + + # ── Dry-run mode ─────────────────────────────────────────────────── + local target + target=$(pick_sysctl_file) + + if $DRY_RUN; then + log_warn "Dry-run mode — no changes will be made" + echo "" + log_info "Would write to: ${target}" + log_info "Parameters:" + echo " net.core.default_qdisc = fq" + echo " net.ipv4.tcp_congestion_control = bbr" + echo "" + exit 0 + fi + + # ── Apply changes ────────────────────────────────────────────────── + log_info "Configuring BBR..." + + local backup_path="" + backup_path=$(backup_sysctl "$target") + + write_params "$target" + apply_sysctl "$target" + + local new_cc new_qdisc + new_cc=$(get_current_cc) + new_qdisc=$(get_current_qdisc) + + print_summary "$old_cc" "$old_qdisc" "$new_cc" "$new_qdisc" "$backup_path" + log_info "${GREEN}${BOLD}Done.${RESET}" +} + +main "$@"