commit a868bde71b17c9c79a47b730aa2ac9be22a91647 Author: root Date: Sat Aug 15 13:20:21 2026 +0800 Initial commit diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..c0c9ece --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +github.000060000.xyz \ No newline at end of file diff --git a/Debian_Kernel.sh b/Debian_Kernel.sh new file mode 100644 index 0000000..c9e491b --- /dev/null +++ b/Debian_Kernel.sh @@ -0,0 +1,103 @@ +#!/bin/bash +#草系最新moeclub.org的代码 + +deb_issue="$(cat /etc/issue)" +deb_relese="$(echo $deb_issue |grep -io 'Ubuntu\|Debian' |sed -r 's/(.*)/\L\1/')" +os_ver="$(dpkg --print-architecture)" +[ -n "$os_ver" ] || exit 1 + +if [ "$deb_relese" == 'ubuntu' ]; then + deb_ver="$(echo $deb_issue |grep -o '[0-9]*\.[0-9]*' |head -n1)" + if [ "$deb_ver" == "14.04" ]; then + item="3.16.0-77-generic" && ver='trusty' + elif [ "$deb_ver" == "16.04" ]; then + item="4.8.0-36-generic" && ver='xenial' + elif [ "$deb_ver" == "18.04" ]; then + item="4.15.0-30-generic" && ver='bionic' + else + exit 1 + fi + url='archive.ubuntu.com' + urls='security.ubuntu.com' +elif [ "$deb_relese" == 'debian' ]; then + deb_ver="$(echo $deb_issue |grep -o '[0-9]*' |head -n1)" + if [ "$deb_ver" == "7" ]; then + item="3.2.0-4-${os_ver}" && ver='wheezy' && url='archive.debian.org' && urls='archive.debian.org' + elif [ "$deb_ver" == "8" ]; then + item="3.16.0-4-${os_ver}" && ver='jessie' && url='archive.debian.org' && urls='deb.debian.org' + elif [ "$deb_ver" == "9" ]; then + item="4.9.0-4-${os_ver}" && ver='stretch' && url='deb.debian.org' && urls='deb.debian.org' + else + exit 1 + fi +else + exit 1 +fi + +[ -n "$item" ] && [ -n "$urls" ] && [ -n "$url" ] && [ -n "$ver" ] || exit 1 + +if [ "$deb_relese" == 'ubuntu' ]; then + echo "deb http://${url}/${deb_relese} ${ver} main restricted universe multiverse" >/etc/apt/sources.list + echo "deb http://${url}/${deb_relese} ${ver}-updates main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb http://${url}/${deb_relese} ${ver}-backports main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb http://${urls}/${deb_relese} ${ver}-security main restricted universe multiverse" >>/etc/apt/sources.list +elif [ "$deb_relese" == 'debian' ]; then + echo "deb http://${url}/${deb_relese} ${ver} main" >/etc/apt/sources.list + echo "deb-src http://${url}/${deb_relese} ${ver} main" >>/etc/apt/sources.list + echo "deb http://${urls}/${deb_relese}-security ${ver}/updates main" >>/etc/apt/sources.list + echo "deb-src http://${urls}/${deb_relese}-security ${ver}/updates main" >>/etc/apt/sources.list +fi + +apt-get update +apt-get install --no-install-recommends -y linux-image-${item} +if [ $? -ne 0 ]; then + if [ "$deb_ver" == "8" ]; then + dpkg -l |grep -q 'linux-base' || { + wget --no-check-certificate -qO '/tmp/linux-base_3.5_all.deb' 'http://snapshot.debian.org/archive/debian/20120304T220938Z/pool/main/l/linux-base/linux-base_3.5_all.deb' + dpkg -i '/tmp/linux-base_3.5_all.deb' + } + wget --no-check-certificate -qO '/tmp/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' 'http://snapshot.debian.org/archive/debian/20171008T163152Z/pool/main/l/linux/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' + dpkg -i '/tmp/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' + if [ $? -ne 0 ]; then + exit 1 + fi + elif [ "$deb_ver" == "9" ]; then + dpkg -l |grep -q 'linux-base' || { + wget --no-check-certificate -qO '/tmp/linux-base_4.5_all.deb' 'http://snapshot.debian.org/archive/debian/20160917T042239Z/pool/main/l/linux-base/linux-base_4.5_all.deb' + dpkg -i '/tmp/linux-base_4.5_all.deb' + } + wget --no-check-certificate -qO '/tmp/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' 'http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' + dpkg -i '/tmp/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' + ##备选 + #https://sys.if.ci/download/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://mirror.cs.uchicago.edu/debian-security/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://debian.sipwise.com/debian-security/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://srv24.dsidata.sk/security.debian.org/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://pubmirror.plutex.de/debian-security/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://packages.mendix.com/debian/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3_amd64.deb + #http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://snapshot.debian.org/archive/debian/20171231T180144Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3_amd64.deb + if [ $? -ne 0 ]; then + exit 1 + fi + else + exit 1 + fi +else + exit 1 +fi +# while true; do + # List_Kernel="$(dpkg -l |grep 'linux-image\|linux-modules\|linux-generic\|linux-headers' |grep -v "$item")" + # Num_Kernel="$(echo "$List_Kernel" |sed '/^$/d' |wc -l)" + # [ "$Num_Kernel" -eq "0" ] && break + # for kernel in `echo "$List_Kernel" |awk '{print $2}'` + # do + # if [ -f "/var/lib/dpkg/info/${kernel}.prerm" ]; then + # sed -i 's/linux-check-removal/#linux-check-removal/' "/var/lib/dpkg/info/${kernel}.prerm" + # sed -i 's/uname -r/echo purge/' "/var/lib/dpkg/info/${kernel}.prerm" + # fi + # dpkg --force-depends --purge "$kernel" + # done + # done +apt-get autoremove -y +[ -d '/var/lib/apt/lists' ] && find /var/lib/apt/lists -type f -delete diff --git a/InstallNET.sh b/InstallNET.sh new file mode 100644 index 0000000..8497f4e --- /dev/null +++ b/InstallNET.sh @@ -0,0 +1,870 @@ +#!/bin/bash + +## License: GPL +## It can reinstall Debian, Ubuntu, CentOS system with network. +## Default root password: MoeClub.org +## Blog: https://moeclub.org +## Written By MoeClub.org + + +export tmpVER='' +export tmpDIST='' +export tmpURL='' +export tmpWORD='' +export tmpMirror='' +export tmpSSL='' +export tmpINS='' +export ipAddr='' +export ipMask='' +export ipGate='' +export Relese='' +export ddMode='0' +export setNet='0' +export setRDP='0' +export setIPv6='0' +export isMirror='0' +export FindDists='0' +export loaderMode='0' +export IncFirmware='0' +export SpikCheckDIST='0' +export setInterfaceName='0' +export UNKNOWHW='0' +export UNVER='6.4' + +while [[ $# -ge 1 ]]; do + case $1 in + -v|--ver) + shift + tmpVER="$1" + shift + ;; + -d|--debian) + shift + Relese='Debian' + tmpDIST="$1" + shift + ;; + -u|--ubuntu) + shift + Relese='Ubuntu' + tmpDIST="$1" + shift + ;; + -c|--centos) + shift + Relese='CentOS' + tmpDIST="$1" + shift + ;; + -dd|--image) + shift + ddMode='1' + tmpURL="$1" + shift + ;; + -p|--password) + shift + tmpWORD="$1" + shift + ;; + -i|--interface) + shift + interface="$1" + shift + ;; + --ip-addr) + shift + ipAddr="$1" + shift + ;; + --ip-mask) + shift + ipMask="$1" + shift + ;; + --ip-gate) + shift + ipGate="$1" + shift + ;; + --dev-net) + shift + setInterfaceName='1' + ;; + --loader) + shift + loaderMode='1' + ;; + --prefer) + shift + tmpPrefer="$1" + shift + ;; + -a|--auto) + shift + tmpINS='auto' + ;; + -m|--manual) + shift + tmpINS='manual' + ;; + -apt|-yum|--mirror) + shift + isMirror='1' + tmpMirror="$1" + shift + ;; + -rdp) + shift + setRDP='1' + WinRemote="$1" + shift + ;; + -ssl) + shift + tmpSSL="$1" + shift + ;; + -firmware) + shift + IncFirmware="1" + ;; + --ipv6) + shift + setIPv6='1' + ;; + *) + if [[ "$1" != 'error' ]]; then echo -ne "\nInvaild option: '$1'\n\n"; fi + echo -ne " Usage:\n\tbash $(basename $0)\t-d/--debian [\033[33m\033[04mdists-name\033[0m]\n\t\t\t\t-u/--ubuntu [\033[04mdists-name\033[0m]\n\t\t\t\t-c/--centos [\033[33m\033[04mdists-verison\033[0m]\n\t\t\t\t-v/--ver [32/\033[33m\033[04mi386\033[0m|64/amd64]\n\t\t\t\t--ip-addr/--ip-gate/--ip-mask\n\t\t\t\t-apt/-yum/--mirror\n\t\t\t\t-dd/--image\n\t\t\t\t-a/--auto\n\t\t\t\t-m/--manual\n" + exit 1; + ;; + esac + done + +[[ "$EUID" -ne '0' ]] && echo "Error:This script must be run as root!" && exit 1; + +function CheckDependence(){ +FullDependence='0'; +for BIN_DEP in `echo "$1" |sed 's/,/\n/g'` + do + if [[ -n "$BIN_DEP" ]]; then + Founded='0'; + for BIN_PATH in `echo "$PATH" |sed 's/:/\n/g'` + do + ls $BIN_PATH/$BIN_DEP >/dev/null 2>&1; + if [ $? == '0' ]; then + Founded='1'; + break; + fi + done + if [ "$Founded" == '1' ]; then + echo -en "[\033[32mok\033[0m]\t"; + else + FullDependence='1'; + echo -en "[\033[31mNot Install\033[0m]"; + fi + echo -en "\t$BIN_DEP\n"; + fi + done +if [ "$FullDependence" == '1' ]; then + echo -ne "\n\033[31mError! \033[0mPlease use '\033[33mapt-get\033[0m' or '\033[33myum\033[0m' install it.\n\n\n" + exit 1; +fi +} + +function SelectMirror(){ + [ $# -ge 3 ] || exit 1 + Relese="$1" + DIST=$(echo "$2" |sed 's/\ //g' |sed -r 's/(.*)/\L\1/') + VER=$(echo "$3" |sed 's/\ //g' |sed -r 's/(.*)/\L\1/') + New=$(echo "$4" |sed 's/\ //g') + [ -n "$Relese" ] || exit 1 + [ -n "$DIST" ] || exit 1 + [ -n "$VER" ] || exit 1 + relese=$(echo $Relese |sed -r 's/(.*)/\L\1/') + if [ "$Relese" == "Debian" ] || [ "$Relese" == "Ubuntu" ]; then + inUpdate=''; [ "$Relese" == "Ubuntu" ] && inUpdate='-updates' + if [[ "$isDigital" == '20.04' ]] || [[ "$DIST" == 'focal' ]]; then + MirrorTEMP="SUB_MIRROR/dists/${DIST}/main/installer-${VER}/current/legacy-images/netboot/${relese}-installer/${VER}/initrd.gz" + else + MirrorTEMP="SUB_MIRROR/dists/${DIST}${inUpdate}/main/installer-${VER}/current/images/netboot/${relese}-installer/${VER}/initrd.gz" + fi + elif [ "$Relese" == "CentOS" ]; then + MirrorTEMP="SUB_MIRROR/${DIST}/os/${VER}/isolinux/initrd.img" + fi + [ -n "$MirrorTEMP" ] || exit 1 + MirrorStatus=0 + declare -A MirrorBackup + MirrorBackup=(["Debian0"]="" ["Debian1"]="http://deb.debian.org/debian" ["Debian2"]="http://archive.debian.org/debian" ["Ubuntu0"]="" ["Ubuntu1"]="http://archive.ubuntu.com/ubuntu" ["CentOS0"]="" ["CentOS1"]="http://mirror.centos.org/centos" ["CentOS2"]="http://vault.centos.org") + echo "$New" |grep -q '^http://\|^https://\|^ftp://' && MirrorBackup[${Relese}0]="$New" + for mirror in $(echo "${!MirrorBackup[@]}" |sed 's/\ /\n/g' |sort -n |grep "^$Relese") + do + CurMirror="${MirrorBackup[$mirror]}" + [ -n "$CurMirror" ] || continue + MirrorURL=`echo "$MirrorTEMP" |sed "s#SUB_MIRROR#${CurMirror}#g"` + wget --no-check-certificate --spider --timeout=3 -o /dev/null "$MirrorURL" + [ $? -eq 0 ] && MirrorStatus=1 && break + done + [ $MirrorStatus -eq 1 ] && echo "$CurMirror" || exit 1 +} + +[ -n "$Relese" ] || Relese='Debian' +linux_relese=$(echo "$Relese" |sed 's/\ //g' |sed -r 's/(.*)/\L\1/') +clear && echo -e "\n\033[36m# Check Dependence\033[0m\n" + +if [[ "$ddMode" == '1' ]]; then + CheckDependence iconv; + linux_relese='debian'; + tmpDIST='jessie'; + tmpVER='amd64'; + tmpINS='auto'; +fi + +if [[ "$Relese" == 'Debian' ]] || [[ "$Relese" == 'Ubuntu' ]]; then + CheckDependence wget,awk,grep,sed,cut,cat,cpio,gzip,find,dirname,basename; +elif [[ "$Relese" == 'CentOS' ]]; then + CheckDependence wget,awk,grep,sed,cut,cat,cpio,gzip,find,dirname,basename,file,xz; +fi +[ -n "$tmpWORD" ] && CheckDependence openssl + +if [[ "$loaderMode" == "0" ]]; then + [[ -f '/boot/grub/grub.cfg' ]] && GRUBVER='0' && GRUBDIR='/boot/grub' && GRUBFILE='grub.cfg'; + [[ -z "$GRUBDIR" ]] && [[ -f '/boot/grub2/grub.cfg' ]] && GRUBVER='0' && GRUBDIR='/boot/grub2' && GRUBFILE='grub.cfg'; + [[ -z "$GRUBDIR" ]] && [[ -f '/boot/grub/grub.conf' ]] && GRUBVER='1' && GRUBDIR='/boot/grub' && GRUBFILE='grub.conf'; + [ -z "$GRUBDIR" -o -z "$GRUBFILE" ] && echo -ne "Error! \nNot Found grub.\n" && exit 1; +else + tmpINS='auto' +fi + +if [[ -n "$tmpVER" ]]; then + tmpVER="$(echo "$tmpVER" |sed -r 's/(.*)/\L\1/')"; + if [[ "$tmpVER" == '32' ]] || [[ "$tmpVER" == 'i386' ]] || [[ "$tmpVER" == 'x86' ]]; then + VER='i386'; + fi + if [[ "$tmpVER" == '64' ]] || [[ "$tmpVER" == 'amd64' ]] || [[ "$tmpVER" == 'x86_64' ]] || [[ "$tmpVER" == 'x64' ]]; then + if [[ "$Relese" == 'Debian' ]] || [[ "$Relese" == 'Ubuntu' ]]; then + VER='amd64'; + elif [[ "$Relese" == 'CentOS' ]]; then + VER='x86_64'; + fi + fi +fi +[ -z "$VER" ] && VER='amd64' + +if [[ -z "$tmpDIST" ]]; then + [ "$Relese" == 'Debian' ] && tmpDIST='jessie' && DIST='jessie'; + [ "$Relese" == 'Ubuntu' ] && tmpDIST='bionic' && DIST='bionic'; + [ "$Relese" == 'CentOS' ] && tmpDIST='6.10' && DIST='6.10'; +fi + +if [[ -z "$DIST" ]]; then + if [[ "$Relese" == 'Debian' ]]; then + SpikCheckDIST='0' + DIST="$(echo "$tmpDIST" |sed -r 's/(.*)/\L\1/')"; + echo "$DIST" |grep -q '[0-9]'; + [[ $? -eq '0' ]] && { + isDigital="$(echo "$DIST" |grep -o '[\.0-9]\{1,\}' |sed -n '1h;1!H;$g;s/\n//g;$p' |cut -d'.' -f1)"; + [[ -n $isDigital ]] && { + [[ "$isDigital" == '7' ]] && DIST='wheezy'; + [[ "$isDigital" == '8' ]] && DIST='jessie'; + [[ "$isDigital" == '9' ]] && DIST='stretch'; + [[ "$isDigital" == '10' ]] && DIST='buster'; + } + } + LinuxMirror=$(SelectMirror "$Relese" "$DIST" "$VER" "$tmpMirror") + fi + if [[ "$Relese" == 'Ubuntu' ]]; then + SpikCheckDIST='0' + DIST="$(echo "$tmpDIST" |sed -r 's/(.*)/\L\1/')"; + echo "$DIST" |grep -q '[0-9]'; + [[ $? -eq '0' ]] && { + isDigital="$(echo "$DIST" |grep -o '[\.0-9]\{1,\}' |sed -n '1h;1!H;$g;s/\n//g;$p')"; + [[ -n $isDigital ]] && { + [[ "$isDigital" == '12.04' ]] && DIST='precise'; + [[ "$isDigital" == '14.04' ]] && DIST='trusty'; + [[ "$isDigital" == '16.04' ]] && DIST='xenial'; + [[ "$isDigital" == '18.04' ]] && DIST='bionic'; + [[ "$isDigital" == '20.04' ]] && DIST='focal'; + } + } + LinuxMirror=$(SelectMirror "$Relese" "$DIST" "$VER" "$tmpMirror") + fi + if [[ "$Relese" == 'CentOS' ]]; then + SpikCheckDIST='1' + DISTCheck="$(echo "$tmpDIST" |grep -o '[\.0-9]\{1,\}')"; + LinuxMirror=$(SelectMirror "$Relese" "$DISTCheck" "$VER" "$tmpMirror") + ListDIST="$(wget --no-check-certificate -qO- "$LinuxMirror/dir_sizes" |cut -f2 |grep '^[0-9]')" + DIST="$(echo "$ListDIST" |grep "^$DISTCheck" |head -n1)" + [[ -z "$DIST" ]] && { + echo -ne '\nThe dists version not found in this mirror, Please check it! \n\n' + bash $0 error; + exit 1; + } + wget --no-check-certificate -qO- "$LinuxMirror/$DIST/os/$VER/.treeinfo" |grep -q 'general'; + [[ $? != '0' ]] && { + echo -ne "\nThe version not found in this mirror, Please change mirror try again! \n\n"; + exit 1; + } + fi +fi + +if [[ -z "$LinuxMirror" ]]; then + echo -ne "\033[31mError! \033[0mInvaild mirror! \n" + [ "$Relese" == 'Debian' ] && echo -en "\033[33mexample:\033[0m http://deb.debian.org/debian\n\n"; + [ "$Relese" == 'Ubuntu' ] && echo -en "\033[33mexample:\033[0m http://archive.ubuntu.com/ubuntu\n\n"; + [ "$Relese" == 'CentOS' ] && echo -en "\033[33mexample:\033[0m http://mirror.centos.org/centos\n\n"; + bash $0 error; + exit 1; +fi + +if [[ "$SpikCheckDIST" == '0' ]]; then + DistsList="$(wget --no-check-certificate -qO- "$LinuxMirror/dists/" |grep -o 'href=.*/"' |cut -d'"' -f2 |sed '/-\|old\|Debian\|experimental\|stable\|test\|sid\|devel/d' |grep '^[^/]' |sed -n '1h;1!H;$g;s/\n//g;s/\//\;/g;$p')"; + for CheckDEB in `echo "$DistsList" |sed 's/;/\n/g'` + do + [[ "$CheckDEB" == "$DIST" ]] && FindDists='1' && break; + done + [[ "$FindDists" == '0' ]] && { + echo -ne '\nThe dists version not found, Please check it! \n\n' + bash $0 error; + exit 1; + } +fi + +[[ "$ddMode" == '1' ]] && { + export SSL_SUPPORT='https://github.com/MoeClub/MoeClub.github.io/raw/master/lib/wget_udeb_amd64.tar.gz'; + if [[ -n "$tmpURL" ]]; then + DDURL="$tmpURL" + echo "$DDURL" |grep -q '^http://\|^ftp://\|^https://'; + [[ $? -ne '0' ]] && echo 'Please input vaild URL,Only support http://, ftp:// and https:// !' && exit 1; + [[ -n "$tmpSSL" ]] && SSL_SUPPORT="$tmpSSL"; + else + echo 'Please input vaild image URL! '; + exit 1; + fi +} + +[[ -n "$tmpINS" ]] && { + [[ "$tmpINS" == 'auto' ]] && inVNC='n'; + [[ "$tmpINS" == 'manual' ]] && inVNC='y'; +} + +[ -n "$ipAddr" ] && [ -n "$ipMask" ] && [ -n "$ipGate" ] && setNet='1'; +[[ -n "$tmpWORD" ]] && myPASSWORD="$(openssl passwd -1 "$tmpWORD")"; +[[ -z "$myPASSWORD" ]] && myPASSWORD='$1$4BJZaD0A$y1QykUnJ6mXprENfwpseH0'; + +if [[ -n "$interface" ]]; then + IFETH="$interface" +else + if [[ "$linux_relese" == 'centos' ]]; then + IFETH="link" + else + IFETH="auto" + fi +fi + +clear && echo -e "\n\033[36m# Install\033[0m\n" + +ASKVNC(){ + inVNC='y'; + [[ "$ddMode" == '0' ]] && { + echo -ne "\033[34mDo you want to install os manually?\033[0m\e[33m[\e[32my\e[33m/n]\e[0m " + read tmpinVNC + [[ -n "$inVNCtmp" ]] && inVNC="$tmpinVNC" + } + [ "$inVNC" == 'y' -o "$inVNC" == 'Y' ] && inVNC='y'; + [ "$inVNC" == 'n' -o "$inVNC" == 'N' ] && inVNC='n'; +} + +[ "$inVNC" == 'y' -o "$inVNC" == 'n' ] || ASKVNC; +[[ "$ddMode" == '0' ]] && { + [[ "$inVNC" == 'y' ]] && echo -e "\033[34mManual Mode\033[0m insatll [\033[33m$Relese\033[0m] [\033[33m$DIST\033[0m] [\033[33m$VER\033[0m] in VNC. " + [[ "$inVNC" == 'n' ]] && echo -e "\033[34mAuto Mode\033[0m insatll [\033[33m$Relese\033[0m] [\033[33m$DIST\033[0m] [\033[33m$VER\033[0m]. " +} +[[ "$ddMode" == '1' ]] && { + echo -ne "\033[34mAuto Mode\033[0m insatll \033[33mWindows\033[0m\n[\033[33m$DDURL\033[0m]\n" +} + +if [[ "$linux_relese" == 'centos' ]]; then + if [[ "$DIST" != "$UNVER" ]]; then + awk 'BEGIN{print '${UNVER}'-'${DIST}'}' |grep -q '^-' + if [ $? != '0' ]; then + UNKNOWHW='1'; + echo -en "\033[33mThe version lower then \033[31m$UNVER\033[33m may not support in auto mode! \033[0m\n"; + if [[ "$inVNC" == 'n' ]]; then + echo -en "\033[35mYou can connect VNC with \033[32mPublic IP\033[35m and port \033[32m1\033[35m/\033[32m5901\033[35m in vnc viewer.\033[0m\n" + read -n 1 -p "Press Enter to continue..." INP + [[ "$INP" != '' ]] && echo -ne '\b \n\n'; + fi + fi + awk 'BEGIN{print '${UNVER}'-'${DIST}'+0.59}' |grep -q '^-' + if [ $? == '0' ]; then + echo -en "\n\033[31mThe version higher then \033[33m6.10 \033[31mis not support in current! \033[0m\n\n" + exit 1; + fi + fi +fi + +echo -e "\n[\033[33m$Relese\033[0m] [\033[33m$DIST\033[0m] [\033[33m$VER\033[0m] Downloading..." + +if [[ "$linux_relese" == 'debian' ]] || [[ "$linux_relese" == 'ubuntu' ]]; then + inUpdate=''; [ "$linux_relese" == 'ubuntu' ] && inUpdate='-updates' + if [[ "$isDigital" == '20.04' ]] || [[ "$DIST" == 'focal' ]]; then + wget --no-check-certificate -qO '/boot/initrd.img' "${LinuxMirror}/dists/${DIST}/main/installer-${VER}/current/legacy-images/netboot/${linux_relese}-installer/${VER}/initrd.gz" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'initrd.img' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 + wget --no-check-certificate -qO '/boot/vmlinuz' "${LinuxMirror}/dists/${DIST}/main/installer-${VER}/current/legacy-images/netboot/${linux_relese}-installer/${VER}/linux" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'vmlinuz' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 + else + wget --no-check-certificate -qO '/boot/initrd.img' "${LinuxMirror}/dists/${DIST}${inUpdate}/main/installer-${VER}/current/images/netboot/${linux_relese}-installer/${VER}/initrd.gz" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'initrd.img' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 + wget --no-check-certificate -qO '/boot/vmlinuz' "${LinuxMirror}/dists/${DIST}${inUpdate}/main/installer-${VER}/current/images/netboot/${linux_relese}-installer/${VER}/linux" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'vmlinuz' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 + fi + MirrorHost="$(echo "$LinuxMirror" |awk -F'://|/' '{print $2}')"; + MirrorFolder="$(echo "$LinuxMirror" |awk -F''${MirrorHost}'' '{print $2}')"; +elif [[ "$linux_relese" == 'centos' ]]; then + wget --no-check-certificate -qO '/boot/initrd.img' "${LinuxMirror}/${DIST}/os/${VER}/isolinux/initrd.img" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'initrd.img' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 + wget --no-check-certificate -qO '/boot/vmlinuz' "${LinuxMirror}/${DIST}/os/${VER}/isolinux/vmlinuz" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'vmlinuz' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 +else + bash $0 error; + exit 1; +fi +if [[ "$linux_relese" == 'debian' ]]; then + if [[ "$IncFirmware" == '1' ]]; then + wget --no-check-certificate -qO '/boot/firmware.cpio.gz' "http://cdimage.debian.org/cdimage/unofficial/non-free/firmware/${DIST}/current/firmware.cpio.gz" + [[ $? -ne '0' ]] && echo -ne "\033[31mError! \033[0mDownload 'firmware' for \033[33m$linux_relese\033[0m failed! \n" && exit 1 + fi + if [[ "$ddMode" == '1' ]]; then + vKernel_udeb=$(wget --no-check-certificate -qO- "http://$DISTMirror/dists/$DIST/main/installer-$VER/current/images/udeb.list" |grep '^acpi-modules' |head -n1 |grep -o '[0-9]\{1,2\}.[0-9]\{1,2\}.[0-9]\{1,2\}-[0-9]\{1,2\}' |head -n1) + [[ -z "vKernel_udeb" ]] && vKernel_udeb="3.16.0-6" + fi +fi + +[[ "$setNet" == '1' ]] && { + IPv4="$ipAddr"; + MASK="$ipMask"; + GATE="$ipGate"; +} || { + DEFAULTNET="$(ip route show |grep -o 'default via [0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.*' |head -n1 |sed 's/proto.*\|onlink.*//g' |awk '{print $NF}')"; + [[ -n "$DEFAULTNET" ]] && IPSUB="$(ip addr |grep ''${DEFAULTNET}'' |grep 'global' |grep 'brd' |head -n1 |grep -o '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}/[0-9]\{1,2\}')"; + IPv4="$(echo -n "$IPSUB" |cut -d'/' -f1)"; + NETSUB="$(echo -n "$IPSUB" |grep -o '/[0-9]\{1,2\}')"; + GATE="$(ip route show |grep -o 'default via [0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}' |head -n1 |grep -o '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}')"; + [[ -n "$NETSUB" ]] && MASK="$(echo -n '128.0.0.0/1,192.0.0.0/2,224.0.0.0/3,240.0.0.0/4,248.0.0.0/5,252.0.0.0/6,254.0.0.0/7,255.0.0.0/8,255.128.0.0/9,255.192.0.0/10,255.224.0.0/11,255.240.0.0/12,255.248.0.0/13,255.252.0.0/14,255.254.0.0/15,255.255.0.0/16,255.255.128.0/17,255.255.192.0/18,255.255.224.0/19,255.255.240.0/20,255.255.248.0/21,255.255.252.0/22,255.255.254.0/23,255.255.255.0/24,255.255.255.128/25,255.255.255.192/26,255.255.255.224/27,255.255.255.240/28,255.255.255.248/29,255.255.255.252/30,255.255.255.254/31,255.255.255.255/32' |grep -o '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}'${NETSUB}'' |cut -d'/' -f1)"; +} + +[[ -n "$GATE" ]] && [[ -n "$MASK" ]] && [[ -n "$IPv4" ]] || { +echo "Not found \`ip command\`, It will use \`route command\`." +ipNum() { + local IFS='.'; + read ip1 ip2 ip3 ip4 <<<"$1"; + echo $((ip1*(1<<24)+ip2*(1<<16)+ip3*(1<<8)+ip4)); +} + +SelectMax(){ +ii=0; +for IPITEM in `route -n |awk -v OUT=$1 '{print $OUT}' |grep '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}'` + do + NumTMP="$(ipNum $IPITEM)"; + eval "arrayNum[$ii]='$NumTMP,$IPITEM'"; + ii=$[$ii+1]; + done +echo ${arrayNum[@]} |sed 's/\s/\n/g' |sort -n -k 1 -t ',' |tail -n1 |cut -d',' -f2; +} + +[[ -z $IPv4 ]] && IPv4="$(ifconfig |grep 'Bcast' |head -n1 |grep -o '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}' |head -n1)"; +[[ -z $GATE ]] && GATE="$(SelectMax 2)"; +[[ -z $MASK ]] && MASK="$(SelectMax 3)"; + +[[ -n "$GATE" ]] && [[ -n "$MASK" ]] && [[ -n "$IPv4" ]] || { + echo "Error! Not configure network. "; + exit 1; +} +} + +[[ "$setNet" != '1' ]] && [[ -f '/etc/network/interfaces' ]] && { + [[ -z "$(sed -n '/iface.*inet static/p' /etc/network/interfaces)" ]] && AutoNet='1' || AutoNet='0'; + [[ -d /etc/network/interfaces.d ]] && { + ICFGN="$(find /etc/network/interfaces.d -name '*.cfg' |wc -l)" || ICFGN='0'; + [[ "$ICFGN" -ne '0' ]] && { + for NetCFG in `ls -1 /etc/network/interfaces.d/*.cfg` + do + [[ -z "$(cat $NetCFG | sed -n '/iface.*inet static/p')" ]] && AutoNet='1' || AutoNet='0'; + [[ "$AutoNet" -eq '0' ]] && break; + done + } + } +} + +[[ "$setNet" != '1' ]] && [[ -d '/etc/sysconfig/network-scripts' ]] && { + ICFGN="$(find /etc/sysconfig/network-scripts -name 'ifcfg-*' |grep -v 'lo'|wc -l)" || ICFGN='0'; + [[ "$ICFGN" -ne '0' ]] && { + for NetCFG in `ls -1 /etc/sysconfig/network-scripts/ifcfg-* |grep -v 'lo$' |grep -v ':[0-9]\{1,\}'` + do + [[ -n "$(cat $NetCFG | sed -n '/BOOTPROTO.*[dD][hH][cC][pP]/p')" ]] && AutoNet='1' || { + AutoNet='0' && . $NetCFG; + [[ -n $NETMASK ]] && MASK="$NETMASK"; + [[ -n $GATEWAY ]] && GATE="$GATEWAY"; + } + [[ "$AutoNet" -eq '0' ]] && break; + done + } +} + +if [[ "$loaderMode" == "0" ]]; then + [[ ! -f $GRUBDIR/$GRUBFILE ]] && echo "Error! Not Found $GRUBFILE. " && exit 1; + + [[ ! -f $GRUBDIR/$GRUBFILE.old ]] && [[ -f $GRUBDIR/$GRUBFILE.bak ]] && mv -f $GRUBDIR/$GRUBFILE.bak $GRUBDIR/$GRUBFILE.old; + mv -f $GRUBDIR/$GRUBFILE $GRUBDIR/$GRUBFILE.bak; + [[ -f $GRUBDIR/$GRUBFILE.old ]] && cat $GRUBDIR/$GRUBFILE.old >$GRUBDIR/$GRUBFILE || cat $GRUBDIR/$GRUBFILE.bak >$GRUBDIR/$GRUBFILE; +else + GRUBVER='2' +fi + +[[ "$GRUBVER" == '0' ]] && { + READGRUB='/tmp/grub.read' + cat $GRUBDIR/$GRUBFILE |sed -n '1h;1!H;$g;s/\n/%%%%%%%/g;$p' |grep -om 1 'menuentry\ [^{]*{[^}]*}%%%%%%%' |sed 's/%%%%%%%/\n/g' >$READGRUB + LoadNum="$(cat $READGRUB |grep -c 'menuentry ')" + if [[ "$LoadNum" -eq '1' ]]; then + cat $READGRUB |sed '/^$/d' >/tmp/grub.new; + elif [[ "$LoadNum" -gt '1' ]]; then + CFG0="$(awk '/menuentry /{print NR}' $READGRUB|head -n 1)"; + CFG2="$(awk '/menuentry /{print NR}' $READGRUB|head -n 2 |tail -n 1)"; + CFG1=""; + for tmpCFG in `awk '/}/{print NR}' $READGRUB` + do + [ "$tmpCFG" -gt "$CFG0" -a "$tmpCFG" -lt "$CFG2" ] && CFG1="$tmpCFG"; + done + [[ -z "$CFG1" ]] && { + echo "Error! read $GRUBFILE. "; + exit 1; + } + + sed -n "$CFG0,$CFG1"p $READGRUB >/tmp/grub.new; + [[ -f /tmp/grub.new ]] && [[ "$(grep -c '{' /tmp/grub.new)" -eq "$(grep -c '}' /tmp/grub.new)" ]] || { + echo -ne "\033[31mError! \033[0mNot configure $GRUBFILE. \n"; + exit 1; + } + fi + [ ! -f /tmp/grub.new ] && echo "Error! $GRUBFILE. " && exit 1; + sed -i "/menuentry.*/c\menuentry\ \'Install OS \[$DIST\ $VER\]\'\ --class debian\ --class\ gnu-linux\ --class\ gnu\ --class\ os\ \{" /tmp/grub.new + sed -i "/echo.*Loading/d" /tmp/grub.new; + INSERTGRUB="$(awk '/menuentry /{print NR}' $GRUBDIR/$GRUBFILE|head -n 1)" +} + +[[ "$GRUBVER" == '1' ]] && { + CFG0="$(awk '/title[\ ]|title[\t]/{print NR}' $GRUBDIR/$GRUBFILE|head -n 1)"; + CFG1="$(awk '/title[\ ]|title[\t]/{print NR}' $GRUBDIR/$GRUBFILE|head -n 2 |tail -n 1)"; + [[ -n $CFG0 ]] && [ -z $CFG1 -o $CFG1 == $CFG0 ] && sed -n "$CFG0,$"p $GRUBDIR/$GRUBFILE >/tmp/grub.new; + [[ -n $CFG0 ]] && [ -z $CFG1 -o $CFG1 != $CFG0 ] && sed -n "$CFG0,$[$CFG1-1]"p $GRUBDIR/$GRUBFILE >/tmp/grub.new; + [[ ! -f /tmp/grub.new ]] && echo "Error! configure append $GRUBFILE. " && exit 1; + sed -i "/title.*/c\title\ \'Install OS \[$DIST\ $VER\]\'" /tmp/grub.new; + sed -i '/^#/d' /tmp/grub.new; + INSERTGRUB="$(awk '/title[\ ]|title[\t]/{print NR}' $GRUBDIR/$GRUBFILE|head -n 1)" +} + +if [[ "$loaderMode" == "0" ]]; then +[[ -n "$(grep 'linux.*/\|kernel.*/' /tmp/grub.new |awk '{print $2}' |tail -n 1 |grep '^/boot/')" ]] && Type='InBoot' || Type='NoBoot'; + +LinuxKernel="$(grep 'linux.*/\|kernel.*/' /tmp/grub.new |awk '{print $1}' |head -n 1)"; +[[ -z "$LinuxKernel" ]] && echo "Error! read grub config! " && exit 1; +LinuxIMG="$(grep 'initrd.*/' /tmp/grub.new |awk '{print $1}' |tail -n 1)"; +[ -z "$LinuxIMG" ] && sed -i "/$LinuxKernel.*\//a\\\tinitrd\ \/" /tmp/grub.new && LinuxIMG='initrd'; + +if [[ "$setInterfaceName" == "1" ]]; then + Add_OPTION="net.ifnames=0 biosdevname=0"; +else + Add_OPTION=""; +fi + +if [[ "$setIPv6" == "1" ]]; then + Add_OPTION="$Add_OPTION ipv6.disable=1"; +fi + +if [[ "$linux_relese" == 'debian' ]] || [[ "$linux_relese" == 'ubuntu' ]]; then + BOOT_OPTION="auto=true $Add_OPTION hostname=$linux_relese domain= -- quiet" +elif [[ "$linux_relese" == 'centos' ]]; then + BOOT_OPTION="ks=file://ks.cfg $Add_OPTION ksdevice=$IFETH" +fi + +[[ "$Type" == 'InBoot' ]] && { + sed -i "/$LinuxKernel.*\//c\\\t$LinuxKernel\\t\/boot\/vmlinuz $BOOT_OPTION" /tmp/grub.new; + sed -i "/$LinuxIMG.*\//c\\\t$LinuxIMG\\t\/boot\/initrd.img" /tmp/grub.new; +} + +[[ "$Type" == 'NoBoot' ]] && { + sed -i "/$LinuxKernel.*\//c\\\t$LinuxKernel\\t\/vmlinuz $BOOT_OPTION" /tmp/grub.new; + sed -i "/$LinuxIMG.*\//c\\\t$LinuxIMG\\t\/initrd.img" /tmp/grub.new; +} + +sed -i '$a\\n' /tmp/grub.new; +fi + +[[ "$inVNC" == 'n' ]] && { +GRUBPATCH='0'; + +if [[ "$loaderMode" == "0" ]]; then +[ -f '/etc/network/interfaces' -o -d '/etc/sysconfig/network-scripts' ] || { + echo "Error, Not found interfaces config."; + exit 1; +} + +sed -i ''${INSERTGRUB}'i\\n' $GRUBDIR/$GRUBFILE; +sed -i ''${INSERTGRUB}'r /tmp/grub.new' $GRUBDIR/$GRUBFILE; +[[ -f $GRUBDIR/grubenv ]] && sed -i 's/saved_entry/#saved_entry/g' $GRUBDIR/grubenv; +fi + +[[ -d /tmp/boot ]] && rm -rf /tmp/boot; +mkdir -p /tmp/boot; +cd /tmp/boot; +if [[ "$linux_relese" == 'debian' ]] || [[ "$linux_relese" == 'ubuntu' ]]; then + COMPTYPE="gzip"; +elif [[ "$linux_relese" == 'centos' ]]; then + COMPTYPE="$(file /boot/initrd.img |grep -o ':.*compressed data' |cut -d' ' -f2 |sed -r 's/(.*)/\L\1/' |head -n1)" + [[ -z "$COMPTYPE" ]] && echo "Detect compressed type fail." && exit 1; +fi +CompDected='0' +for ListCOMP in `echo -en 'gzip\nlzma\nxz'` + do + if [[ "$COMPTYPE" == "$ListCOMP" ]]; then + CompDected='1' + if [[ "$COMPTYPE" == 'gzip' ]]; then + NewIMG="initrd.img.gz" + else + NewIMG="initrd.img.$COMPTYPE" + fi + mv -f "/boot/initrd.img" "/tmp/$NewIMG" + break; + fi + done +[[ "$CompDected" != '1' ]] && echo "Detect compressed type not support." && exit 1; +[[ "$COMPTYPE" == 'lzma' ]] && UNCOMP='xz --format=lzma --decompress'; +[[ "$COMPTYPE" == 'xz' ]] && UNCOMP='xz --decompress'; +[[ "$COMPTYPE" == 'gzip' ]] && UNCOMP='gzip -d'; + +$UNCOMP < /tmp/$NewIMG | cpio --extract --verbose --make-directories --no-absolute-filenames >>/dev/null 2>&1 + +if [[ "$linux_relese" == 'debian' ]] || [[ "$linux_relese" == 'ubuntu' ]]; then +cat >/tmp/boot/preseed.cfg<>/dev/null 2>&1 +} + +[[ "$ddMode" == '1' ]] && { +WinNoDHCP(){ + echo -ne "for\0040\0057f\0040\0042tokens\00753\0052\0042\0040\0045\0045i\0040in\0040\0050\0047netsh\0040interface\0040show\0040interface\0040\0136\0174more\0040\00533\0040\0136\0174findstr\0040\0057I\0040\0057R\0040\0042本地\0056\0052\0040以太\0056\0052\0040Local\0056\0052\0040Ethernet\0042\0047\0051\0040do\0040\0050set\0040EthName\0075\0045\0045j\0051\r\nnetsh\0040\0055c\0040interface\0040ip\0040set\0040address\0040name\0075\0042\0045EthName\0045\0042\0040source\0075static\0040address\0075$IPv4\0040mask\0075$MASK\0040gateway\0075$GATE\r\nnetsh\0040\0055c\0040interface\0040ip\0040add\0040dnsservers\0040name\0075\0042\0045EthName\0045\0042\0040address\00758\00568\00568\00568\0040index\00751\0040validate\0075no\r\n\r\n" >>'/tmp/boot/net.tmp'; +} +WinRDP(){ + echo -ne "netsh\0040firewall\0040set\0040portopening\0040protocol\0075ALL\0040port\0075$WinRemote\0040name\0075RDP\0040mode\0075ENABLE\0040scope\0075ALL\0040profile\0075ALL\r\nnetsh\0040firewall\0040set\0040portopening\0040protocol\0075ALL\0040port\0075$WinRemote\0040name\0075RDP\0040mode\0075ENABLE\0040scope\0075ALL\0040profile\0075CURRENT\r\nreg\0040add\0040\0042HKLM\0134SYSTEM\0134CurrentControlSet\0134Control\0134Network\0134NewNetworkWindowOff\0042\0040\0057f\r\nreg\0040add\0040\0042HKLM\0134SYSTEM\0134CurrentControlSet\0134Control\0134Terminal\0040Server\0042\0040\0057v\0040fDenyTSConnections\0040\0057t\0040reg\0137dword\0040\0057d\00400\0040\0057f\r\nreg\0040add\0040\0042HKLM\0134SYSTEM\0134CurrentControlSet\0134Control\0134Terminal\0040Server\0134Wds\0134rdpwd\0134Tds\0134tcp\0042\0040\0057v\0040PortNumber\0040\0057t\0040reg\0137dword\0040\0057d\0040$WinRemote\0040\0057f\r\nreg\0040add\0040\0042HKLM\0134SYSTEM\0134CurrentControlSet\0134Control\0134Terminal\0040Server\0134WinStations\0134RDP\0055Tcp\0042\0040\0057v\0040PortNumber\0040\0057t\0040reg\0137dword\0040\0057d\0040$WinRemote\0040\0057f\r\nreg\0040add\0040\0042HKLM\0134SYSTEM\0134CurrentControlSet\0134Control\0134Terminal\0040Server\0134WinStations\0134RDP\0055Tcp\0042\0040\0057v\0040UserAuthentication\0040\0057t\0040reg\0137dword\0040\0057d\00400\0040\0057f\r\nFOR\0040\0057F\0040\0042tokens\00752\0040delims\0075\0072\0042\0040\0045\0045i\0040in\0040\0050\0047SC\0040QUERYEX\0040TermService\0040\0136\0174FINDSTR\0040\0057I\0040\0042PID\0042\0047\0051\0040do\0040TASKKILL\0040\0057F\0040\0057PID\0040\0045\0045i\r\nFOR\0040\0057F\0040\0042tokens\00752\0040delims\0075\0072\0042\0040\0045\0045i\0040in\0040\0050\0047SC\0040QUERYEX\0040UmRdpService\0040\0136\0174FINDSTR\0040\0057I\0040\0042PID\0042\0047\0051\0040do\0040TASKKILL\0040\0057F\0040\0057PID\0040\0045\0045i\r\nSC\0040START\0040TermService\r\n\r\n" >>'/tmp/boot/net.tmp'; +} + echo -ne "\0100ECHO\0040OFF\r\n\r\ncd\0056\0076\0045WINDIR\0045\0134GetAdmin\r\nif\0040exist\0040\0045WINDIR\0045\0134GetAdmin\0040\0050del\0040\0057f\0040\0057q\0040\0042\0045WINDIR\0045\0134GetAdmin\0042\0051\0040else\0040\0050\r\necho\0040CreateObject\0136\0050\0042Shell\0056Application\0042\0136\0051\0056ShellExecute\0040\0042\0045\0176s0\0042\0054\0040\0042\0045\0052\0042\0054\0040\0042\0042\0054\0040\0042runas\0042\0054\00401\0040\0076\0076\0040\0042\0045temp\0045\0134Admin\0056vbs\0042\r\n\0042\0045temp\0045\0134Admin\0056vbs\0042\r\ndel\0040\0057f\0040\0057q\0040\0042\0045temp\0045\0134Admin\0056vbs\0042\r\nexit\0040\0057b\00402\0051\r\n\r\n" >'/tmp/boot/net.tmp'; + [[ "$setNet" == '1' ]] && WinNoDHCP; + [[ "$setNet" == '0' ]] && [[ "$AutoNet" == '0' ]] && WinNoDHCP; + [[ "$setRDP" == '1' ]] && [[ -n "$WinRemote" ]] && WinRDP + echo -ne "ECHO\0040SELECT\0040VOLUME\0075\0045\0045SystemDrive\0045\0045\0040\0076\0040\0042\0045SystemDrive\0045\0134diskpart\0056extend\0042\r\nECHO\0040EXTEND\0040\0076\0076\0040\0042\0045SystemDrive\0045\0134diskpart\0056extend\0042\r\nSTART\0040/WAIT\0040DISKPART\0040\0057S\0040\0042\0045SystemDrive\0045\0134diskpart\0056extend\0042\r\nDEL\0040\0057f\0040\0057q\0040\0042\0045SystemDrive\0045\0134diskpart\0056extend\0042\r\n\r\n" >>'/tmp/boot/net.tmp'; + echo -ne "cd\0040\0057d\0040\0042\0045ProgramData\0045\0057Microsoft\0057Windows\0057Start\0040Menu\0057Programs\0057Startup\0042\r\ndel\0040\0057f\0040\0057q\0040net\0056bat\r\n\r\n\r\n" >>'/tmp/boot/net.tmp'; + iconv -f 'UTF-8' -t 'GBK' '/tmp/boot/net.tmp' -o '/tmp/boot/net.bat' + rm -rf '/tmp/boot/net.tmp' + echo "$DDURL" |grep -q '^https://' + [[ $? -eq '0' ]] && { + echo -ne '\nAdd ssl support...\n' + [[ -n $SSL_SUPPORT ]] && { + wget --no-check-certificate -qO- "$SSL_SUPPORT" |tar -x + [[ ! -f /tmp/boot/usr/bin/wget ]] && echo 'Error! SSL_SUPPORT.' && exit 1; + sed -i 's/wget\ -qO-/\/usr\/bin\/wget\ --no-check-certificate\ --retry-connrefused\ --tries=7\ --continue\ -qO-/g' /tmp/boot/preseed.cfg + [[ $? -eq '0' ]] && echo -ne 'Success! \n\n' + } || { + echo -ne 'Not ssl support package! \n\n'; + exit 1; + } + } +} + +[[ "$ddMode" == '0' ]] && { + sed -i '/anna-install/d' /tmp/boot/preseed.cfg + sed -i 's/wget.*\/sbin\/reboot\;\ //g' /tmp/boot/preseed.cfg +} + +elif [[ "$linux_relese" == 'centos' ]]; then +cat >/tmp/boot/ks.cfg< /boot/initrd.img; +rm -rf /tmp/boot; +} + +[[ "$inVNC" == 'y' ]] && { + sed -i '$i\\n' $GRUBDIR/$GRUBFILE + sed -i '$r /tmp/grub.new' $GRUBDIR/$GRUBFILE + echo -e "\n\033[33m\033[04mIt will reboot! \nPlease connect VNC! \nSelect\033[0m\033[32m Install OS [$DIST $VER] \033[33m\033[4mto install system.\033[04m\n\n\033[31m\033[04mThere is some information for you.\nDO NOT CLOSE THE WINDOW! \033[0m\n" + echo -e "\033[35mIPv4\t\tNETMASK\t\tGATEWAY\033[0m" + echo -e "\033[36m\033[04m$IPv4\033[0m\t\033[36m\033[04m$MASK\033[0m\t\033[36m\033[04m$GATE\033[0m\n\n" + + read -n 1 -p "Press Enter to reboot..." INP + [[ "$INP" != '' ]] && echo -ne '\b \n\n'; +} + +chown root:root $GRUBDIR/$GRUBFILE +chmod 444 $GRUBDIR/$GRUBFILE + +if [[ "$loaderMode" == "0" ]]; then + sleep 3 && reboot >/dev/null 2>&1 +else + rm -rf "$HOME/loader" + mkdir -p "$HOME/loader" + cp -rf "/boot/initrd.img" "$HOME/loader/initrd.img" + cp -rf "/boot/vmlinuz" "$HOME/loader/vmlinuz" + [[ -f "/boot/initrd.img" ]] && rm -rf "/boot/initrd.img" + [[ -f "/boot/vmlinuz" ]] && rm -rf "/boot/vmlinuz" + echo && ls -AR1 "$HOME/loader" +fi diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..23cb790 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LotServer_KeyGen-main.zip b/LotServer_KeyGen-main.zip new file mode 100644 index 0000000..38f49dd Binary files /dev/null and b/LotServer_KeyGen-main.zip differ diff --git a/LotServer_KeyGen-master.zip b/LotServer_KeyGen-master.zip new file mode 100644 index 0000000..6b83b4b Binary files /dev/null and b/LotServer_KeyGen-master.zip differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..36e979d --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ +赞助 +
+支付宝 +
+![20200312144201.png](https://vip1.loli.io/2020/03/12/7IJvKaTcrLBDbtz.png) + +[搬瓦工在线库存查询](https://bwg.ylx.me/) + + +不卸载内核版本 + +``` +bash <(curl -fsSL https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcpx.sh) +``` + +提示:设置BBR和优化已经分开,需要各走一遍。即20和32都需要运行一遍,不分先后。 + +关联action自动编译内核 + +[https://github.com/ylx2016/kernel/](https://github.com/ylx2016/kernel/) + +尝试加入参数直接调用优化 ./tcpx.sh op1(对应32) (不会判断系统) op3(对应38) + +ip质检 +``` +bash <(curl -Ls IP.Check.Place) +``` + +双持bbr+锐速 +
+bbr 添加 +``` +echo "net.core.default_qdisc=fq" >> /etc/sysctl.d/99-sysctl.conf +``` +``` +echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.d/99-sysctl.conf +``` +``` +sysctl -p +``` +编辑锐速文件 +``` +nano /appex/etc/config +``` +检测代码有BUG,如果锐速正常 运行查看 +``` +bash /appex/bin/lotServer.sh status | grep "LotServer" +``` +检查bbr 内核默认bbr算法不会有输出 +``` +lsmod | grep bbr +``` +检查centos安装内核 +``` +grubby --info=ALL|awk -F= '$1=="kernel" {print i++ " : " $2}' +``` +查看当前支持TCP算法 +``` +cat /proc/sys/net/ipv4/tcp_allowed_congestion_control +``` +查看当前运行的算法 +``` +cat /proc/sys/net/ipv4/tcp_congestion_control +``` +查看当前队列算法 +``` +sysctl net.core.default_qdisc +``` +命令: `uname -a` +
+作用: 查看系统内核版本号及系统名称 + +命令: `cat /proc/version` +
+作用: 查看目录"/proc"下version的信息,也可以得到当前系统的内核版本号及系统名称 + +真实队列查看? 更改队列算法可能需要重启生效 +
+`tc -s qdisc show` + +`/etc/sysctl.d/99-sysctl.conf` +
+`sysctl --system` + +ylx2016与chiakge、cx9208无任何关系 +
+bbsplus算法原作者 +
+https://blog.csdn.net/dog250/article/details/80629551 +
+bbrplus首用名 ? +
+https://github.com/cx9208/bbrplus +
+新版bbrplus +
+https://github.com/UJX6N/bbrplus-5.10 +
+xanmod官网 +
+https://xanmod.org +
+Zen官网 +
+https://liquorix.net/ +
+锐速 +
+https://moeclub.org/2017/03/09/14/ +
+其他内核 +
+https://github.com/alibaba/cloud-kernel +
+https://github.com/Tencent/TencentOS-kernel +
+官方编译好的内核 +
+https://sourceforge.net/projects/xanmod/files/releases/current +
+https://elrepo.org/linux/kernel/el7/x86_64/RPMS/ +
+https://elrepo.org/linux/kernel/el8/x86_64/RPMS/ +
+https://kernel.ubuntu.com/~kernel-ppa/mainline/ +
+http://mirrors.aliyun.com/alinux/2.1903/plus/x86_64/Packages/ +
+https://mirrors.tencent.com/tlinux/2.4/tlinux/x86_64/RPMS/ +
+https://bintray.com/multipath-tcp/mptcp_rpm/mptcp/v0.95.1#files +
+https://bintray.com/multipath-tcp/mptcp_deb/mptcp/v0.95.1#files + +DD脚本 +
+https://git.beta.gs/ +
+https://www.cxthhhhh.com/network-reinstall-system-modify + + +服务周期 +
+https://zh.wikipedia.org/zh/Ubuntu +
+https://wiki.ubuntu.com/Releases +
+https://wiki.debian.org/LTS +
+https://wiki.centos.org/zh/About/Product diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/bbr/centos/6/x32/kernel-ml-4.11.8.rpm b/bbr/centos/6/x32/kernel-ml-4.11.8.rpm new file mode 100644 index 0000000..387b37a Binary files /dev/null and b/bbr/centos/6/x32/kernel-ml-4.11.8.rpm differ diff --git a/bbr/centos/6/x32/kernel-ml-devel-4.11.8.rpm b/bbr/centos/6/x32/kernel-ml-devel-4.11.8.rpm new file mode 100644 index 0000000..72d7204 Binary files /dev/null and b/bbr/centos/6/x32/kernel-ml-devel-4.11.8.rpm differ diff --git a/bbr/centos/6/x32/kernel-ml-headers-4.11.8.rpm b/bbr/centos/6/x32/kernel-ml-headers-4.11.8.rpm new file mode 100644 index 0000000..7ad4f28 Binary files /dev/null and b/bbr/centos/6/x32/kernel-ml-headers-4.11.8.rpm differ diff --git a/bbr/centos/6/x64/kernel-ml-4.11.8.rpm b/bbr/centos/6/x64/kernel-ml-4.11.8.rpm new file mode 100644 index 0000000..4143ee5 Binary files /dev/null and b/bbr/centos/6/x64/kernel-ml-4.11.8.rpm differ diff --git a/bbr/centos/6/x64/kernel-ml-devel-4.11.8.rpm b/bbr/centos/6/x64/kernel-ml-devel-4.11.8.rpm new file mode 100644 index 0000000..4c1a767 Binary files /dev/null and b/bbr/centos/6/x64/kernel-ml-devel-4.11.8.rpm differ diff --git a/bbr/centos/6/x64/kernel-ml-headers-4.11.8.rpm b/bbr/centos/6/x64/kernel-ml-headers-4.11.8.rpm new file mode 100644 index 0000000..9ed970a Binary files /dev/null and b/bbr/centos/6/x64/kernel-ml-headers-4.11.8.rpm differ diff --git a/bbr/centos/7/x64/kernel-ml-4.11.8.rpm b/bbr/centos/7/x64/kernel-ml-4.11.8.rpm new file mode 100644 index 0000000..a5f9c0c Binary files /dev/null and b/bbr/centos/7/x64/kernel-ml-4.11.8.rpm differ diff --git a/bbr/centos/7/x64/kernel-ml-devel-4.11.8.rpm b/bbr/centos/7/x64/kernel-ml-devel-4.11.8.rpm new file mode 100644 index 0000000..de97307 Binary files /dev/null and b/bbr/centos/7/x64/kernel-ml-devel-4.11.8.rpm differ diff --git a/bbr/centos/7/x64/kernel-ml-headers-4.11.8.rpm b/bbr/centos/7/x64/kernel-ml-headers-4.11.8.rpm new file mode 100644 index 0000000..9617c86 Binary files /dev/null and b/bbr/centos/7/x64/kernel-ml-headers-4.11.8.rpm differ diff --git a/bbr/centos/RPM-GPG-KEY-elrepo.org b/bbr/centos/RPM-GPG-KEY-elrepo.org new file mode 100644 index 0000000..fe0c082 --- /dev/null +++ b/bbr/centos/RPM-GPG-KEY-elrepo.org @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.5 (GNU/Linux) + +mQGiBEm+/6QRBAC5mbtqOFSQ0FkTLIMdIoqxtraIeUqwbPp3IBYQ/u/EREjyEf1D +qFyBEXII0dD8JDT85vRZ81jhB7nFWa0VbUfY0xfghkbnokiNBVNpiQcvszw3UYDF +aLAaOC8Z98vmlsQaBBTQG6704ZXLr7FJyG3GP5WE6egXIQQbrMcdmCoRBwCg/dwC +HLWjuemoDc5SX7hKHbB4zZ8D/jP+oMbqz+bDn8OZ2UuaGdxr+mHW8tzTdPjnEU8e +hUt1ws8eBqn/gMfKvUBa8xFSILe8Ty99u+VjFbcRsdf0H6dRre9AdDVUz5oxzoPw +gamA8mhPQvFh3wt9smtRUh5IoM2LiM1s5pGMLuYuvSnVUPArEnSfW6K5I6v7OarU +3WfrBACDEGGcaWKjfdkRtmKIQrzu6AnldVC1ISLVAoqxHnKNFTk1BgO0PSZDpfJI +x8fMCnGlusoL6F5+LYEk4K4B0zvlj1ur3JocjxpuBLccl94JTo/+I9ZbS8ptUqLw +LBUkgIQJzzIH4G5NZsQ3FpzSWGRFVa7etqTv9BfUMUmJxhEoobQ/ZWxyZXBvLm9y +ZyAoUlBNIFNpZ25pbmcgS2V5IGZvciBlbHJlcG8ub3JnKSA8c2VjdXJlQGVscmVw +by5vcmc+iGAEExECACAFAkm+/6QCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK +CRAwm8MFuq2uUlgWAKCGWOpyodbzxS7Xy/0X9m9qVnHq+ACfUHrQzYAtFRpT07Sg +giosE+mvAKu5Ag0ESb7/pxAIALgT0q0HhwugE717a7N+gAtCbFu8pCXOZcrAnJpG +cMA9VWmsODZStPTxXYM2ggCMSzUnNis8pPZOPIP5C+t2IYtVjLshM4C9UiY7u5ET +jErWCxWmF+2QLO50K56E4qxj0wufZt9P+Ih0USUM5L2oyrQG51uj/2v3Qq3igc8Z +NTtmEOfis3losusQbAfZtTBmNQ0570kkhMxiyavgAUxLenXHYrkDJFuL7XdBCmna +kykTn2dzU81rIDZ+QPxII4V/eZ5xGiRY/EDUIAysEV2m0NNZgWi/twUnZICm7zYp +VRviJrBnFTvNEPMhiGRnJgQp/Krv4WIHQ67N8lQg3q5RJX8AAwUH/0UBjBgbsuWR +dB+ZYWjKPBy+bJY/6HefPUuLrt3QDNINMW8kY7VzWRMWyGc4IlPJDW0nwOn/vT5H +Dgc3YmA3tm7cKj5wpFijzff61Db8eq+CUKGscKxDBGzHq6oirM5U7DQLmipCs5Eb +efwHIjE3aOUSnoQmniEnBWI2hm/rftmY9oJSi8xgns4wAokDPiMcucADtbV3fznx +ppuowtBi8bcGB1vJZjTySQcSKWv+IVp5ej06uZ9501BEm6HxUZkuAGPecP65kcQu +5bg7B7LQeI5AWyujqvfuWgAF34xhl9QU/sDWxM3wUog+bw7y8pyWCqnJhp70yx8o +SqzhFygT62eISQQYEQIACQUCSb7/pwIbDAAKCRAwm8MFuq2uUq8PAKC1+E2pSwiS +oHXkKYPYDwApsP1mVACfRe1YnggLYQtG9LMeweVQQC77rK8= +=qyRr +-----END PGP PUBLIC KEY BLOCK----- diff --git a/bbr/centos/tcp_nanqinlang.c b/bbr/centos/tcp_nanqinlang.c new file mode 100644 index 0000000..c8b802f --- /dev/null +++ b/bbr/centos/tcp_nanqinlang.c @@ -0,0 +1,976 @@ +/* tcp_nanqinlang + + * CentOS + + * v4.12.10 mainline kernel + + × New BBR Congestion Control + + * Modified by (C) 2017 nanqinlang + + ******************************************************************************* + * Bottleneck Bandwidth and RTT (BBR) congestion control + * + * BBR congestion control computes the sending rate based on the delivery + * rate (throughput) estimated from ACKs. In a nutshell: + * + * On each ACK, update our model of the network path: + * bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips) + * min_rtt = windowed_min(rtt, 10 seconds) + * pacing_rate = pacing_gain * bottleneck_bandwidth + * cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4) + * + * The core algorithm does not react directly to packet losses or delays, + * although BBR may adjust the size of next send per ACK when loss is + * observed, or adjust the sending rate if it estimates there is a + * traffic policer, in order to keep the drop rate reasonable. + * + * Here is a state transition diagram for BBR: + * + * | + * V + * +---> STARTUP ----+ + * | | | + * | V | + * | DRAIN ----+ + * | | | + * | V | + * +---> PROBE_BW ----+ + * | ^ | | + * | | | | + * | +----+ | + * | | + * +---- PROBE_RTT <--+ + * + * A BBR flow starts in STARTUP, and ramps up its sending rate quickly. + * When it estimates the pipe is full, it enters DRAIN to drain the queue. + * In steady state a BBR flow only uses PROBE_BW and PROBE_RTT. + * A long-lived BBR flow spends the vast majority of its time remaining + * (repeatedly) in PROBE_BW, fully probing and utilizing the pipe's bandwidth + * in a fair manner, with a small, bounded queue. *If* a flow has been + * continuously sending for the entire min_rtt window, and hasn't seen an RTT + * sample that matches or decreases its min_rtt estimate for 10 seconds, then + * it briefly enters PROBE_RTT to cut inflight to a minimum value to re-probe + * the path's two-way propagation delay (min_rtt). When exiting PROBE_RTT, if + * we estimated that we reached the full bw of the pipe then we enter PROBE_BW; + * otherwise we enter STARTUP to try to fill the pipe. + * + * BBR is described in detail in: + * "BBR: Congestion-Based Congestion Control", + * Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, + * Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016. + * + * There is a public e-mail list for discussing BBR development and testing: + * https://groups.google.com/forum/#!forum/bbr-dev + * + * NOTE: BBR *must* be used with the fq qdisc ("man tc-fq") with pacing enabled, + * since pacing is integral to the BBR design and implementation. + * BBR without pacing would not function properly, and may incur unnecessary + * high packet loss rates. + */ + +#include +#include +#include +#include +#include +#include + +/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth + * estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps. + * This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32. + * Since the minimum window is >=4 packets, the lower bound isn't + * an issue. The upper bound isn't an issue with existing technologies. + */ +#define BW_SCALE 24 +#define BW_UNIT (1 << BW_SCALE) + +#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */ +#define BBR_UNIT (1 << BBR_SCALE) + +#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */ + + +// ************************************************************************** +// the following is the main +// ************************************************************************** + + +/* BBR has the following modes for deciding how fast to send: */ +// four working mode +enum bbr_mode { + BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ + BBR_DRAIN, /* drain any queue created during startup */ + BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ + BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */ +}; + + +/* BBR congestion control block */ +// control block with u32 values you set +struct bbr { + u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */ + u32 min_rtt_stamp; /* timestamp of min_rtt_us */ + u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */ + struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */ + u32 rtt_cnt; /* count of packet-timed rounds elapsed */ + u32 next_rtt_delivered; /* scb->tx.delivered at end of round */ + struct skb_mstamp cycle_mstamp; /* time of this cycle phase start */ + u32 mode:3, /* current bbr_mode in state machine */ + prev_ca_state:3, /* CA state on previous ACK */ + packet_conservation:1, /* use packet conservation? */ + restore_cwnd:1, /* decided to revert cwnd to old value */ + round_start:1, /* start of packet-timed tx->ack round? */ + tso_segs_goal:7, /* segments we want in each skb we send */ + idle_restart:1, /* restarting after idle? */ + probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */ + unused:5, + lt_is_sampling:1, /* taking long-term ("LT") samples now? */ + lt_rtt_cnt:7, /* round trips in long-term interval */ + lt_use_bw:1; /* use lt_bw as our bw estimate? */ + u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */ + u32 lt_last_delivered; /* LT intvl start: tp->delivered */ + u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */ + u32 lt_last_lost; /* LT intvl start: tp->lost */ + u32 pacing_gain:10, /* current gain for setting pacing rate */ + cwnd_gain:10, /* current gain for setting cwnd */ + full_bw_cnt:3, /* number of rounds without large bw gains */ + cycle_idx:3, /* current index in pacing_gain cycle array */ + unused_b:5; + u32 prior_cwnd; /* prior cwnd upon entering loss recovery */ + u32 full_bw; /* recent bw, to estimate if pipe is full */ +}; + + +/* Window length of bw filter (in rounds): */ +// according to the above define: "#define CYCLE_LEN 8" +// default is "+2" +static const int bbr_bw_rtts = CYCLE_LEN + 2; +/* Window length of min_rtt filter (in sec): */ +// minimum RTT +// default is 10 seconds +static const u32 bbr_min_rtt_win_sec = 10; +/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */ +// minimum time of keeping in BBR_DRAIN mode +// default is 200 ms +// i set it as 100 ms, decrease minimum drain's time to early switch to BBR_PROBE_BW mode +static const u32 bbr_probe_rtt_mode_ms = 100; +/* Skip TSO below the following bandwidth (bits/sec): */ +static const int bbr_min_tso_rate = 1200000; + +/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain + * that will allow a smoothly increasing pacing rate that will double each RTT + * and send the same number of packets per RTT that an un-paced, slow-starting + * Reno or CUBIC flow would: + */ +// defaultlt use [2885/1000 + 1] to balance high gain pacing +// i set it as [3 + 1] +static const int bbr_high_gain = BBR_UNIT * 3000 / 1000 + 1; +/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain + * the queue created in BBR_STARTUP in a single round: + */ +// according to above, set this as [3 + 1] +static const int bbr_drain_gain = BBR_UNIT * 1000 / 3000; +/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */ +// BBR takes [the calculated BDP]*2 as congestion-window +// when is in BBR_PROBE_BW mode, calculating pacing-rate-which-used-by-cwnd is not based on "bbr_pacing_gain" number-group, but unvariable [BBR_UNIT * 2] +static const int bbr_cwnd_gain = BBR_UNIT * 2; +/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */ +static const int bbr_pacing_gain[] = { +// for the stable bbr mode "BBR_PROBE_BW" which makes the fastest speed mode. +// there are 8 pacing rate + // pacing for discover bandwidth released by other tcp status. + // increase it to discover more available bw. + BBR_UNIT * 6 / 4, /* probe for more available bw */ + // need a drain when the queue is coming even heavy traffic conn. + // the speeder set as 5/3, but the drainer set as 2/3(set it drain less). (all rate value is compare to 1) + BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */ + // the following 6 pacing rate value, are used to decide that: + // those 6 phenemonons are all take [the max bw and the min rtt discovered in 10 bbr period] as rate and cwnd. + // increase it to calculate bigger target value. + BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, /* cruise at 1.0*bw to utilize pipe, */ + BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4 /* without creating excess queue... */ +}; + +/* Randomize the starting gain cycling phase over N phases: */ +static const u32 bbr_cycle_rand = 7; + +/* Try to keep at least this many packets in flight, if things go smoothly. For + * smooth functioning, a sliding window protocol ACKing every other packet + * needs at least 4 packets in flight: + */ +// minimumly keeps 4 package when discover minimum rtt +// use 1 package to discover minimum rtt is less effective. +// use 5 package to discover minimum rtt is more effective, but may makes a heavy package queue. +// use 4 package to discover minimum rtt is just good. +static const u32 bbr_cwnd_min_target = 4; + +/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */ +/* If bw has increased significantly (1.25x), there may be more bw available: */ +static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4; +/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */ +static const u32 bbr_full_bw_cnt = 3; + +/* "long-term" ("LT") bandwidth estimator parameters... */ +/* The minimum number of rounds in an LT bw sampling interval: */ +static const u32 bbr_lt_intvl_min_rtts = 4; +/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */ +static const u32 bbr_lt_loss_thresh = 50; +/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */ +// i set its minimum limit as [1/4] ratio +static const u32 bbr_lt_bw_ratio = BBR_UNIT / 4; +/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */ +// according to the above "bbr_lt_bw_ratio = BBR_UNIT / 4", i set this as [4000/4] +static const u32 bbr_lt_bw_diff = 4000 / 4; +/* If we estimate we're policed, use lt_bw for this many round trips: */ +static const u32 bbr_lt_bw_max_rtts = 48; + +/* Do we estimate that STARTUP filled the pipe? */ +static bool bbr_full_bw_reached(const struct sock *sk) +{ + const struct bbr *bbr = inet_csk_ca(sk); + + return bbr->full_bw_cnt >= bbr_full_bw_cnt; +} + +/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */ +static u32 bbr_max_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return minmax_get(&bbr->bw); +} + +/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */ +static u32 bbr_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk); +} + +/* Return rate in bytes per second, optionally with a gain. + * The order here is chosen carefully to avoid overflow of u64. This should + * work for input rates of up to 2.9Tbit/sec and gain of 2.89x. + */ +static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain) +{ + rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache); + rate *= gain; + rate >>= BBR_SCALE; + rate *= USEC_PER_SEC; + return rate >> BW_SCALE; +} + +/* Pace using current bw estimate and a gain factor. In order to help drive the + * network toward lower queues while maintaining high utilization and low + * latency, the average pacing rate aims to be slightly (~1%) lower than the + * estimated bandwidth. This is an important aspect of the design. In this + * implementation this slightly lower pacing rate is achieved implicitly by not + * including link-layer headers in the packet size used for the pacing rate. + */ +static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u64 rate = bw; + + rate = bbr_rate_bytes_per_sec(sk, rate, gain); + rate = min_t(u64, rate, sk->sk_max_pacing_rate); + if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate) + sk->sk_pacing_rate = rate; +} + +/* Return count of segments we want in the skbs we send, or 0 for default. */ +static u32 bbr_tso_segs_goal(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->tso_segs_goal; +} + +static void bbr_set_tso_segs_goal(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 min_segs; + + min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2; + bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs), + 0x7FU); +} + +/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */ +static void bbr_save_cwnd(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT) + bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */ + else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */ + bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd); +} + +static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (event == CA_EVENT_TX_START && tp->app_limited) { + bbr->idle_restart = 1; + /* Avoid pointless buffer overflows: pace at est. bw if we don't + * need more speed (we're restarting from idle and app-limited). + */ + if (bbr->mode == BBR_PROBE_BW) + bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT); + } +} + +/* Find target cwnd. Right-size the cwnd based on min RTT and the + * estimated bottleneck bandwidth: + * + * cwnd = bw * min_rtt * gain = BDP * gain + * + * The key factor, gain, controls the amount of queue. While a small gain + * builds a smaller queue, it becomes more vulnerable to noise in RTT + * measurements (e.g., delayed ACKs or other ACK compression effects). This + * noise may cause BBR to under-estimate the rate. + * + * To achieve full performance in high-speed paths, we budget enough cwnd to + * fit full-sized skbs in-flight on both end hosts to fully utilize the path: + * - one skb in sending host Qdisc, + * - one skb in sending host TSO/GSO engine + * - one skb being received by receiver host LRO/GRO/delayed-ACK engine + * Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because + * in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets, + * which allows 2 outstanding 2-packet sequences, to try to keep pipe + * full even with ACK-every-other-packet delayed ACKs. + */ +static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd; + u64 w; + + /* If we've never had a valid RTT sample, cap cwnd at the initial + * default. This should only happen when the connection is not using TCP + * timestamps and has retransmitted all of the SYN/SYNACK/data packets + * ACKed so far. In this case, an RTO can cut cwnd to 1, in which + * case we need to slow-start up toward something safe: TCP_INIT_CWND. + */ + if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */ + return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/ + + w = (u64)bw * bbr->min_rtt_us; + + /* Apply a gain to the given value, then remove the BW_SCALE shift. */ + cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT; + + /* Allow enough full-sized skbs in flight to utilize end systems. */ + cwnd += 3 * bbr->tso_segs_goal; + + /* Reduce delayed ACKs by rounding up cwnd to the next even number. */ + cwnd = (cwnd + 1) & ~1U; + + return cwnd; +} + +/* An optimization in BBR to reduce losses: On the first round of recovery, we + * follow the packet conservation principle: send P packets per P packets acked. + * After that, we slow-start and send at most 2*P packets per P packets acked. + * After recovery finishes, or upon undo, we restore the cwnd we had when + * recovery started (capped by the target cwnd based on estimated BDP). + * + * TODO(ycheng/ncardwell): implement a rate-based approach. + */ +static bool bbr_set_cwnd_to_recover_or_restore( + struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state; + u32 cwnd = tp->snd_cwnd; + + /* An ACK for P pkts should release at most 2*P packets. We do this + * in two steps. First, here we deduct the number of lost packets. + * Then, in bbr_set_cwnd() we slow start up toward the target cwnd. + */ + if (rs->losses > 0) + cwnd = max_t(s32, cwnd - rs->losses, 1); + + if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) { + /* Starting 1st round of Recovery, so do packet conservation. */ + bbr->packet_conservation = 1; + bbr->next_rtt_delivered = tp->delivered; /* start round now */ + /* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */ + cwnd = tcp_packets_in_flight(tp) + acked; + } else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) { + /* Exiting loss recovery; restore cwnd saved before recovery. */ + bbr->restore_cwnd = 1; + bbr->packet_conservation = 0; + } + bbr->prev_ca_state = state; + + if (bbr->restore_cwnd) { + /* Restore cwnd after exiting loss recovery or PROBE_RTT. */ + cwnd = max(cwnd, bbr->prior_cwnd); + bbr->restore_cwnd = 0; + } + + if (bbr->packet_conservation) { + *new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked); + return true; /* yes, using packet conservation */ + } + *new_cwnd = cwnd; + return false; +} + +/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss + * has drawn us down below target), or snap down to target if we're above it. + */ +static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs, + u32 acked, u32 bw, int gain) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd = 0, target_cwnd = 0; + + if (!acked) + return; + + if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd)) + goto done; + + /* If we're below target cwnd, slow start cwnd toward target cwnd. */ + target_cwnd = bbr_target_cwnd(sk, bw, gain); + if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */ + cwnd = min(cwnd + acked, target_cwnd); + else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND) + cwnd = cwnd + acked; + cwnd = max(cwnd, bbr_cwnd_min_target); + +done: + tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */ + if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */ + tp->snd_cwnd = max(tp->snd_cwnd >> 1, bbr_cwnd_min_target); +} + +/* End cycle phase if it's time and/or we hit the phase's in-flight target. */ +static bool bbr_is_next_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool is_full_length = + skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) > + bbr->min_rtt_us; + u32 inflight, bw; + + /* The pacing_gain of 1.0 paces at the estimated bw to try to fully + * use the pipe without increasing the queue. + */ + if (bbr->pacing_gain == BBR_UNIT) + return is_full_length; /* just use wall clock time */ + + inflight = rs->prior_in_flight; /* what was in-flight before ACK? */ + bw = bbr_max_bw(sk); + + /* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at + * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is + * small (e.g. on a LAN). We do not persist if packets are lost, since + * a path with small buffers may not hold that much. + */ + if (bbr->pacing_gain > BBR_UNIT) + return is_full_length && + (rs->losses || /* perhaps pacing_gain*BDP won't fit */ + inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain)); + + /* A pacing_gain < 1.0 tries to drain extra queue we added if bw + * probing didn't find more bw. If inflight falls to match BDP then we + * estimate queue is drained; persisting would underutilize the pipe. + */ + return is_full_length || + inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT); +} + +static void bbr_advance_cycle_phase(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1); + bbr->cycle_mstamp = tp->delivered_mstamp; + bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx]; +} + +/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */ +static void bbr_update_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw && + bbr_is_next_cycle_phase(sk, rs)) + bbr_advance_cycle_phase(sk); +} + +static void bbr_reset_startup_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_STARTUP; + bbr->pacing_gain = bbr_high_gain; + bbr->cwnd_gain = bbr_high_gain; +} + +static void bbr_reset_probe_bw_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_PROBE_BW; + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = bbr_cwnd_gain; + bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand); + bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */ +} + +static void bbr_reset_mode(struct sock *sk) +{ + if (!bbr_full_bw_reached(sk)) + bbr_reset_startup_mode(sk); + else + bbr_reset_probe_bw_mode(sk); +} + +/* Start a new long-term sampling interval. */ +static void bbr_reset_lt_bw_sampling_interval(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_last_stamp = tp->delivered_mstamp.stamp_jiffies; + bbr->lt_last_delivered = tp->delivered; + bbr->lt_last_lost = tp->lost; + bbr->lt_rtt_cnt = 0; +} + +/* Completely reset long-term bandwidth sampling. */ +static void bbr_reset_lt_bw_sampling(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_bw = 0; + bbr->lt_use_bw = 0; + bbr->lt_is_sampling = false; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Long-term bw sampling interval is done. Estimate whether we're policed. */ +static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 diff; + + if (bbr->lt_bw) { /* do we have bw from a previous interval? */ + /* Is new bw close to the lt_bw from the previous interval? */ + diff = abs(bw - bbr->lt_bw); + if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) || + (bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <= + bbr_lt_bw_diff)) { + /* All criteria are met; estimate we're policed. */ + bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */ + bbr->lt_use_bw = 1; + bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */ + bbr->lt_rtt_cnt = 0; + return; + } + } + bbr->lt_bw = bw; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of + * Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and + * explicitly models their policed rate, to reduce unnecessary losses. We + * estimate that we're policed if we see 2 consecutive sampling intervals with + * consistent throughput and high packet loss. If we think we're being policed, + * set lt_bw to the "long-term" average delivery rate from those 2 intervals. + */ +static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 lost, delivered; + u64 bw; + s32 t; + + if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */ + if (bbr->mode == BBR_PROBE_BW && bbr->round_start && + ++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) { + bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */ + bbr_reset_probe_bw_mode(sk); /* restart gain cycling */ + } + return; + } + + /* Wait for the first loss before sampling, to let the policer exhaust + * its tokens and estimate the steady-state rate allowed by the policer. + * Starting samples earlier includes bursts that over-estimate the bw. + */ + if (!bbr->lt_is_sampling) { + if (!rs->losses) + return; + bbr_reset_lt_bw_sampling_interval(sk); + bbr->lt_is_sampling = true; + } + + /* To avoid underestimates, reset sampling if we run out of data. */ + if (rs->is_app_limited) { + bbr_reset_lt_bw_sampling(sk); + return; + } + + if (bbr->round_start) + bbr->lt_rtt_cnt++; /* count round trips in this interval */ + if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts) + return; /* sampling interval needs to be longer */ + if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) { + bbr_reset_lt_bw_sampling(sk); /* interval is too long */ + return; + } + + /* End sampling interval when a packet is lost, so we estimate the + * policer tokens were exhausted. Stopping the sampling before the + * tokens are exhausted under-estimates the policed rate. + */ + if (!rs->losses) + return; + + /* Calculate packets lost and delivered in sampling interval. */ + lost = tp->lost - bbr->lt_last_lost; + delivered = tp->delivered - bbr->lt_last_delivered; + /* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */ + if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered) + return; + + /* Find average delivery rate in this sampling interval. */ + t = (s32)(tp->delivered_mstamp.stamp_jiffies - bbr->lt_last_stamp); + if (t < 1) + return; /* interval is less than one jiffy, so wait */ + t = jiffies_to_usecs(t); + /* Interval long enough for jiffies_to_usecs() to return a bogus 0? */ + if (t < 1) { + bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */ + return; + } + bw = (u64)delivered * BW_UNIT; + do_div(bw, t); + bbr_lt_bw_interval_done(sk, bw); +} + +/* Estimate the bandwidth based on how fast packets are delivered */ +static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->round_start = 0; + if (rs->delivered < 0 || rs->interval_us <= 0) + return; /* Not a valid observation */ + + /* See if we've reached the next RTT */ + if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) { + bbr->next_rtt_delivered = tp->delivered; + bbr->rtt_cnt++; + bbr->round_start = 1; + bbr->packet_conservation = 0; + } + + bbr_lt_bw_sampling(sk, rs); + + /* Divide delivered by the interval to find a (lower bound) bottleneck + * bandwidth sample. Delivered is in packets and interval_us in uS and + * ratio will be <<1 for most connections. So delivered is first scaled. + */ + bw = (u64)rs->delivered * BW_UNIT; + do_div(bw, rs->interval_us); + + /* If this sample is application-limited, it is likely to have a very + * low delivered count that represents application behavior rather than + * the available network rate. Such a sample could drag down estimated + * bw, causing needless slow-down. Thus, to continue to send at the + * last measured network rate, we filter out app-limited samples unless + * they describe the path bw at least as well as our bw model. + * + * So the goal during app-limited phase is to proceed with the best + * network rate no matter how long. We automatically leave this + * phase when app writes faster than the network can deliver :) + */ + if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) { + /* Incorporate new sample into our max bw filter. */ + minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw); + } +} + +/* Estimate when the pipe is full, using the change in delivery rate: BBR + * estimates that STARTUP filled the pipe if the estimated bw hasn't changed by + * at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited + * rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the + * higher rwin, 3: we get higher delivery rate samples. Or transient + * cross-traffic or radio noise can go away. CUBIC Hystart shares a similar + * design goal, but uses delay and inter-ACK spacing instead of bandwidth. + */ +static void bbr_check_full_bw_reached(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw_thresh; + + if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited) + return; + + bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE; + if (bbr_max_bw(sk) >= bw_thresh) { + bbr->full_bw = bbr_max_bw(sk); + bbr->full_bw_cnt = 0; + return; + } + ++bbr->full_bw_cnt; +} + +/* If pipe is probably full, drain the queue and then enter steady-state. */ +static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) { + bbr->mode = BBR_DRAIN; /* drain queue we created */ + bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */ + bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */ + } /* fall through to check if in-flight is already small: */ + if (bbr->mode == BBR_DRAIN && + tcp_packets_in_flight(tcp_sk(sk)) <= + bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT)) + bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */ +} + +/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and + * periodically drain the bottleneck queue, to converge to measure the true + * min_rtt (unloaded propagation delay). This allows the flows to keep queues + * small (reducing queuing delay and packet loss) and achieve fairness among + * BBR flows. + * + * The min_rtt filter window is 10 seconds. When the min_rtt estimate expires, + * we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets. + * After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed + * round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and + * re-enter the previous mode. BBR uses 200ms to approximately bound the + * performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s). + * + * Note that flows need only pay 2% if they are busy sending over the last 10 + * seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have + * natural silences or low-rate periods within 10 seconds where the rate is low + * enough for long enough to drain its queue in the bottleneck. We pick up + * these min RTT measurements opportunistically with our min_rtt filter. :-) + */ +static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool filter_expired; + + /* Track min RTT seen in the min_rtt_win_sec filter window: */ + // as above BBR_Structure define: "min_rtt_win_sec = 5 seconds" + filter_expired = after(tcp_time_stamp, + bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ); + if (rs->rtt_us >= 0 && + (rs->rtt_us <= bbr->min_rtt_us || filter_expired)) { + bbr->min_rtt_us = rs->rtt_us; + bbr->min_rtt_stamp = tcp_time_stamp; + } + + if (bbr_probe_rtt_mode_ms > 0 && filter_expired && + !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) { + bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */ + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = BBR_UNIT; + bbr_save_cwnd(sk); /* note cwnd so we can restore it */ + bbr->probe_rtt_done_stamp = 0; + } + + if (bbr->mode == BBR_PROBE_RTT) { + /* Ignore low rate samples during this mode. */ + tp->app_limited = + (tp->delivered + tcp_packets_in_flight(tp)) ? : 1; + /* Maintain min packets in flight for max(200 ms, 1 round). */ + if (!bbr->probe_rtt_done_stamp && + tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) { + bbr->probe_rtt_done_stamp = tcp_time_stamp + + msecs_to_jiffies(bbr_probe_rtt_mode_ms >> 1); + bbr->probe_rtt_round_done = 0; + bbr->next_rtt_delivered = tp->delivered; + } else if (bbr->probe_rtt_done_stamp) { + if (bbr->round_start) + bbr->probe_rtt_round_done = 1; + if (bbr->probe_rtt_round_done && + after(tcp_time_stamp, bbr->probe_rtt_done_stamp)) { + bbr->min_rtt_stamp = tcp_time_stamp; + bbr->restore_cwnd = 1; /* snap to prior_cwnd */ + bbr_reset_mode(sk); + } + } + } + bbr->idle_restart = 0; +} + +static void bbr_update_model(struct sock *sk, const struct rate_sample *rs) +{ + bbr_update_bw(sk, rs); + bbr_update_cycle_phase(sk, rs); + bbr_check_full_bw_reached(sk, rs); + bbr_check_drain(sk, rs); + bbr_update_min_rtt(sk, rs); +} + +static void bbr_main(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw; + + bbr_update_model(sk, rs); + + bw = bbr_bw(sk); + bbr_set_pacing_rate(sk, bw, bbr->pacing_gain); + bbr_set_tso_segs_goal(sk); + bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain); +} + +static void bbr_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->prior_cwnd = 0; + bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */ + bbr->rtt_cnt = 0; + bbr->next_rtt_delivered = 0; + bbr->prev_ca_state = TCP_CA_Open; + bbr->packet_conservation = 0; + + bbr->probe_rtt_done_stamp = 0; + bbr->probe_rtt_round_done = 0; + bbr->min_rtt_us = tcp_min_rtt(tp); + bbr->min_rtt_stamp = tcp_time_stamp; + + minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */ + + /* Initialize pacing rate to: high_gain * init_cwnd / RTT. */ + bw = (u64)tp->snd_cwnd * BW_UNIT; + do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC); + sk->sk_pacing_rate = 0; /* force an update of sk_pacing_rate */ + bbr_set_pacing_rate(sk, bw, bbr_high_gain); + + bbr->restore_cwnd = 0; + bbr->round_start = 0; + bbr->idle_restart = 0; + bbr->full_bw = 0; + bbr->full_bw_cnt = 0; + bbr->cycle_mstamp.v64 = 0; + bbr->cycle_idx = 0; + bbr_reset_lt_bw_sampling(sk); + bbr_reset_startup_mode(sk); +} + +static u32 bbr_sndbuf_expand(struct sock *sk) +{ + /* Provision 3 * cwnd since BBR may slow-start even during recovery. */ + return 3; +} + +/* In theory BBR does not need to undo the cwnd since it does not + * always reduce cwnd on losses (see bbr_main()). Keep it for now. + */ +static u32 bbr_undo_cwnd(struct sock *sk) +{ + return tcp_sk(sk)->snd_cwnd; +} + +/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */ +static u32 bbr_ssthresh(struct sock *sk) +{ + bbr_save_cwnd(sk); + return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */ +} + +static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr, union tcp_cc_info *info) +{ + if (ext & (1 << (INET_DIAG_BBRINFO - 1)) || + ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw = bbr_bw(sk); + bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE; + memset(&info->bbr, 0, sizeof(info->bbr)); + info->bbr.bbr_bw_lo = (u32)bw; + info->bbr.bbr_bw_hi = (u32)(bw >> 32); + info->bbr.bbr_min_rtt = bbr->min_rtt_us; + info->bbr.bbr_pacing_gain = bbr->pacing_gain; + info->bbr.bbr_cwnd_gain = bbr->cwnd_gain; + *attr = INET_DIAG_BBRINFO; + return sizeof(info->bbr); + } + return 0; +} + +static void bbr_set_state(struct sock *sk, u8 new_state) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (new_state == TCP_CA_Loss) { + struct rate_sample rs = { .losses = 1 }; + + bbr->prev_ca_state = TCP_CA_Loss; + bbr->full_bw = 0; + bbr->round_start = 1; /* treat RTO like end of a round */ + bbr_lt_bw_sampling(sk, &rs); + } +} + +static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = { + .flags = TCP_CONG_NON_RESTRICTED, + .name = "nanqinlang", + .owner = THIS_MODULE, + .init = bbr_init, + .cong_control = bbr_main, + .sndbuf_expand = bbr_sndbuf_expand, + .undo_cwnd = bbr_undo_cwnd, + .cwnd_event = bbr_cwnd_event, + .ssthresh = bbr_ssthresh, + .tso_segs_goal = bbr_tso_segs_goal, + .get_info = bbr_get_info, + .set_state = bbr_set_state, +}; + +static int __init bbr_register(void) +{ + BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_bbr_cong_ops); +} + +static void __exit bbr_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_bbr_cong_ops); +} + +module_init(bbr_register); +module_exit(bbr_unregister); + +MODULE_AUTHOR("Van Jacobson "); +MODULE_AUTHOR("Neal Cardwell "); +MODULE_AUTHOR("Yuchung Cheng "); +MODULE_AUTHOR("Soheil Hassas Yeganeh "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)"); +MODULE_AUTHOR("Nanqinlang "); \ No newline at end of file diff --git a/bbr/debian-ubuntu/linux-headers-4.11.8-all.deb b/bbr/debian-ubuntu/linux-headers-4.11.8-all.deb new file mode 100644 index 0000000..d7a72eb Binary files /dev/null and b/bbr/debian-ubuntu/linux-headers-4.11.8-all.deb differ diff --git a/bbr/debian-ubuntu/x32/linux-headers-4.11.8.deb b/bbr/debian-ubuntu/x32/linux-headers-4.11.8.deb new file mode 100644 index 0000000..106dc1e Binary files /dev/null and b/bbr/debian-ubuntu/x32/linux-headers-4.11.8.deb differ diff --git a/bbr/debian-ubuntu/x32/linux-image-4.11.8.deb b/bbr/debian-ubuntu/x32/linux-image-4.11.8.deb new file mode 100644 index 0000000..6f8788a Binary files /dev/null and b/bbr/debian-ubuntu/x32/linux-image-4.11.8.deb differ diff --git a/bbr/debian-ubuntu/x64/linux-headers-4.11.8.deb b/bbr/debian-ubuntu/x64/linux-headers-4.11.8.deb new file mode 100644 index 0000000..d46e3b0 Binary files /dev/null and b/bbr/debian-ubuntu/x64/linux-headers-4.11.8.deb differ diff --git a/bbr/debian-ubuntu/x64/linux-image-4.11.8.deb b/bbr/debian-ubuntu/x64/linux-image-4.11.8.deb new file mode 100644 index 0000000..e47a191 Binary files /dev/null and b/bbr/debian-ubuntu/x64/linux-image-4.11.8.deb differ diff --git a/bbr/tcp_nanqinlang.c b/bbr/tcp_nanqinlang.c new file mode 100644 index 0000000..5ef792c --- /dev/null +++ b/bbr/tcp_nanqinlang.c @@ -0,0 +1,976 @@ +/* tcp_nanqinlang + + * Debian + + * general kernel + + × New BBR Congestion Control + + * Modified by (C) 2017 nanqinlang + + ******************************************************************************* + * Bottleneck Bandwidth and RTT (BBR) congestion control + * + * BBR congestion control computes the sending rate based on the delivery + * rate (throughput) estimated from ACKs. In a nutshell: + * + * On each ACK, update our model of the network path: + * bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips) + * min_rtt = windowed_min(rtt, 10 seconds) + * pacing_rate = pacing_gain * bottleneck_bandwidth + * cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4) + * + * The core algorithm does not react directly to packet losses or delays, + * although BBR may adjust the size of next send per ACK when loss is + * observed, or adjust the sending rate if it estimates there is a + * traffic policer, in order to keep the drop rate reasonable. + * + * Here is a state transition diagram for BBR: + * + * | + * V + * +---> STARTUP ----+ + * | | | + * | V | + * | DRAIN ----+ + * | | | + * | V | + * +---> PROBE_BW ----+ + * | ^ | | + * | | | | + * | +----+ | + * | | + * +---- PROBE_RTT <--+ + * + * A BBR flow starts in STARTUP, and ramps up its sending rate quickly. + * When it estimates the pipe is full, it enters DRAIN to drain the queue. + * In steady state a BBR flow only uses PROBE_BW and PROBE_RTT. + * A long-lived BBR flow spends the vast majority of its time remaining + * (repeatedly) in PROBE_BW, fully probing and utilizing the pipe's bandwidth + * in a fair manner, with a small, bounded queue. *If* a flow has been + * continuously sending for the entire min_rtt window, and hasn't seen an RTT + * sample that matches or decreases its min_rtt estimate for 10 seconds, then + * it briefly enters PROBE_RTT to cut inflight to a minimum value to re-probe + * the path's two-way propagation delay (min_rtt). When exiting PROBE_RTT, if + * we estimated that we reached the full bw of the pipe then we enter PROBE_BW; + * otherwise we enter STARTUP to try to fill the pipe. + * + * BBR is described in detail in: + * "BBR: Congestion-Based Congestion Control", + * Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, + * Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016. + * + * There is a public e-mail list for discussing BBR development and testing: + * https://groups.google.com/forum/#!forum/bbr-dev + * + * NOTE: BBR *must* be used with the fq qdisc ("man tc-fq") with pacing enabled, + * since pacing is integral to the BBR design and implementation. + * BBR without pacing would not function properly, and may incur unnecessary + * high packet loss rates. + */ + +#include +#include +#include +#include +#include +#include + +/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth + * estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps. + * This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32. + * Since the minimum window is >=4 packets, the lower bound isn't + * an issue. The upper bound isn't an issue with existing technologies. + */ +#define BW_SCALE 24 +#define BW_UNIT (1 << BW_SCALE) + +#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */ +#define BBR_UNIT (1 << BBR_SCALE) + +#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */ + + +// ************************************************************************** +// the following is the main +// ************************************************************************** + + +/* BBR has the following modes for deciding how fast to send: */ +// four working mode +enum bbr_mode { + BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ + BBR_DRAIN, /* drain any queue created during startup */ + BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ + BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */ +}; + + +/* BBR congestion control block */ +// control block with u32 values you set +struct bbr { + u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */ + u32 min_rtt_stamp; /* timestamp of min_rtt_us */ + u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */ + struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */ + u32 rtt_cnt; /* count of packet-timed rounds elapsed */ + u32 next_rtt_delivered; /* scb->tx.delivered at end of round */ + struct skb_mstamp cycle_mstamp; /* time of this cycle phase start */ + u32 mode:3, /* current bbr_mode in state machine */ + prev_ca_state:3, /* CA state on previous ACK */ + packet_conservation:1, /* use packet conservation? */ + restore_cwnd:1, /* decided to revert cwnd to old value */ + round_start:1, /* start of packet-timed tx->ack round? */ + tso_segs_goal:7, /* segments we want in each skb we send */ + idle_restart:1, /* restarting after idle? */ + probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */ + unused:5, + lt_is_sampling:1, /* taking long-term ("LT") samples now? */ + lt_rtt_cnt:7, /* round trips in long-term interval */ + lt_use_bw:1; /* use lt_bw as our bw estimate? */ + u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */ + u32 lt_last_delivered; /* LT intvl start: tp->delivered */ + u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */ + u32 lt_last_lost; /* LT intvl start: tp->lost */ + u32 pacing_gain:10, /* current gain for setting pacing rate */ + cwnd_gain:10, /* current gain for setting cwnd */ + full_bw_cnt:3, /* number of rounds without large bw gains */ + cycle_idx:3, /* current index in pacing_gain cycle array */ + unused_b:5; + u32 prior_cwnd; /* prior cwnd upon entering loss recovery */ + u32 full_bw; /* recent bw, to estimate if pipe is full */ +}; + + +/* Window length of bw filter (in rounds): */ +// according to the above define: "#define CYCLE_LEN 8" +// default is "+2" +static const int bbr_bw_rtts = CYCLE_LEN + 2; +/* Window length of min_rtt filter (in sec): */ +// minimum RTT +// default is 10 seconds +static const u32 bbr_min_rtt_win_sec = 10; +/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */ +// minimum time of keeping in BBR_DRAIN mode +// default is 200 ms +// i set it as 100 ms, decrease minimum drain's time to early switch to BBR_PROBE_BW mode +static const u32 bbr_probe_rtt_mode_ms = 100; +/* Skip TSO below the following bandwidth (bits/sec): */ +static const int bbr_min_tso_rate = 1200000; + +/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain + * that will allow a smoothly increasing pacing rate that will double each RTT + * and send the same number of packets per RTT that an un-paced, slow-starting + * Reno or CUBIC flow would: + */ +// defaultlt use [2885/1000 + 1] to balance high gain pacing +// i set it as [3 + 1] +static const int bbr_high_gain = BBR_UNIT * 3000 / 1000 + 1; +/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain + * the queue created in BBR_STARTUP in a single round: + */ +// according to above, set this as [3 + 1] +static const int bbr_drain_gain = BBR_UNIT * 1000 / 3000; +/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */ +// BBR takes [the calculated BDP]*2 as congestion-window +// when is in BBR_PROBE_BW mode, calculating pacing-rate-which-used-by-cwnd is not based on "bbr_pacing_gain" number-group, but unvariable [BBR_UNIT * 2] +static const int bbr_cwnd_gain = BBR_UNIT * 2; +/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */ +static const int bbr_pacing_gain[] = { +// for the stable bbr mode "BBR_PROBE_BW" which makes the fastest speed mode. +// there are 8 pacing rate + // pacing for discover bandwidth released by other tcp status. + // increase it to discover more available bw. + BBR_UNIT * 6 / 4, /* probe for more available bw */ + // need a drain when the queue is coming even heavy traffic conn. + // the speeder set as 5/3, but the drainer set as 2/3(set it drain less). (all rate value is compare to 1) + BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */ + // the following 6 pacing rate value, are used to decide that: + // those 6 phenemonons are all take [the max bw and the min rtt discovered in 10 bbr period] as rate and cwnd. + // increase it to calculate bigger target value. + BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, BBR_UNIT * 5 / 4, /* cruise at 1.0*bw to utilize pipe, */ + BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4, BBR_UNIT * 6 / 4 /* without creating excess queue... */ +}; + +/* Randomize the starting gain cycling phase over N phases: */ +static const u32 bbr_cycle_rand = 7; + +/* Try to keep at least this many packets in flight, if things go smoothly. For + * smooth functioning, a sliding window protocol ACKing every other packet + * needs at least 4 packets in flight: + */ +// minimumly keeps 4 package when discover minimum rtt +// use 1 package to discover minimum rtt is less effective. +// use 5 package to discover minimum rtt is more effective, but may makes a heavy package queue. +// use 4 package to discover minimum rtt is just good. +static const u32 bbr_cwnd_min_target = 4; + +/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */ +/* If bw has increased significantly (1.25x), there may be more bw available: */ +static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4; +/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */ +static const u32 bbr_full_bw_cnt = 3; + +/* "long-term" ("LT") bandwidth estimator parameters... */ +/* The minimum number of rounds in an LT bw sampling interval: */ +static const u32 bbr_lt_intvl_min_rtts = 4; +/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */ +static const u32 bbr_lt_loss_thresh = 50; +/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */ +// i set its minimum limit as [1/4] ratio +static const u32 bbr_lt_bw_ratio = BBR_UNIT / 4; +/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */ +// according to the above "bbr_lt_bw_ratio = BBR_UNIT / 4", i set this as [4000/4] +static const u32 bbr_lt_bw_diff = 4000 / 4; +/* If we estimate we're policed, use lt_bw for this many round trips: */ +static const u32 bbr_lt_bw_max_rtts = 48; + +/* Do we estimate that STARTUP filled the pipe? */ +static bool bbr_full_bw_reached(const struct sock *sk) +{ + const struct bbr *bbr = inet_csk_ca(sk); + + return bbr->full_bw_cnt >= bbr_full_bw_cnt; +} + +/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */ +static u32 bbr_max_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return minmax_get(&bbr->bw); +} + +/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */ +static u32 bbr_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk); +} + +/* Return rate in bytes per second, optionally with a gain. + * The order here is chosen carefully to avoid overflow of u64. This should + * work for input rates of up to 2.9Tbit/sec and gain of 2.89x. + */ +static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain) +{ + rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache); + rate *= gain; + rate >>= BBR_SCALE; + rate *= USEC_PER_SEC; + return rate >> BW_SCALE; +} + +/* Pace using current bw estimate and a gain factor. In order to help drive the + * network toward lower queues while maintaining high utilization and low + * latency, the average pacing rate aims to be slightly (~1%) lower than the + * estimated bandwidth. This is an important aspect of the design. In this + * implementation this slightly lower pacing rate is achieved implicitly by not + * including link-layer headers in the packet size used for the pacing rate. + */ +static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u64 rate = bw; + + rate = bbr_rate_bytes_per_sec(sk, rate, gain); + rate = min_t(u64, rate, sk->sk_max_pacing_rate); + if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate) + sk->sk_pacing_rate = rate; +} + +/* Return count of segments we want in the skbs we send, or 0 for default. */ +static u32 bbr_tso_segs_goal(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->tso_segs_goal; +} + +static void bbr_set_tso_segs_goal(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 min_segs; + + min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2; + bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs), + 0x7FU); +} + +/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */ +static void bbr_save_cwnd(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT) + bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */ + else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */ + bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd); +} + +static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (event == CA_EVENT_TX_START && tp->app_limited) { + bbr->idle_restart = 1; + /* Avoid pointless buffer overflows: pace at est. bw if we don't + * need more speed (we're restarting from idle and app-limited). + */ + if (bbr->mode == BBR_PROBE_BW) + bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT); + } +} + +/* Find target cwnd. Right-size the cwnd based on min RTT and the + * estimated bottleneck bandwidth: + * + * cwnd = bw * min_rtt * gain = BDP * gain + * + * The key factor, gain, controls the amount of queue. While a small gain + * builds a smaller queue, it becomes more vulnerable to noise in RTT + * measurements (e.g., delayed ACKs or other ACK compression effects). This + * noise may cause BBR to under-estimate the rate. + * + * To achieve full performance in high-speed paths, we budget enough cwnd to + * fit full-sized skbs in-flight on both end hosts to fully utilize the path: + * - one skb in sending host Qdisc, + * - one skb in sending host TSO/GSO engine + * - one skb being received by receiver host LRO/GRO/delayed-ACK engine + * Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because + * in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets, + * which allows 2 outstanding 2-packet sequences, to try to keep pipe + * full even with ACK-every-other-packet delayed ACKs. + */ +static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd; + u64 w; + + /* If we've never had a valid RTT sample, cap cwnd at the initial + * default. This should only happen when the connection is not using TCP + * timestamps and has retransmitted all of the SYN/SYNACK/data packets + * ACKed so far. In this case, an RTO can cut cwnd to 1, in which + * case we need to slow-start up toward something safe: TCP_INIT_CWND. + */ + if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */ + return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/ + + w = (u64)bw * bbr->min_rtt_us; + + /* Apply a gain to the given value, then remove the BW_SCALE shift. */ + cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT; + + /* Allow enough full-sized skbs in flight to utilize end systems. */ + cwnd += 3 * bbr->tso_segs_goal; + + /* Reduce delayed ACKs by rounding up cwnd to the next even number. */ + cwnd = (cwnd + 1) & ~1U; + + return cwnd; +} + +/* An optimization in BBR to reduce losses: On the first round of recovery, we + * follow the packet conservation principle: send P packets per P packets acked. + * After that, we slow-start and send at most 2*P packets per P packets acked. + * After recovery finishes, or upon undo, we restore the cwnd we had when + * recovery started (capped by the target cwnd based on estimated BDP). + * + * TODO(ycheng/ncardwell): implement a rate-based approach. + */ +static bool bbr_set_cwnd_to_recover_or_restore( + struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state; + u32 cwnd = tp->snd_cwnd; + + /* An ACK for P pkts should release at most 2*P packets. We do this + * in two steps. First, here we deduct the number of lost packets. + * Then, in bbr_set_cwnd() we slow start up toward the target cwnd. + */ + if (rs->losses > 0) + cwnd = max_t(s32, cwnd - rs->losses, 1); + + if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) { + /* Starting 1st round of Recovery, so do packet conservation. */ + bbr->packet_conservation = 1; + bbr->next_rtt_delivered = tp->delivered; /* start round now */ + /* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */ + cwnd = tcp_packets_in_flight(tp) + acked; + } else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) { + /* Exiting loss recovery; restore cwnd saved before recovery. */ + bbr->restore_cwnd = 1; + bbr->packet_conservation = 0; + } + bbr->prev_ca_state = state; + + if (bbr->restore_cwnd) { + /* Restore cwnd after exiting loss recovery or PROBE_RTT. */ + cwnd = max(cwnd, bbr->prior_cwnd); + bbr->restore_cwnd = 0; + } + + if (bbr->packet_conservation) { + *new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked); + return true; /* yes, using packet conservation */ + } + *new_cwnd = cwnd; + return false; +} + +/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss + * has drawn us down below target), or snap down to target if we're above it. + */ +static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs, + u32 acked, u32 bw, int gain) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd = 0, target_cwnd = 0; + + if (!acked) + return; + + if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd)) + goto done; + + /* If we're below target cwnd, slow start cwnd toward target cwnd. */ + target_cwnd = bbr_target_cwnd(sk, bw, gain); + if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */ + cwnd = min(cwnd + acked, target_cwnd); + else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND) + cwnd = cwnd + acked; + cwnd = max(cwnd, bbr_cwnd_min_target); + +done: + tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */ + if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */ + tp->snd_cwnd = max(tp->snd_cwnd >> 1, bbr_cwnd_min_target); +} + +/* End cycle phase if it's time and/or we hit the phase's in-flight target. */ +static bool bbr_is_next_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool is_full_length = + skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) > + bbr->min_rtt_us; + u32 inflight, bw; + + /* The pacing_gain of 1.0 paces at the estimated bw to try to fully + * use the pipe without increasing the queue. + */ + if (bbr->pacing_gain == BBR_UNIT) + return is_full_length; /* just use wall clock time */ + + inflight = rs->prior_in_flight; /* what was in-flight before ACK? */ + bw = bbr_max_bw(sk); + + /* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at + * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is + * small (e.g. on a LAN). We do not persist if packets are lost, since + * a path with small buffers may not hold that much. + */ + if (bbr->pacing_gain > BBR_UNIT) + return is_full_length && + (rs->losses || /* perhaps pacing_gain*BDP won't fit */ + inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain)); + + /* A pacing_gain < 1.0 tries to drain extra queue we added if bw + * probing didn't find more bw. If inflight falls to match BDP then we + * estimate queue is drained; persisting would underutilize the pipe. + */ + return is_full_length || + inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT); +} + +static void bbr_advance_cycle_phase(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1); + bbr->cycle_mstamp = tp->delivered_mstamp; + bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx]; +} + +/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */ +static void bbr_update_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw && + bbr_is_next_cycle_phase(sk, rs)) + bbr_advance_cycle_phase(sk); +} + +static void bbr_reset_startup_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_STARTUP; + bbr->pacing_gain = bbr_high_gain; + bbr->cwnd_gain = bbr_high_gain; +} + +static void bbr_reset_probe_bw_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_PROBE_BW; + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = bbr_cwnd_gain; + bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand); + bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */ +} + +static void bbr_reset_mode(struct sock *sk) +{ + if (!bbr_full_bw_reached(sk)) + bbr_reset_startup_mode(sk); + else + bbr_reset_probe_bw_mode(sk); +} + +/* Start a new long-term sampling interval. */ +static void bbr_reset_lt_bw_sampling_interval(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_last_stamp = tp->delivered_mstamp.stamp_jiffies; + bbr->lt_last_delivered = tp->delivered; + bbr->lt_last_lost = tp->lost; + bbr->lt_rtt_cnt = 0; +} + +/* Completely reset long-term bandwidth sampling. */ +static void bbr_reset_lt_bw_sampling(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_bw = 0; + bbr->lt_use_bw = 0; + bbr->lt_is_sampling = false; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Long-term bw sampling interval is done. Estimate whether we're policed. */ +static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 diff; + + if (bbr->lt_bw) { /* do we have bw from a previous interval? */ + /* Is new bw close to the lt_bw from the previous interval? */ + diff = abs(bw - bbr->lt_bw); + if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) || + (bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <= + bbr_lt_bw_diff)) { + /* All criteria are met; estimate we're policed. */ + bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */ + bbr->lt_use_bw = 1; + bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */ + bbr->lt_rtt_cnt = 0; + return; + } + } + bbr->lt_bw = bw; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of + * Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and + * explicitly models their policed rate, to reduce unnecessary losses. We + * estimate that we're policed if we see 2 consecutive sampling intervals with + * consistent throughput and high packet loss. If we think we're being policed, + * set lt_bw to the "long-term" average delivery rate from those 2 intervals. + */ +static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 lost, delivered; + u64 bw; + s32 t; + + if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */ + if (bbr->mode == BBR_PROBE_BW && bbr->round_start && + ++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) { + bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */ + bbr_reset_probe_bw_mode(sk); /* restart gain cycling */ + } + return; + } + + /* Wait for the first loss before sampling, to let the policer exhaust + * its tokens and estimate the steady-state rate allowed by the policer. + * Starting samples earlier includes bursts that over-estimate the bw. + */ + if (!bbr->lt_is_sampling) { + if (!rs->losses) + return; + bbr_reset_lt_bw_sampling_interval(sk); + bbr->lt_is_sampling = true; + } + + /* To avoid underestimates, reset sampling if we run out of data. */ + if (rs->is_app_limited) { + bbr_reset_lt_bw_sampling(sk); + return; + } + + if (bbr->round_start) + bbr->lt_rtt_cnt++; /* count round trips in this interval */ + if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts) + return; /* sampling interval needs to be longer */ + if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) { + bbr_reset_lt_bw_sampling(sk); /* interval is too long */ + return; + } + + /* End sampling interval when a packet is lost, so we estimate the + * policer tokens were exhausted. Stopping the sampling before the + * tokens are exhausted under-estimates the policed rate. + */ + if (!rs->losses) + return; + + /* Calculate packets lost and delivered in sampling interval. */ + lost = tp->lost - bbr->lt_last_lost; + delivered = tp->delivered - bbr->lt_last_delivered; + /* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */ + if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered) + return; + + /* Find average delivery rate in this sampling interval. */ + t = (s32)(tp->delivered_mstamp.stamp_jiffies - bbr->lt_last_stamp); + if (t < 1) + return; /* interval is less than one jiffy, so wait */ + t = jiffies_to_usecs(t); + /* Interval long enough for jiffies_to_usecs() to return a bogus 0? */ + if (t < 1) { + bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */ + return; + } + bw = (u64)delivered * BW_UNIT; + do_div(bw, t); + bbr_lt_bw_interval_done(sk, bw); +} + +/* Estimate the bandwidth based on how fast packets are delivered */ +static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->round_start = 0; + if (rs->delivered < 0 || rs->interval_us <= 0) + return; /* Not a valid observation */ + + /* See if we've reached the next RTT */ + if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) { + bbr->next_rtt_delivered = tp->delivered; + bbr->rtt_cnt++; + bbr->round_start = 1; + bbr->packet_conservation = 0; + } + + bbr_lt_bw_sampling(sk, rs); + + /* Divide delivered by the interval to find a (lower bound) bottleneck + * bandwidth sample. Delivered is in packets and interval_us in uS and + * ratio will be <<1 for most connections. So delivered is first scaled. + */ + bw = (u64)rs->delivered * BW_UNIT; + do_div(bw, rs->interval_us); + + /* If this sample is application-limited, it is likely to have a very + * low delivered count that represents application behavior rather than + * the available network rate. Such a sample could drag down estimated + * bw, causing needless slow-down. Thus, to continue to send at the + * last measured network rate, we filter out app-limited samples unless + * they describe the path bw at least as well as our bw model. + * + * So the goal during app-limited phase is to proceed with the best + * network rate no matter how long. We automatically leave this + * phase when app writes faster than the network can deliver :) + */ + if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) { + /* Incorporate new sample into our max bw filter. */ + minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw); + } +} + +/* Estimate when the pipe is full, using the change in delivery rate: BBR + * estimates that STARTUP filled the pipe if the estimated bw hasn't changed by + * at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited + * rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the + * higher rwin, 3: we get higher delivery rate samples. Or transient + * cross-traffic or radio noise can go away. CUBIC Hystart shares a similar + * design goal, but uses delay and inter-ACK spacing instead of bandwidth. + */ +static void bbr_check_full_bw_reached(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw_thresh; + + if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited) + return; + + bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE; + if (bbr_max_bw(sk) >= bw_thresh) { + bbr->full_bw = bbr_max_bw(sk); + bbr->full_bw_cnt = 0; + return; + } + ++bbr->full_bw_cnt; +} + +/* If pipe is probably full, drain the queue and then enter steady-state. */ +static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) { + bbr->mode = BBR_DRAIN; /* drain queue we created */ + bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */ + bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */ + } /* fall through to check if in-flight is already small: */ + if (bbr->mode == BBR_DRAIN && + tcp_packets_in_flight(tcp_sk(sk)) <= + bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT)) + bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */ +} + +/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and + * periodically drain the bottleneck queue, to converge to measure the true + * min_rtt (unloaded propagation delay). This allows the flows to keep queues + * small (reducing queuing delay and packet loss) and achieve fairness among + * BBR flows. + * + * The min_rtt filter window is 10 seconds. When the min_rtt estimate expires, + * we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets. + * After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed + * round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and + * re-enter the previous mode. BBR uses 200ms to approximately bound the + * performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s). + * + * Note that flows need only pay 2% if they are busy sending over the last 10 + * seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have + * natural silences or low-rate periods within 10 seconds where the rate is low + * enough for long enough to drain its queue in the bottleneck. We pick up + * these min RTT measurements opportunistically with our min_rtt filter. :-) + */ +static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool filter_expired; + + /* Track min RTT seen in the min_rtt_win_sec filter window: */ + // as above BBR_Structure define: "min_rtt_win_sec = 5 seconds" + filter_expired = after(tcp_time_stamp, + bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ); + if (rs->rtt_us >= 0 && + (rs->rtt_us <= bbr->min_rtt_us || filter_expired)) { + bbr->min_rtt_us = rs->rtt_us; + bbr->min_rtt_stamp = tcp_time_stamp; + } + + if (bbr_probe_rtt_mode_ms > 0 && filter_expired && + !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) { + bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */ + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = BBR_UNIT; + bbr_save_cwnd(sk); /* note cwnd so we can restore it */ + bbr->probe_rtt_done_stamp = 0; + } + + if (bbr->mode == BBR_PROBE_RTT) { + /* Ignore low rate samples during this mode. */ + tp->app_limited = + (tp->delivered + tcp_packets_in_flight(tp)) ? : 1; + /* Maintain min packets in flight for max(200 ms, 1 round). */ + if (!bbr->probe_rtt_done_stamp && + tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) { + bbr->probe_rtt_done_stamp = tcp_time_stamp + + msecs_to_jiffies(bbr_probe_rtt_mode_ms >> 1); + bbr->probe_rtt_round_done = 0; + bbr->next_rtt_delivered = tp->delivered; + } else if (bbr->probe_rtt_done_stamp) { + if (bbr->round_start) + bbr->probe_rtt_round_done = 1; + if (bbr->probe_rtt_round_done && + after(tcp_time_stamp, bbr->probe_rtt_done_stamp)) { + bbr->min_rtt_stamp = tcp_time_stamp; + bbr->restore_cwnd = 1; /* snap to prior_cwnd */ + bbr_reset_mode(sk); + } + } + } + bbr->idle_restart = 0; +} + +static void bbr_update_model(struct sock *sk, const struct rate_sample *rs) +{ + bbr_update_bw(sk, rs); + bbr_update_cycle_phase(sk, rs); + bbr_check_full_bw_reached(sk, rs); + bbr_check_drain(sk, rs); + bbr_update_min_rtt(sk, rs); +} + +static void bbr_main(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw; + + bbr_update_model(sk, rs); + + bw = bbr_bw(sk); + bbr_set_pacing_rate(sk, bw, bbr->pacing_gain); + bbr_set_tso_segs_goal(sk); + bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain); +} + +static void bbr_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->prior_cwnd = 0; + bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */ + bbr->rtt_cnt = 0; + bbr->next_rtt_delivered = 0; + bbr->prev_ca_state = TCP_CA_Open; + bbr->packet_conservation = 0; + + bbr->probe_rtt_done_stamp = 0; + bbr->probe_rtt_round_done = 0; + bbr->min_rtt_us = tcp_min_rtt(tp); + bbr->min_rtt_stamp = tcp_time_stamp; + + minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */ + + /* Initialize pacing rate to: high_gain * init_cwnd / RTT. */ + bw = (u64)tp->snd_cwnd * BW_UNIT; + do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC); + sk->sk_pacing_rate = 0; /* force an update of sk_pacing_rate */ + bbr_set_pacing_rate(sk, bw, bbr_high_gain); + + bbr->restore_cwnd = 0; + bbr->round_start = 0; + bbr->idle_restart = 0; + bbr->full_bw = 0; + bbr->full_bw_cnt = 0; + bbr->cycle_mstamp.v64 = 0; + bbr->cycle_idx = 0; + bbr_reset_lt_bw_sampling(sk); + bbr_reset_startup_mode(sk); +} + +static u32 bbr_sndbuf_expand(struct sock *sk) +{ + /* Provision 3 * cwnd since BBR may slow-start even during recovery. */ + return 3; +} + +/* In theory BBR does not need to undo the cwnd since it does not + * always reduce cwnd on losses (see bbr_main()). Keep it for now. + */ +static u32 bbr_undo_cwnd(struct sock *sk) +{ + return tcp_sk(sk)->snd_cwnd; +} + +/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */ +static u32 bbr_ssthresh(struct sock *sk) +{ + bbr_save_cwnd(sk); + return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */ +} + +static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr, union tcp_cc_info *info) +{ + if (ext & (1 << (INET_DIAG_BBRINFO - 1)) || + ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw = bbr_bw(sk); + bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE; + memset(&info->bbr, 0, sizeof(info->bbr)); + info->bbr.bbr_bw_lo = (u32)bw; + info->bbr.bbr_bw_hi = (u32)(bw >> 32); + info->bbr.bbr_min_rtt = bbr->min_rtt_us; + info->bbr.bbr_pacing_gain = bbr->pacing_gain; + info->bbr.bbr_cwnd_gain = bbr->cwnd_gain; + *attr = INET_DIAG_BBRINFO; + return sizeof(info->bbr); + } + return 0; +} + +static void bbr_set_state(struct sock *sk, u8 new_state) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (new_state == TCP_CA_Loss) { + struct rate_sample rs = { .losses = 1 }; + + bbr->prev_ca_state = TCP_CA_Loss; + bbr->full_bw = 0; + bbr->round_start = 1; /* treat RTO like end of a round */ + bbr_lt_bw_sampling(sk, &rs); + } +} + +static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = { + .flags = TCP_CONG_NON_RESTRICTED, + .name = "nanqinlang", + .owner = THIS_MODULE, + .init = bbr_init, + .cong_control = bbr_main, + .sndbuf_expand = bbr_sndbuf_expand, + .undo_cwnd = bbr_undo_cwnd, + .cwnd_event = bbr_cwnd_event, + .ssthresh = bbr_ssthresh, + .tso_segs_goal = bbr_tso_segs_goal, + .get_info = bbr_get_info, + .set_state = bbr_set_state, +}; + +static int __init bbr_register(void) +{ + BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_bbr_cong_ops); +} + +static void __exit bbr_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_bbr_cong_ops); +} + +module_init(bbr_register); +module_exit(bbr_unregister); + +MODULE_AUTHOR("Van Jacobson "); +MODULE_AUTHOR("Neal Cardwell "); +MODULE_AUTHOR("Yuchung Cheng "); +MODULE_AUTHOR("Soheil Hassas Yeganeh "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)"); +MODULE_AUTHOR("Nanqinlang "); \ No newline at end of file diff --git a/bbr/tcp_tsunami.c b/bbr/tcp_tsunami.c new file mode 100644 index 0000000..4d6ffe8 --- /dev/null +++ b/bbr/tcp_tsunami.c @@ -0,0 +1,905 @@ +/* Bottleneck Bandwidth and RTT (BBR) congestion control + * + * BBR congestion control computes the sending rate based on the delivery + * rate (throughput) estimated from ACKs. In a nutshell: + * + * On each ACK, update our model of the network path: + * bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips) + * min_rtt = windowed_min(rtt, 10 seconds) + * pacing_rate = pacing_gain * bottleneck_bandwidth + * cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4) + * + * The core algorithm does not react directly to packet losses or delays, + * although BBR may adjust the size of next send per ACK when loss is + * observed, or adjust the sending rate if it estimates there is a + * traffic policer, in order to keep the drop rate reasonable. + * + * BBR is described in detail in: + * "BBR: Congestion-Based Congestion Control", + * Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, + * Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016. + * + * There is a public e-mail list for discussing BBR development and testing: + * https://groups.google.com/forum/#!forum/bbr-dev + * + * NOTE: BBR *must* be used with the fq qdisc ("man tc-fq") with pacing enabled, + * since pacing is integral to the BBR design and implementation. + * BBR without pacing would not function properly, and may incur unnecessary + * high packet loss rates. + */ +#include +#include +#include +#include +#include +#include + +/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth + * estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps. + * This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32. + * Since the minimum window is >=4 packets, the lower bound isn't + * an issue. The upper bound isn't an issue with existing technologies. + */ +#define BW_SCALE 24 +#define BW_UNIT (1 << BW_SCALE) + +#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */ +#define BBR_UNIT (1 << BBR_SCALE) + +/* BBR has the following modes for deciding how fast to send: */ +enum bbr_mode { + BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ + BBR_DRAIN, /* drain any queue created during startup */ + BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ + BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */ +}; + +/* BBR congestion control block */ +struct bbr { + u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */ +//deprecated u32 rtt_us; + u32 min_rtt_stamp; /* timestamp of min_rtt_us */ + u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */ +//deprecated struct minmax max_rtt; + struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */ + u32 rtt_cnt; /* count of packet-timed rounds elapsed */ + u32 next_rtt_delivered; /* scb->tx.delivered at end of round */ + struct skb_mstamp cycle_mstamp; /* time of this cycle phase start */ + u32 mode:3, /* current bbr_mode in state machine */ + prev_ca_state:3, /* CA state on previous ACK */ + packet_conservation:1, /* use packet conservation? */ + restore_cwnd:1, /* decided to revert cwnd to old value */ + round_start:1, /* start of packet-timed tx->ack round? */ + tso_segs_goal:7, /* segments we want in each skb we send */ + idle_restart:1, /* restarting after idle? */ + probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */ + unused:5, + lt_is_sampling:1, /* taking long-term ("LT") samples now? */ + lt_rtt_cnt:7, /* round trips in long-term interval */ + lt_use_bw:1; /* use lt_bw as our bw estimate? */ + u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */ + u32 lt_last_delivered; /* LT intvl start: tp->delivered */ + u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */ + u32 lt_last_lost; /* LT intvl start: tp->lost */ + u32 pacing_gain:10, /* current gain for setting pacing rate */ + cwnd_gain:10, /* current gain for setting cwnd */ + full_bw_cnt:3, /* number of rounds without large bw gains */ + cycle_idx:3, /* current index in pacing_gain cycle array */ + unused_b:6; + u32 prior_cwnd; /* prior cwnd upon entering loss recovery */ + u32 full_bw; /* recent bw, to estimate if pipe is full */ +}; + +#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */ + +/* Window length of bw filter (in rounds): */ +static const int bbr_bw_rtts = CYCLE_LEN + 7; +/* Window length of min_rtt filter (in sec): */ +static const u32 bbr_min_rtt_win_sec = 20; +/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */ +static const u32 bbr_probe_rtt_mode_ms = 200; +/* Skip TSO below the following bandwidth (bits/sec): */ +static const int bbr_min_tso_rate = 1200000; + +/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain + * that will allow a smoothly increasing pacing rate that will double each RTT + * and send the same number of packets per RTT that an un-paced, slow-starting + * Reno or CUBIC flow would: + */ +static const int bbr_high_gain = BBR_UNIT * 2885 / 1000 + 1; +/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain + * the queue created in BBR_STARTUP in a single round: + */ +static const int bbr_drain_gain = BBR_UNIT * 1200 / 2885; +/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */ +static const int bbr_cwnd_gain = BBR_UNIT * 2; +/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */ +static const int bbr_pacing_gain[] = { + BBR_UNIT * 3 / 2, /* probe for more available bw */ + BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */ + BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, /* cruise at 1.0*bw to utilize pipe, */ + BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8 /* without creating excess queue... */ +}; +/* Randomize the starting gain cycling phase over N phases: */ +static const u32 bbr_cycle_rand = 7; + +/* Try to keep at least this many packets in flight, if things go smoothly. For + * smooth functioning, a sliding window protocol ACKing every other packet + * needs at least 4 packets in flight: + */ +static const u32 bbr_cwnd_min_target = 4; + +/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */ +/* If bw has increased significantly (1.25x), there may be more bw available: */ +static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4; +/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */ +static const u32 bbr_full_bw_cnt = 3; + +/* "long-term" ("LT") bandwidth estimator parameters... */ +/* The minimum number of rounds in an LT bw sampling interval: */ +static const u32 bbr_lt_intvl_min_rtts = 4; +/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */ +static const u32 bbr_lt_loss_thresh = 50; +/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */ +static const u32 bbr_lt_bw_ratio = BBR_UNIT / 8; +/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */ +static const u32 bbr_lt_bw_diff = 4000 / 8; +/* If we estimate we're policed, use lt_bw for this many round trips: */ +static const u32 bbr_lt_bw_max_rtts = 48; + +/* Do we estimate that STARTUP filled the pipe? */ +static bool bbr_full_bw_reached(const struct sock *sk) +{ + const struct bbr *bbr = inet_csk_ca(sk); + + return bbr->full_bw_cnt >= bbr_full_bw_cnt; +} + +/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */ +static u32 bbr_max_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return minmax_get(&bbr->bw); +} + +/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */ +static u32 bbr_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk); +} + +/* Return rate in bytes per second, optionally with a gain. + * The order here is chosen carefully to avoid overflow of u64. This should + * work for input rates of up to 2.9Tbit/sec and gain of 2.89x. + */ +static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain) +{ + rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache); + rate *= gain; + rate >>= BBR_SCALE; + rate *= USEC_PER_SEC; + return rate >> BW_SCALE; +} + +/* Pace using current bw estimate and a gain factor. In order to help drive the + * network toward lower queues while maintaining high utilization and low + * latency, the average pacing rate aims to be slightly (~1%) lower than the + * estimated bandwidth. This is an important aspect of the design. In this + * implementation this slightly lower pacing rate is achieved implicitly by not + * including link-layer headers in the packet size used for the pacing rate. + */ +static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u64 rate = bw; + + rate = bbr_rate_bytes_per_sec(sk, rate, gain); + rate = min_t(u64, rate, sk->sk_max_pacing_rate); + if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate) + sk->sk_pacing_rate = rate; +} + +/* Return count of segments we want in the skbs we send, or 0 for default. */ +static u32 bbr_tso_segs_goal(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->tso_segs_goal; +} + +static void bbr_set_tso_segs_goal(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 min_segs; + + min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2; + bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs), + 0x7FU); +} + +/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */ +static void bbr_save_cwnd(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT) + bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */ + else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */ + bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd); +} + +static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (event == CA_EVENT_TX_START && tp->app_limited) { + bbr->idle_restart = 1; + /* Avoid pointless buffer overflows: pace at est. bw if we don't + * need more speed (we're restarting from idle and app-limited). + */ + if (bbr->mode == BBR_PROBE_BW) + bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT); + } +} + +/* Find target cwnd. Right-size the cwnd based on min RTT and the + * estimated bottleneck bandwidth: + * + * cwnd = bw * min_rtt * gain = BDP * gain + * + * The key factor, gain, controls the amount of queue. While a small gain + * builds a smaller queue, it becomes more vulnerable to noise in RTT + * measurements (e.g., delayed ACKs or other ACK compression effects). This + * noise may cause BBR to under-estimate the rate. + * + * To achieve full performance in high-speed paths, we budget enough cwnd to + * fit full-sized skbs in-flight on both end hosts to fully utilize the path: + * - one skb in sending host Qdisc, + * - one skb in sending host TSO/GSO engine + * - one skb being received by receiver host LRO/GRO/delayed-ACK engine + * Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because + * in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets, + * which allows 2 outstanding 2-packet sequences, to try to keep pipe + * full even with ACK-every-other-packet delayed ACKs. + */ +static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd; + u64 w; + + /* If we've never had a valid RTT sample, cap cwnd at the initial + * default. This should only happen when the connection is not using TCP + * timestamps and has retransmitted all of the SYN/SYNACK/data packets + * ACKed so far. In this case, an RTO can cut cwnd to 1, in which + * case we need to slow-start up toward something safe: TCP_INIT_CWND. + */ + if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */ + return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/ + + w = (u64)bw * bbr->min_rtt_us; + + /* Apply a gain to the given value, then remove the BW_SCALE shift. */ + cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT; + + /* Allow enough full-sized skbs in flight to utilize end systems. */ + cwnd += 3 * bbr->tso_segs_goal; + + /* Reduce delayed ACKs by rounding up cwnd to the next even number. */ + cwnd = (cwnd + 1) & ~1U; + + return cwnd; +} + +/* An optimization in BBR to reduce losses: On the first round of recovery, we + * follow the packet conservation principle: send P packets per P packets acked. + * After that, we slow-start and send at most 2*P packets per P packets acked. + * After recovery finishes, or upon undo, we restore the cwnd we had when + * recovery started (capped by the target cwnd based on estimated BDP). + * + * TODO(ycheng/ncardwell): implement a rate-based approach. + */ +static bool bbr_set_cwnd_to_recover_or_restore( + struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state; + u32 cwnd = tp->snd_cwnd; + + /* An ACK for P pkts should release at most 2*P packets. We do this + * in two steps. First, here we deduct the number of lost packets. + * Then, in bbr_set_cwnd() we slow start up toward the target cwnd. + */ + if (rs->losses > 0) + cwnd = max_t(s32, cwnd - rs->losses, 1); + + if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) { + /* Starting 1st round of Recovery, so do packet conservation. */ + bbr->packet_conservation = 1; + bbr->next_rtt_delivered = tp->delivered; /* start round now */ + /* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */ + cwnd = tcp_packets_in_flight(tp) + acked; + } else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) { + /* Exiting loss recovery; restore cwnd saved before recovery. */ + bbr->restore_cwnd = 1; + bbr->packet_conservation = 0; + } + bbr->prev_ca_state = state; + + if (bbr->restore_cwnd) { + /* Restore cwnd after exiting loss recovery or PROBE_RTT. */ + cwnd = max(cwnd, bbr->prior_cwnd); + bbr->restore_cwnd = 0; + } + + if (bbr->packet_conservation) { + *new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked); + return true; /* yes, using packet conservation */ + } + *new_cwnd = cwnd; + return false; +} + +/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss + * has drawn us down below target), or snap down to target if we're above it. + */ +static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs, + u32 acked, u32 bw, int gain) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd = 0, target_cwnd = 0; + + if (!acked) + return; + + if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd)) + goto done; + + /* If we're below target cwnd, slow start cwnd toward target cwnd. */ + target_cwnd = bbr_target_cwnd(sk, bw, gain); + if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */ + cwnd = min(cwnd + acked, target_cwnd); + else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND) + cwnd = cwnd + acked; + cwnd = max(cwnd, bbr_cwnd_min_target); + +done: + tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */ + if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */ + tp->snd_cwnd = max(tp->snd_cwnd >> 1, bbr_cwnd_min_target); +} + +/* End cycle phase if it's time and/or we hit the phase's in-flight target. */ +static bool bbr_is_next_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool is_full_length = + skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) > + bbr->min_rtt_us; + u32 inflight, bw; + + /* The pacing_gain of 1.0 paces at the estimated bw to try to fully + * use the pipe without increasing the queue. + */ + if (bbr->pacing_gain == BBR_UNIT) + return is_full_length; /* just use wall clock time */ + + inflight = rs->prior_in_flight; /* what was in-flight before ACK? */ + bw = bbr_max_bw(sk); + + /* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at + * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is + * small (e.g. on a LAN). We do not persist if packets are lost, since + * a path with small buffers may not hold that much. + */ + if (bbr->pacing_gain > BBR_UNIT) + return is_full_length && + (rs->losses || /* perhaps pacing_gain*BDP won't fit */ + inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain)); + + /* A pacing_gain < 1.0 tries to drain extra queue we added if bw + * probing didn't find more bw. If inflight falls to match BDP then we + * estimate queue is drained; persisting would underutilize the pipe. + */ + return is_full_length || + inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT); +} + +static void bbr_advance_cycle_phase(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1); + bbr->cycle_mstamp = tp->delivered_mstamp; + bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx]; +} + +/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */ +static void bbr_update_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw && + bbr_is_next_cycle_phase(sk, rs)) + bbr_advance_cycle_phase(sk); +} + +static void bbr_reset_startup_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_STARTUP; + bbr->pacing_gain = bbr_high_gain; + bbr->cwnd_gain = bbr_high_gain; +} + +static void bbr_reset_probe_bw_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_PROBE_BW; + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = bbr_cwnd_gain; + bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand); + bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */ +} + +static void bbr_reset_mode(struct sock *sk) +{ + if (!bbr_full_bw_reached(sk)) + bbr_reset_startup_mode(sk); + else + bbr_reset_probe_bw_mode(sk); +} + +/* Start a new long-term sampling interval. */ +static void bbr_reset_lt_bw_sampling_interval(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_last_stamp = tp->delivered_mstamp.stamp_jiffies; + bbr->lt_last_delivered = tp->delivered; + bbr->lt_last_lost = tp->lost; + bbr->lt_rtt_cnt = 0; +} + +/* Completely reset long-term bandwidth sampling. */ +static void bbr_reset_lt_bw_sampling(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_bw = 0; + bbr->lt_use_bw = 0; + bbr->lt_is_sampling = false; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Long-term bw sampling interval is done. Estimate whether we're policed. */ +static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 diff; + + if (bbr->lt_bw) { /* do we have bw from a previous interval? */ + /* Is new bw close to the lt_bw from the previous interval? */ + diff = abs(bw - bbr->lt_bw); + if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) || + (bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <= + bbr_lt_bw_diff)) { + /* All criteria are met; estimate we're policed. */ + bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */ + bbr->lt_use_bw = 1; + bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */ + bbr->lt_rtt_cnt = 0; + return; + } + } + bbr->lt_bw = bw; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of + * Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and + * explicitly models their policed rate, to reduce unnecessary losses. We + * estimate that we're policed if we see 2 consecutive sampling intervals with + * consistent throughput and high packet loss. If we think we're being policed, + * set lt_bw to the "long-term" average delivery rate from those 2 intervals. + */ +static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 lost, delivered; + u64 bw; + s32 t; + + if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */ + if (bbr->mode == BBR_PROBE_BW && bbr->round_start && + ++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) { + bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */ + bbr_reset_probe_bw_mode(sk); /* restart gain cycling */ + } + return; + } + + /* Wait for the first loss before sampling, to let the policer exhaust + * its tokens and estimate the steady-state rate allowed by the policer. + * Starting samples earlier includes bursts that over-estimate the bw. + */ + if (!bbr->lt_is_sampling) { + if (!rs->losses) + return; + bbr_reset_lt_bw_sampling_interval(sk); + bbr->lt_is_sampling = true; + } + + /* To avoid underestimates, reset sampling if we run out of data. */ + if (rs->is_app_limited) { + bbr_reset_lt_bw_sampling(sk); + return; + } + + if (bbr->round_start) + bbr->lt_rtt_cnt++; /* count round trips in this interval */ + if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts) + return; /* sampling interval needs to be longer */ + if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) { + bbr_reset_lt_bw_sampling(sk); /* interval is too long */ + return; + } + + /* End sampling interval when a packet is lost, so we estimate the + * policer tokens were exhausted. Stopping the sampling before the + * tokens are exhausted under-estimates the policed rate. + */ + if (!rs->losses) + return; + + /* Calculate packets lost and delivered in sampling interval. */ + lost = tp->lost - bbr->lt_last_lost; + delivered = tp->delivered - bbr->lt_last_delivered; + /* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */ + if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered) + return; + + /* Find average delivery rate in this sampling interval. */ + t = (s32)(tp->delivered_mstamp.stamp_jiffies - bbr->lt_last_stamp); + if (t < 1) + return; /* interval is less than one jiffy, so wait */ + t = jiffies_to_usecs(t); + /* Interval long enough for jiffies_to_usecs() to return a bogus 0? */ + if (t < 1) { + bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */ + return; + } + bw = (u64)delivered * BW_UNIT; + do_div(bw, t); + bbr_lt_bw_interval_done(sk, bw); +} + +/* Estimate the bandwidth based on how fast packets are delivered */ +static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->round_start = 0; + if (rs->delivered < 0 || rs->interval_us <= 0) + return; /* Not a valid observation */ + + /* See if we've reached the next RTT */ + if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) { + bbr->next_rtt_delivered = tp->delivered; + bbr->rtt_cnt++; + bbr->round_start = 1; + bbr->packet_conservation = 0; + } + + bbr_lt_bw_sampling(sk, rs); + + /* Divide delivered by the interval to find a (lower bound) bottleneck + * bandwidth sample. Delivered is in packets and interval_us in uS and + * ratio will be <<1 for most connections. So delivered is first scaled. + */ + bw = (u64)rs->delivered * BW_UNIT; + do_div(bw, rs->interval_us); + + /* If this sample is application-limited, it is likely to have a very + * low delivered count that represents application behavior rather than + * the available network rate. Such a sample could drag down estimated + * bw, causing needless slow-down. Thus, to continue to send at the + * last measured network rate, we filter out app-limited samples unless + * they describe the path bw at least as well as our bw model. + * + * So the goal during app-limited phase is to proceed with the best + * network rate no matter how long. We automatically leave this + * phase when app writes faster than the network can deliver :) + */ + if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) { + /* Incorporate new sample into our max bw filter. */ + minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw); + } +} + +/* Estimate when the pipe is full, using the change in delivery rate: BBR + * estimates that STARTUP filled the pipe if the estimated bw hasn't changed by + * at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited + * rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the + * higher rwin, 3: we get higher delivery rate samples. Or transient + * cross-traffic or radio noise can go away. CUBIC Hystart shares a similar + * design goal, but uses delay and inter-ACK spacing instead of bandwidth. + */ +static void bbr_check_full_bw_reached(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw_thresh; + + if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited) + return; + + bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE; + if (bbr_max_bw(sk) >= bw_thresh) { + bbr->full_bw = bbr_max_bw(sk); + bbr->full_bw_cnt = 0; + return; + } + ++bbr->full_bw_cnt; +} + +/* If pipe is probably full, drain the queue and then enter steady-state. */ +static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) { + bbr->mode = BBR_DRAIN; /* drain queue we created */ + bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */ + bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */ + } /* fall through to check if in-flight is already small: */ + if (bbr->mode == BBR_DRAIN && + tcp_packets_in_flight(tcp_sk(sk)) <= + bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT)) + bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */ +} + +/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and + * periodically drain the bottleneck queue, to converge to measure the true + * min_rtt (unloaded propagation delay). This allows the flows to keep queues + * small (reducing queuing delay and packet loss) and achieve fairness among + * BBR flows. + * + * The min_rtt filter window is 10 seconds. When the min_rtt estimate expires, + * we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets. + * After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed + * round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and + * re-enter the previous mode. BBR uses 200ms to approximately bound the + * performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s). + * + * Note that flows need only pay 2% if they are busy sending over the last 10 + * seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have + * natural silences or low-rate periods within 10 seconds where the rate is low + * enough for long enough to drain its queue in the bottleneck. We pick up + * these min RTT measurements opportunistically with our min_rtt filter. :-) + */ +static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); +//deprecated u32 rtt_prior = 0; + bool filter_expired; + + /* Track min RTT seen in the min_rtt_win_sec filter window: */ + filter_expired = after(tcp_time_stamp, + bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ); + if (rs->rtt_us >= 0 && + (rs->rtt_us <= bbr->min_rtt_us || filter_expired)) { + bbr->min_rtt_us = rs->rtt_us; + bbr->min_rtt_stamp = tcp_time_stamp; +//deprecated bbr->rtt_us = rs->rtt_us; + } +//deprecated bbr->rtt_us = rs->rtt_us; +//deprecated rtt_prior = minmax_get(&bbr->max_rtt); +//deprecated bbr->rtt_us = min(bbr->rtt_us, rtt_prior); + +//deprecated minmax_running_max(&bbr->max_rtt, bbr_bw_rtts, bbr->rtt_cnt, rs->rtt_us); + + if (bbr_probe_rtt_mode_ms > 0 && filter_expired && + !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) { + bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */ + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = BBR_UNIT; + bbr_save_cwnd(sk); /* note cwnd so we can restore it */ + bbr->probe_rtt_done_stamp = 0; + } + + if (bbr->mode == BBR_PROBE_RTT) { + /* Ignore low rate samples during this mode. */ + tp->app_limited = + (tp->delivered + tcp_packets_in_flight(tp)) ? : 1; + /* Maintain min packets in flight for max(200 ms, 1 round). */ + if (!bbr->probe_rtt_done_stamp && + tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) { + bbr->probe_rtt_done_stamp = tcp_time_stamp + + msecs_to_jiffies(bbr_probe_rtt_mode_ms >> 1); + bbr->probe_rtt_round_done = 0; + bbr->next_rtt_delivered = tp->delivered; + } else if (bbr->probe_rtt_done_stamp) { + if (bbr->round_start) + bbr->probe_rtt_round_done = 1; + if (bbr->probe_rtt_round_done && + after(tcp_time_stamp, bbr->probe_rtt_done_stamp)) { + bbr->min_rtt_stamp = tcp_time_stamp; + bbr->restore_cwnd = 1; /* snap to prior_cwnd */ + bbr_reset_mode(sk); + } + } + } + bbr->idle_restart = 0; +} + +static void bbr_update_model(struct sock *sk, const struct rate_sample *rs) +{ + bbr_update_bw(sk, rs); + bbr_update_cycle_phase(sk, rs); + bbr_check_full_bw_reached(sk, rs); + bbr_check_drain(sk, rs); + bbr_update_min_rtt(sk, rs); +} + +static void bbr_main(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw; + + bbr_update_model(sk, rs); + + bw = bbr_bw(sk); + bbr_set_pacing_rate(sk, bw, bbr->pacing_gain); + bbr_set_tso_segs_goal(sk); + bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain); +} + +static void bbr_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->prior_cwnd = 0; + bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */ + bbr->rtt_cnt = 0; + bbr->next_rtt_delivered = 0; + bbr->prev_ca_state = TCP_CA_Open; + bbr->packet_conservation = 0; + + bbr->probe_rtt_done_stamp = 0; + bbr->probe_rtt_round_done = 0; + bbr->min_rtt_us = tcp_min_rtt(tp); + bbr->min_rtt_stamp = tcp_time_stamp; + + minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */ + + /* Initialize pacing rate to: high_gain * init_cwnd / RTT. */ + bw = (u64)tp->snd_cwnd * BW_UNIT; + do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC); + sk->sk_pacing_rate = 0; /* force an update of sk_pacing_rate */ + bbr_set_pacing_rate(sk, bw, bbr_high_gain); + + bbr->restore_cwnd = 0; + bbr->round_start = 0; + bbr->idle_restart = 0; + bbr->full_bw = 0; + bbr->full_bw_cnt = 0; + bbr->cycle_mstamp.v64 = 0; + bbr->cycle_idx = 0; + bbr_reset_lt_bw_sampling(sk); + bbr_reset_startup_mode(sk); +} + +static u32 bbr_sndbuf_expand(struct sock *sk) +{ + /* Provision 3 * cwnd since BBR may slow-start even during recovery. */ + return 3; +} + +/* In theory BBR does not need to undo the cwnd since it does not + * always reduce cwnd on losses (see bbr_main()). Keep it for now. + */ +static u32 bbr_undo_cwnd(struct sock *sk) +{ + return tcp_sk(sk)->snd_cwnd; +} + +/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */ +static u32 bbr_ssthresh(struct sock *sk) +{ + bbr_save_cwnd(sk); + return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */ +} + +static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr, + union tcp_cc_info *info) +{ + if (ext & (1 << (INET_DIAG_BBRINFO - 1)) || + ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw = bbr_bw(sk); + + bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE; + memset(&info->bbr, 0, sizeof(info->bbr)); + info->bbr.bbr_bw_lo = (u32)bw; + info->bbr.bbr_bw_hi = (u32)(bw >> 32); + info->bbr.bbr_min_rtt = bbr->min_rtt_us; + info->bbr.bbr_pacing_gain = bbr->pacing_gain; + info->bbr.bbr_cwnd_gain = bbr->cwnd_gain; + *attr = INET_DIAG_BBRINFO; + return sizeof(info->bbr); + } + return 0; +} + +static void bbr_set_state(struct sock *sk, u8 new_state) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (new_state == TCP_CA_Loss) { + struct rate_sample rs = { .losses = 1 }; + + bbr->prev_ca_state = TCP_CA_Loss; + bbr->full_bw = 0; + bbr->round_start = 1; /* treat RTO like end of a round */ + bbr_lt_bw_sampling(sk, &rs); + } +} + +static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = { + .flags = TCP_CONG_NON_RESTRICTED, + .name = "tsunami", + .owner = THIS_MODULE, + .init = bbr_init, + .cong_control = bbr_main, + .sndbuf_expand = bbr_sndbuf_expand, + .undo_cwnd = bbr_undo_cwnd, + .cwnd_event = bbr_cwnd_event, + .ssthresh = bbr_ssthresh, + .tso_segs_goal = bbr_tso_segs_goal, + .get_info = bbr_get_info, + .set_state = bbr_set_state, +}; + +static int __init bbr_register(void) +{ + BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_bbr_cong_ops); +} + +static void __exit bbr_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_bbr_cong_ops); +} + +module_init(bbr_register); +module_exit(bbr_unregister); + +MODULE_AUTHOR("Van Jacobson "); +MODULE_AUTHOR("Neal Cardwell "); +MODULE_AUTHOR("Yuchung Cheng "); +MODULE_AUTHOR("Soheil Hassas Yeganeh "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)"); \ No newline at end of file diff --git a/bbrplus/centos/7/kernel-4.14.129-bbrplus.rpm b/bbrplus/centos/7/kernel-4.14.129-bbrplus.rpm new file mode 100644 index 0000000..1106e0c Binary files /dev/null and b/bbrplus/centos/7/kernel-4.14.129-bbrplus.rpm differ diff --git a/bbrplus/centos/7/kernel-headers-4.14.129-bbrplus.rpm b/bbrplus/centos/7/kernel-headers-4.14.129-bbrplus.rpm new file mode 100644 index 0000000..0c3a4be Binary files /dev/null and b/bbrplus/centos/7/kernel-headers-4.14.129-bbrplus.rpm differ diff --git a/bbrplus/debian-ubuntu/x64/linux-headers-4.14.129-bbrplus.deb b/bbrplus/debian-ubuntu/x64/linux-headers-4.14.129-bbrplus.deb new file mode 100644 index 0000000..5b3883f Binary files /dev/null and b/bbrplus/debian-ubuntu/x64/linux-headers-4.14.129-bbrplus.deb differ diff --git a/bbrplus/debian-ubuntu/x64/linux-image-4.14.129-bbrplus.deb b/bbrplus/debian-ubuntu/x64/linux-image-4.14.129-bbrplus.deb new file mode 100644 index 0000000..ba7ea94 Binary files /dev/null and b/bbrplus/debian-ubuntu/x64/linux-image-4.14.129-bbrplus.deb differ diff --git a/c7_tcp_react.sh b/c7_tcp_react.sh new file mode 100644 index 0000000..7af14c1 --- /dev/null +++ b/c7_tcp_react.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +kernel_version=`uname -r | awk -F "-" '{print $1}'` + +if [ ${kernel_version} = "5.9.6" ]; then + mkdir /root/tcp + cd /root/tcp + if [[ -e /root/tcp/tcp_react_rc2.ko ]]; then + insmod /root/tcp/tcp_react_rc2.ko + sysctl -w net.ipv4.tcp_congestion_control=react_rc2 + else + yum remove kernel-headers -y + wget -O kernel-headers-c7.rpm https://elrepo.org/linux/kernel/el7/x86_64/RPMS/kernel-ml-headers-5.9.6-1.el7.elrepo.x86_64.rpm + wget -O kernel-c7.rpm https://elrepo.org/linux/kernel/el7/x86_64/RPMS/kernel-ml-5.9.6-1.el7.elrepo.x86_64.rpm + wget -O kernel-devel-c7.rpm https://elrepo.org/linux/kernel/el7/x86_64/RPMS/kernel-ml-devel-5.9.6-1.el7.elrepo.x86_64.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + yum install -y kernel-devel-c7.rpm + yum -y install centos-release-scl-rh + yum -y install devtoolset-8-gcc make && source /opt/rh/devtoolset-8/enable && wget -O ./tcp_react_rc2.c https://github.com/ylx2016/Linux-NetSpeed/raw/master/tcp_react_rc2.c && echo "obj-m:=tcp_react_rc2.o" > Makefile && make -C /lib/modules/$(uname -r)/build M=`pwd` + insmod /root/tcp/tcp_react_rc2.ko + sysctl -w net.ipv4.tcp_congestion_control=react_rc2 + fi + cd + exit 0 +else + wget -O kernel-c7.rpm https://elrepo.org/linux/kernel/el7/x86_64/RPMS/kernel-ml-5.9.6-1.el7.elrepo.x86_64.rpm + yum install -y kernel-c7.rpm + grub2-mkconfig -o /boot/grub2/grub.cfg + grub2-set-default 0 + echo -e "重启切换下内核,确认当前内核是5.9.6再运行本脚本" +fi \ No newline at end of file diff --git a/cname b/cname new file mode 100644 index 0000000..1fc970d --- /dev/null +++ b/cname @@ -0,0 +1 @@ +github.000060000.xyz diff --git a/lotserver/centos/6/x32/kernel-2.6.32-504.rpm b/lotserver/centos/6/x32/kernel-2.6.32-504.rpm new file mode 100644 index 0000000..2293fab Binary files /dev/null and b/lotserver/centos/6/x32/kernel-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x32/kernel-devel-2.6.32-504.rpm b/lotserver/centos/6/x32/kernel-devel-2.6.32-504.rpm new file mode 100644 index 0000000..7a98344 Binary files /dev/null and b/lotserver/centos/6/x32/kernel-devel-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x32/kernel-firmware-2.6.32-504.rpm b/lotserver/centos/6/x32/kernel-firmware-2.6.32-504.rpm new file mode 100644 index 0000000..093ae3d Binary files /dev/null and b/lotserver/centos/6/x32/kernel-firmware-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x32/kernel-headers-2.6.32-504.rpm b/lotserver/centos/6/x32/kernel-headers-2.6.32-504.rpm new file mode 100644 index 0000000..ea69ae2 Binary files /dev/null and b/lotserver/centos/6/x32/kernel-headers-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x64/kernel-2.6.32-504.rpm b/lotserver/centos/6/x64/kernel-2.6.32-504.rpm new file mode 100644 index 0000000..61f7dce Binary files /dev/null and b/lotserver/centos/6/x64/kernel-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x64/kernel-devel-2.6.32-504.rpm b/lotserver/centos/6/x64/kernel-devel-2.6.32-504.rpm new file mode 100644 index 0000000..e7a4faf Binary files /dev/null and b/lotserver/centos/6/x64/kernel-devel-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x64/kernel-firmware-2.6.32-504.rpm b/lotserver/centos/6/x64/kernel-firmware-2.6.32-504.rpm new file mode 100644 index 0000000..093ae3d Binary files /dev/null and b/lotserver/centos/6/x64/kernel-firmware-2.6.32-504.rpm differ diff --git a/lotserver/centos/6/x64/kernel-headers-2.6.32-504.rpm b/lotserver/centos/6/x64/kernel-headers-2.6.32-504.rpm new file mode 100644 index 0000000..df54d89 Binary files /dev/null and b/lotserver/centos/6/x64/kernel-headers-2.6.32-504.rpm differ diff --git a/lotserver/centos/7/x64/kernel-3.10.0-327.rpm b/lotserver/centos/7/x64/kernel-3.10.0-327.rpm new file mode 100644 index 0000000..9a4f152 Binary files /dev/null and b/lotserver/centos/7/x64/kernel-3.10.0-327.rpm differ diff --git a/lotserver/centos/7/x64/kernel-3.10.0-957.rpm b/lotserver/centos/7/x64/kernel-3.10.0-957.rpm new file mode 100644 index 0000000..a2fc8e5 Binary files /dev/null and b/lotserver/centos/7/x64/kernel-3.10.0-957.rpm differ diff --git a/lotserver/centos/7/x64/kernel-4.11.2-1.rpm b/lotserver/centos/7/x64/kernel-4.11.2-1.rpm new file mode 100644 index 0000000..85e8c1c Binary files /dev/null and b/lotserver/centos/7/x64/kernel-4.11.2-1.rpm differ diff --git a/lotserver/centos/7/x64/kernel-devel-3.10.0-327.rpm b/lotserver/centos/7/x64/kernel-devel-3.10.0-327.rpm new file mode 100644 index 0000000..f9d8997 Binary files /dev/null and b/lotserver/centos/7/x64/kernel-devel-3.10.0-327.rpm differ diff --git a/lotserver/centos/7/x64/kernel-devel-3.10.0-957.rpm b/lotserver/centos/7/x64/kernel-devel-3.10.0-957.rpm new file mode 100644 index 0000000..2df4e2c Binary files /dev/null and b/lotserver/centos/7/x64/kernel-devel-3.10.0-957.rpm differ diff --git a/lotserver/centos/7/x64/kernel-devel-4.11.2-1.rpm b/lotserver/centos/7/x64/kernel-devel-4.11.2-1.rpm new file mode 100644 index 0000000..8692b3f Binary files /dev/null and b/lotserver/centos/7/x64/kernel-devel-4.11.2-1.rpm differ diff --git a/lotserver/centos/7/x64/kernel-headers-3.10.0-327.rpm b/lotserver/centos/7/x64/kernel-headers-3.10.0-327.rpm new file mode 100644 index 0000000..74d8cc2 Binary files /dev/null and b/lotserver/centos/7/x64/kernel-headers-3.10.0-327.rpm differ diff --git a/lotserver/centos/7/x64/kernel-headers-3.10.0-957.rpm b/lotserver/centos/7/x64/kernel-headers-3.10.0-957.rpm new file mode 100644 index 0000000..f9eaca4 Binary files /dev/null and b/lotserver/centos/7/x64/kernel-headers-3.10.0-957.rpm differ diff --git a/lotserver/centos/7/x64/kernel-headers-4.11.2-1.rpm b/lotserver/centos/7/x64/kernel-headers-4.11.2-1.rpm new file mode 100644 index 0000000..7607e3d Binary files /dev/null and b/lotserver/centos/7/x64/kernel-headers-4.11.2-1.rpm differ diff --git a/lotserver/centos/RPM-GPG-KEY-elrepo.org b/lotserver/centos/RPM-GPG-KEY-elrepo.org new file mode 100644 index 0000000..fe0c082 --- /dev/null +++ b/lotserver/centos/RPM-GPG-KEY-elrepo.org @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.5 (GNU/Linux) + +mQGiBEm+/6QRBAC5mbtqOFSQ0FkTLIMdIoqxtraIeUqwbPp3IBYQ/u/EREjyEf1D +qFyBEXII0dD8JDT85vRZ81jhB7nFWa0VbUfY0xfghkbnokiNBVNpiQcvszw3UYDF +aLAaOC8Z98vmlsQaBBTQG6704ZXLr7FJyG3GP5WE6egXIQQbrMcdmCoRBwCg/dwC +HLWjuemoDc5SX7hKHbB4zZ8D/jP+oMbqz+bDn8OZ2UuaGdxr+mHW8tzTdPjnEU8e +hUt1ws8eBqn/gMfKvUBa8xFSILe8Ty99u+VjFbcRsdf0H6dRre9AdDVUz5oxzoPw +gamA8mhPQvFh3wt9smtRUh5IoM2LiM1s5pGMLuYuvSnVUPArEnSfW6K5I6v7OarU +3WfrBACDEGGcaWKjfdkRtmKIQrzu6AnldVC1ISLVAoqxHnKNFTk1BgO0PSZDpfJI +x8fMCnGlusoL6F5+LYEk4K4B0zvlj1ur3JocjxpuBLccl94JTo/+I9ZbS8ptUqLw +LBUkgIQJzzIH4G5NZsQ3FpzSWGRFVa7etqTv9BfUMUmJxhEoobQ/ZWxyZXBvLm9y +ZyAoUlBNIFNpZ25pbmcgS2V5IGZvciBlbHJlcG8ub3JnKSA8c2VjdXJlQGVscmVw +by5vcmc+iGAEExECACAFAkm+/6QCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK +CRAwm8MFuq2uUlgWAKCGWOpyodbzxS7Xy/0X9m9qVnHq+ACfUHrQzYAtFRpT07Sg +giosE+mvAKu5Ag0ESb7/pxAIALgT0q0HhwugE717a7N+gAtCbFu8pCXOZcrAnJpG +cMA9VWmsODZStPTxXYM2ggCMSzUnNis8pPZOPIP5C+t2IYtVjLshM4C9UiY7u5ET +jErWCxWmF+2QLO50K56E4qxj0wufZt9P+Ih0USUM5L2oyrQG51uj/2v3Qq3igc8Z +NTtmEOfis3losusQbAfZtTBmNQ0570kkhMxiyavgAUxLenXHYrkDJFuL7XdBCmna +kykTn2dzU81rIDZ+QPxII4V/eZ5xGiRY/EDUIAysEV2m0NNZgWi/twUnZICm7zYp +VRviJrBnFTvNEPMhiGRnJgQp/Krv4WIHQ67N8lQg3q5RJX8AAwUH/0UBjBgbsuWR +dB+ZYWjKPBy+bJY/6HefPUuLrt3QDNINMW8kY7VzWRMWyGc4IlPJDW0nwOn/vT5H +Dgc3YmA3tm7cKj5wpFijzff61Db8eq+CUKGscKxDBGzHq6oirM5U7DQLmipCs5Eb +efwHIjE3aOUSnoQmniEnBWI2hm/rftmY9oJSi8xgns4wAokDPiMcucADtbV3fznx +ppuowtBi8bcGB1vJZjTySQcSKWv+IVp5ej06uZ9501BEm6HxUZkuAGPecP65kcQu +5bg7B7LQeI5AWyujqvfuWgAF34xhl9QU/sDWxM3wUog+bw7y8pyWCqnJhp70yx8o +SqzhFygT62eISQQYEQIACQUCSb7/pwIbDAAKCRAwm8MFuq2uUq8PAKC1+E2pSwiS +oHXkKYPYDwApsP1mVACfRe1YnggLYQtG9LMeweVQQC77rK8= +=qyRr +-----END PGP PUBLIC KEY BLOCK----- diff --git a/lotserver/debian/x32/linux-image-3.2.0-4.deb b/lotserver/debian/x32/linux-image-3.2.0-4.deb new file mode 100644 index 0000000..09f51cc Binary files /dev/null and b/lotserver/debian/x32/linux-image-3.2.0-4.deb differ diff --git a/lotserver/debian/x64/linux-image-3.16.0-4.deb b/lotserver/debian/x64/linux-image-3.16.0-4.deb new file mode 100644 index 0000000..8b93e8c Binary files /dev/null and b/lotserver/debian/x64/linux-image-3.16.0-4.deb differ diff --git a/lotserver/debian/x64/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb b/lotserver/debian/x64/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb new file mode 100644 index 0000000..e97a0a0 Binary files /dev/null and b/lotserver/debian/x64/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb differ diff --git a/lotserver/debian/x64/linux-image-4.9.0-4.deb b/lotserver/debian/x64/linux-image-4.9.0-4.deb new file mode 100644 index 0000000..d3e3bb6 Binary files /dev/null and b/lotserver/debian/x64/linux-image-4.9.0-4.deb differ diff --git a/lotserver/ubuntu/x32/linux-headers-3.13.0-29-all.deb b/lotserver/ubuntu/x32/linux-headers-3.13.0-29-all.deb new file mode 100644 index 0000000..7b97ea0 Binary files /dev/null and b/lotserver/ubuntu/x32/linux-headers-3.13.0-29-all.deb differ diff --git a/lotserver/ubuntu/x32/linux-headers-3.13.0-29.deb b/lotserver/ubuntu/x32/linux-headers-3.13.0-29.deb new file mode 100644 index 0000000..f7c267e Binary files /dev/null and b/lotserver/ubuntu/x32/linux-headers-3.13.0-29.deb differ diff --git a/lotserver/ubuntu/x32/linux-image-3.13.0-29.deb b/lotserver/ubuntu/x32/linux-image-3.13.0-29.deb new file mode 100644 index 0000000..be6c05e Binary files /dev/null and b/lotserver/ubuntu/x32/linux-image-3.13.0-29.deb differ diff --git a/lotserver/ubuntu/x64/linux-headers-4.4.0-47-all.deb b/lotserver/ubuntu/x64/linux-headers-4.4.0-47-all.deb new file mode 100644 index 0000000..9537da3 Binary files /dev/null and b/lotserver/ubuntu/x64/linux-headers-4.4.0-47-all.deb differ diff --git a/lotserver/ubuntu/x64/linux-headers-4.4.0-47.deb b/lotserver/ubuntu/x64/linux-headers-4.4.0-47.deb new file mode 100644 index 0000000..6cd1698 Binary files /dev/null and b/lotserver/ubuntu/x64/linux-headers-4.4.0-47.deb differ diff --git a/lotserver/ubuntu/x64/linux-image-4.4.0-47.deb b/lotserver/ubuntu/x64/linux-image-4.4.0-47.deb new file mode 100644 index 0000000..6a0efdc Binary files /dev/null and b/lotserver/ubuntu/x64/linux-image-4.4.0-47.deb differ diff --git a/tcp.sh b/tcp.sh new file mode 100755 index 0000000..20be982 --- /dev/null +++ b/tcp.sh @@ -0,0 +1,2337 @@ +#!/usr/bin/env bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH +#================================================= +# System Required: CentOS 7/8,Debian/ubuntu,oraclelinux +# Description: BBR+BBRplus+Lotserver +# Version: 100.0.4.2 +# Author: 千影,cx9208,YLX +# 更新内容及反馈: https://blog.ylx.me/archives/783.html +#================================================= + +# RED='\033[0;31m' +# GREEN='\033[0;32m' +# YELLOW='\033[0;33m' +# SKYBLUE='\033[0;36m' +# PLAIN='\033[0m' + +sh_ver="100.0.4.2" +github="raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master" + +imgurl="" +headurl="" +github_network=1 + +Green_font_prefix="\033[32m" +Red_font_prefix="\033[31m" +Font_color_suffix="\033[0m" +Info="${Green_font_prefix}[信息]${Font_color_suffix}" +Error="${Red_font_prefix}[错误]${Font_color_suffix}" +Tip="${Green_font_prefix}[注意]${Font_color_suffix}" + +if [ -f "/etc/sysctl.d/bbr.conf" ]; then + rm -rf /etc/sysctl.d/bbr.conf +fi + +# 检查当前用户是否为 root 用户 +if [ "$EUID" -ne 0 ]; then + echo "请使用 root 用户身份运行此脚本" + exit +fi + +#优化系统配置 +optimizing_system_old() { + if [ ! -f "/etc/sysctl.d/99-sysctl.conf" ]; then + touch /etc/sysctl.d/99-sysctl.conf + fi + sed -i '/net.ipv4.tcp_retries2/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_slow_start_after_idle/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.conf + sed -i '/fs.file-max/d' /etc/sysctl.conf + sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.conf + sed -i '/net.ipv4.route.gc_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_synack_retries/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syn_retries/d' /etc/sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_timestamps/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_orphans/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.conf + + echo "net.ipv4.tcp_retries2 = 8 +net.ipv4.tcp_slow_start_after_idle = 0 +fs.file-max = 1000000 +fs.inotify.max_user_instances = 8192 +net.ipv4.tcp_syncookies = 1 +net.ipv4.tcp_fin_timeout = 30 +net.ipv4.tcp_tw_reuse = 1 +net.ipv4.ip_local_port_range = 1024 65000 +net.ipv4.tcp_max_syn_backlog = 16384 +net.ipv4.tcp_max_tw_buckets = 6000 +net.ipv4.route.gc_timeout = 100 +net.ipv4.tcp_syn_retries = 1 +net.ipv4.tcp_synack_retries = 1 +net.core.somaxconn = 32768 +net.core.netdev_max_backlog = 32768 +net.ipv4.tcp_timestamps = 0 +net.ipv4.tcp_max_orphans = 32768 +# forward ipv4 +#net.ipv4.ip_forward = 1" >>/etc/sysctl.d/99-sysctl.conf + sysctl -p + echo "* soft nofile 1000000 +* hard nofile 1000000" >/etc/security/limits.conf + echo "ulimit -SHn 1000000" >>/etc/profile + read -p "需要重启VPS后,才能生效系统优化配置,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi +} + +optimizing_system_johnrosen1() { + if [ ! -f "/etc/sysctl.d/99-sysctl.conf" ]; then + touch /etc/sysctl.d/99-sysctl.conf + fi + sed -i '/net.ipv4.tcp_fack/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_early_retrans/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.unres_qlen/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_orphans/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_buckets/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/kernel.pid_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/vm.nr_hugepages/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.optmem_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.route_localnet/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.accept_ra/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.accept_ra/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.netdev_budget/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.netdev_budget_usecs/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/fs.file-max /d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.rmem_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.wmem_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.rmem_default/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.wmem_default/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.icmp_echo_ignore_all/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.icmp_echo_ignore_broadcasts/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.icmp_ignore_bogus_error_responses/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.secure_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.secure_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.send_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.send_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.rp_filter/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.rp_filter/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_time/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_intvl/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_probes/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_synack_retries/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_rfc1337/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_timestamps/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_rmem/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_wmem/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.udp_rmem_min/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.udp_wmem_min/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_mtu_probing/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.arp_ignore /d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.arp_ignore/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.arp_announce/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.arp_announce/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_autocorking/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_slow_start_after_idle/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_notsent_lowat/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_no_metrics_save/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn_fallback/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_frto/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/vm.swappiness/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.ip_unprivileged_port_start/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/vm.overcommit_memory/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.gc_thresh3/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.gc_thresh2/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.gc_thresh1/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.neigh.default.gc_thresh3/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.neigh.default.gc_thresh2/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.neigh.default.gc_thresh1/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.nf_conntrack_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_fin_wait/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_time_wait/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_close_wait/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_established/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/fs.inotify.max_user_watches/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_low_latency/d' /etc/sysctl.d/99-sysctl.conf + + cat >'/etc/sysctl.d/99-sysctl.conf' </sys/kernel/mm/transparent_hugepage/enabled + + cat >'/etc/systemd/system.conf' <'/etc/security/limits.conf' <>/etc/profile + + if grep -q "pam_limits.so" /etc/pam.d/common-session; then + : + else + sed -i '/required pam_limits.so/d' /etc/pam.d/common-session + echo "session required pam_limits.so" >>/etc/pam.d/common-session + fi + systemctl daemon-reload + echo -e "${Info}优化方案2应用结束,可能需要重启!" +} + +# 函数:生成并写入 Linux 内核网络优化配置 +# 参数(可选,按顺序): +# $1: 延迟 (ms,默认 100) +# $2: 本地带宽 (Mbps,默认 1000) +# $3: VPS 带宽 (Mbps,默认 1000) +# $4: VPS 内存 (MB,默认自动获取) +optimizing_system_radicalizate() { + # 设置默认值 + local latency_default=100 + local local_bw_default=1000 + local vps_bw_default=1000 + local vps_mem_default=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo) + + # 提示用户输入参数 + echo -e "请输入参数(用空格隔开):延迟(ms) 本地带宽(Mbps) VPS带宽(Mbps) VPS内存(MB)" + echo -e "可以输入单个或多个参数,未提供的参数将使用默认值" + echo -e "示例1(完整参数):180 500 500 1024" + echo -e "示例2(单个参数):200 # 结果:延迟=200ms, 本地带宽=1000Mbps, VPS带宽=1000Mbps, 内存=自动获取" + echo -e "默认参数:延迟=$latency_default ms, 本地带宽=$local_bw_default Mbps, VPS带宽=$vps_bw_default Mbps, 内存=自动获取($vps_mem_default MB)" + echo -e "将在 12 秒后使用默认参数,请输入参数并按回车提交(留空使用默认值):" + + # 设置 12 秒倒计时,静默等待输入 + local input + if ! read -t 12 -r input; then + echo -e "\n未输入参数或超时,使用默认参数..." + local latency=$latency_default + local local_bw=$local_bw_default + local vps_bw=$vps_bw_default + local vps_mem=$vps_mem_default + else + # 分割用户输入 + read -r latency local_bw vps_bw vps_mem <<<"$input" + # 使用默认值补齐未提供的参数 + latency=${latency:-$latency_default} + local_bw=${local_bw:-$local_bw_default} + vps_bw=${vps_bw:-$vps_bw_default} + vps_mem=${vps_mem:-$vps_mem_default} + fi + + # 显示最终提交的参数信息 + echo -e "最终提交参数:延迟=$latency ms, 本地带宽=$local_bw Mbps, VPS带宽=$vps_bw Mbps, 内存=$vps_mem MB" + sleep 1 # 延迟 1 秒 + + # 检查输入参数是否有效 + if ! [[ "$latency" =~ ^[0-9]+$ ]] || ! [[ "$local_bw" =~ ^[0-9]+$ ]] || + ! [[ "$vps_bw" =~ ^[0-9]+$ ]] || ! [[ "$vps_mem" =~ ^[0-9]+$ ]]; then + echo -e "Error: All parameters must be positive integers." + return 1 + fi + + # 检查是否有 root 权限 + if [ "$EUID" -ne 0 ]; then + echo -e "Error: This script requires root privileges to write to /etc/sysctl.d/99-sysctl.conf." + return 1 + fi + + # 计算带宽延迟积 (BDP, 字节):min(local_bw, vps_bw) * latency / 8 + local min_bw=$((local_bw < vps_bw ? local_bw : vps_bw)) + local bdp=$((min_bw * 1000000 * latency / 8 / 1000)) + + # 根据内存和 BDP 设置缓冲区大小 (上限不超过内存的 50%),使用整数运算 + local rmem_max=$((bdp * 2)) # 2 倍 BDP + local wmem_max=$((bdp * 3 / 2)) # 1.5 倍 BDP + local max_mem_bytes=$((vps_mem * 1024 * 1024 * 50 / 100)) + [[ $rmem_max -gt $max_mem_bytes ]] && rmem_max=$max_mem_bytes + [[ $wmem_max -gt $max_mem_bytes ]] && wmem_max=$max_mem_bytes + [[ $rmem_max -lt 1048576 ]] && rmem_max=1048576 # 最小 1MB + [[ $wmem_max -lt 1048576 ]] && wmem_max=1048576 # 最小 1MB + + # 根据带宽和内存设置队列长度 + local netdev_max_backlog=$((min_bw * 10)) + [[ $netdev_max_backlog -gt 10000 ]] && netdev_max_backlog=10000 + [[ $netdev_max_backlog -lt 1000 ]] && netdev_max_backlog=1000 + local somaxconn=$((vps_mem * 20)) + [[ $somaxconn -gt 16384 ]] && somaxconn=16384 + [[ $somaxconn -lt 512 ]] && somaxconn=512 + local tcp_max_syn_backlog=$((somaxconn * 4)) + [[ $tcp_max_syn_backlog -gt 65536 ]] && tcp_max_syn_backlog=65536 + + # 根据延迟设置初始拥塞窗口 + local tcp_init_cwnd=$((latency / 20 + 10)) + [[ $tcp_init_cwnd -gt 32 ]] && tcp_init_cwnd=32 + [[ $tcp_init_cwnd -lt 10 ]] && tcp_init_cwnd=10 + + # 根据内存设置最小空闲内存 (约 10-15% 内存) + local min_free_kbytes=$((vps_mem * 1024 * 12 / 100)) + [[ $min_free_kbytes -gt 524288 ]] && min_free_kbytes=524288 # 最大 512MB + [[ $min_free_kbytes -lt 65536 ]] && min_free_kbytes=65536 # 最小 64MB + + # 目标配置文件 + local config_file="/etc/sysctl.d/99-sysctl.conf" + local temp_file=$(mktemp) + + # 定义所有参数和值(包含测试中发现的额外参数) + declare -A sysctl_params=( + ["kernel.pid_max"]="65535" + ["kernel.panic"]="1" + ["kernel.sysrq"]="1" + ["kernel.core_pattern"]="core_%e" + ["kernel.printk"]="3 4 1 3" + ["kernel.numa_balancing"]="0" + ["kernel.sched_autogroup_enabled"]="0" + ["vm.swappiness"]="5" + ["vm.dirty_ratio"]="5" + ["vm.dirty_background_ratio"]="2" + ["vm.panic_on_oom"]="1" + ["vm.overcommit_memory"]="1" + ["vm.min_free_kbytes"]="$min_free_kbytes" + ["net.core.netdev_max_backlog"]="$netdev_max_backlog" + ["net.core.rmem_max"]="$rmem_max" + ["net.core.wmem_max"]="$wmem_max" + ["net.core.rmem_default"]="262144" + ["net.core.wmem_default"]="262144" + ["net.core.somaxconn"]="$somaxconn" + ["net.core.optmem_max"]="262144" + ["net.netfilter.nf_conntrack_max"]="262144" + ["net.nf_conntrack_max"]="262144" + ["net.ipv4.tcp_fastopen"]="3" + ["net.ipv4.tcp_timestamps"]="1" + ["net.ipv4.tcp_tw_reuse"]="1" + ["net.ipv4.tcp_fin_timeout"]="10" + ["net.ipv4.tcp_slow_start_after_idle"]="0" + ["net.ipv4.tcp_max_tw_buckets"]="32768" + ["net.ipv4.tcp_sack"]="1" + ["net.ipv4.tcp_fack"]="1" + ["net.ipv4.tcp_rmem"]="32768 262144 $rmem_max" + ["net.ipv4.tcp_wmem"]="32768 262144 $wmem_max" + ["net.ipv4.tcp_mtu_probing"]="1" + ["net.ipv4.tcp_notsent_lowat"]="16384" + ["net.ipv4.tcp_window_scaling"]="1" + ["net.ipv4.tcp_adv_win_scale"]="2" + ["net.ipv4.tcp_moderate_rcvbuf"]="1" + ["net.ipv4.tcp_no_metrics_save"]="1" + ["net.ipv4.tcp_init_cwnd"]="$tcp_init_cwnd" + ["net.ipv4.tcp_max_syn_backlog"]="$tcp_max_syn_backlog" + ["net.ipv4.tcp_max_orphans"]="32768" + ["net.ipv4.tcp_synack_retries"]="2" + ["net.ipv4.tcp_syn_retries"]="2" + ["net.ipv4.tcp_abort_on_overflow"]="0" + ["net.ipv4.tcp_stdurg"]="0" + ["net.ipv4.tcp_rfc1337"]="0" + ["net.ipv4.tcp_syncookies"]="1" + ["net.ipv4.tcp_low_latency"]="1" + ["net.ipv4.ip_local_port_range"]="1024 65535" + ["net.ipv4.ip_no_pmtu_disc"]="0" + ["net.ipv4.route.gc_timeout"]="100" + ["net.ipv4.neigh.default.gc_stale_time"]="120" + ["net.ipv4.neigh.default.gc_thresh3"]="4096" + ["net.ipv4.neigh.default.gc_thresh2"]="2048" + ["net.ipv4.neigh.default.gc_thresh1"]="512" + ["net.ipv4.icmp_echo_ignore_broadcasts"]="1" + ["net.ipv4.icmp_ignore_bogus_error_responses"]="1" + ["net.ipv4.conf.all.rp_filter"]="1" + ["net.ipv4.conf.default.rp_filter"]="1" + ["net.ipv4.conf.all.arp_announce"]="2" + ["net.ipv4.conf.default.arp_announce"]="2" + ["net.ipv4.conf.all.arp_ignore"]="1" + ["net.ipv4.conf.default.arp_ignore"]="1" + ) + + # 添加文件头部注释 + echo "# Generated sysctl configuration" >"$temp_file" + echo "# Latency: $latency ms" >>"$temp_file" + echo "# Local Bandwidth: $local_bw Mbps" >>"$temp_file" + echo "# VPS Bandwidth: $vps_bw Mbps" >>"$temp_file" + echo "# VPS Memory: $vps_mem MB" >>"$temp_file" + echo "" >>"$temp_file" + + # 如果配置文件不存在,直接写入所有参数 + if [ ! -f "$config_file" ]; then + for key in "${!sysctl_params[@]}"; do + echo "$key=${sysctl_params[$key]}" >>"$temp_file" + done + else + # 读取现有配置文件内容,去除多余空格 + cp "$config_file" "$temp_file.bak" + while IFS='=' read -r key value; do + key=$(echo "$key" | xargs) # 去除首尾空格 + value=$(echo "$value" | xargs) # 去除值中的多余空格 + if [ -n "$key" ] && [[ ! "$key" =~ ^# ]]; then + if [[ -n "${sysctl_params[$key]}" ]]; then + echo "$key=${sysctl_params[$key]}" >>"$temp_file.new" + unset sysctl_params["$key"] + else + echo "$key=$value" >>"$temp_file.new" + fi + fi + done <"$config_file" + + # 将新参数追加到临时文件 + for key in "${!sysctl_params[@]}"; do + echo "$key=${sysctl_params[$key]}" >>"$temp_file.new" + done + + # 合并并替换临时文件 + cat "$temp_file" >"$temp_file.final" + cat "$temp_file.new" >>"$temp_file.final" + mv "$temp_file.final" "$temp_file" + rm -f "$temp_file.new" "$temp_file.bak" + fi + + # 写入目标文件并应用配置 + mv "$temp_file" "$config_file" + chmod 644 "$config_file" + sysctl -p "$config_file" >/dev/null 2>&1 + sysctl --system >/dev/null 2>&1 + echo -e "${Info}激进方案应用完毕,有些设置可能需要重启生效!" +} + +#处理传进来的参数 直接优化 +err() { + echo "错误: $1" + exit 1 +} + +while [ $# -gt 0 ]; do + case $1 in + op) + optimizing_system_old # 调用函数 + exit + ;; + op2) + optimizing_system_johnrosen1 # 调用函数 + exit + ;; + op3) + optimizing_system_radicalizate # 调用函数 + exit + ;; + *) + err "未知选项: \"$1\"" + ;; + esac + shift # 移动到下一个参数 +done + +# 检查github网络 +check_github() { + # 检测域名的可访问性函数 + check_domain() { + local domain="$1" + if ! curl --max-time 5 --head --silent --fail "$domain" >/dev/null; then + echo -e "${Error}无法访问 $domain,请检查网络或者本地DNS 或者访问频率过快而受限" + github_network=0 + fi + } + + # 检测所有域名的可访问性 + check_domain "https://raw.githubusercontent.com" + check_domain "https://api.github.com" + check_domain "https://github.com" + + if [ "$github_network" -eq 0 ]; then + echo -e "${Error}github网络访问受限,将影响内核的安装以及脚本的检查更新,1秒后继续运行脚本" + sleep 1 + else + # 所有域名均可访问,打印成功提示 + echo -e "${Green_font_prefix}github可访问${Font_color_suffix},继续执行脚本..." + fi +} + +#检查连接 +checkurl() { + local url="$1" + local maxRetries=3 + local retryDelay=2 + + if [[ -z "$url" ]]; then + echo "错误:缺少URL参数!" + exit 1 + fi + + local retries=0 + local responseCode="" + + while [[ -z "$responseCode" && $retries -lt $maxRetries ]]; do + responseCode=$(curl --max-time 6 -s -L -m 10 --connect-timeout 5 -o /dev/null -w "%{http_code}" "$url") + + if [[ -z "$responseCode" ]]; then + ((retries++)) + sleep $retryDelay + fi + done + + if [[ -n "$responseCode" && ("$responseCode" == "200" || "$responseCode" =~ ^3[0-9]{2}$) ]]; then + echo "下载地址检查OK,继续!" + else + echo "下载地址检查出错,退出!" + exit 1 + fi +} + +#cn处理github加速 +check_cn() { + # 检查是否安装了jq命令,如果没有安装则进行安装 + if ! command -v jq >/dev/null 2>&1; then + if command -v yum >/dev/null 2>&1; then + sudo yum install epel-release -y + sudo yum install -y jq + elif command -v apt-get >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y jq + else + echo "无法安装jq命令。请手动安装jq后再试。" + exit 1 + fi + fi + + # 获取当前IP地址,设置超时为3秒 + current_ip=$(curl -s --max-time 3 https://api.ipify.org) + + # 使用ip-api.com查询IP所在国家,设置超时为3秒 + response=$(curl -s --max-time 3 "http://ip-api.com/json/$current_ip") + + # 检查国家是否为中国 + country=$(echo "$response" | jq -r '.countryCode') + if [[ "$country" == "CN" ]]; then + local suffixes=( + "https://gh.con.sh/" + "https://gh-proxy.com/" + "https://ghp.ci/" + "https://gh.m-l.cc/" + "https://down.npee.cn/?" + "https://mirror.ghproxy.com/" + "https://ghps.cc/" + "https://gh.api.99988866.xyz/" + "https://git.886.be/" + "https://hub.gitmirror.com/" + "https://pd.zwc365.com/" + "https://gh.ddlc.top/" + "https://slink.ltd/" + "https://github.moeyy.xyz/" + "https://ghproxy.crazypeace.workers.dev/" + "https://gh.h233.eu.org/" + ) + + # 循环遍历每个后缀并测试组合的链接 + for suffix in "${suffixes[@]}"; do + # 组合后缀和原始链接 + combined_url="$suffix$1" + + # 使用 curl -I 获取头部信息,提取状态码 + local response_code=$(curl --max-time 2 -sL -w "%{http_code}" -I "$combined_url" | head -n 1 | awk '{print $2}') + + # 检查响应码是否表示成功 (2xx) + if [[ $response_code -ge 200 && $response_code -lt 300 ]]; then + echo "$combined_url" + return 0 # 返回可用链接,结束函数 + fi + done + + # 如果没有找到有效链接,返回原始链接 + else + echo "$1" + return 1 + + fi +} + +#下载 +download_file() { + url="$1" + filename="$2" + + wget "$url" -O "$filename" + status=$? + + if [ $status -eq 0 ]; then + echo -e "\e[32m文件下载成功或已经是最新。\e[0m" + else + echo -e "\e[31m文件下载失败,退出状态码: $status\e[0m" + exit 1 + fi +} + +#檢查賦值 +check_empty() { + local var_value=$1 + + if [[ -z $var_value ]]; then + echo "$var_value 是空值,退出!" + exit 1 + fi +} + +#安装BBR内核 +installbbr() { + kernel_version="5.9.6" + bit=$(uname -m) + rm -rf bbr + mkdir bbr && cd bbr || exit + + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}' | awk -F '[_]' '{print $3}') + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_latest_bbr_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + #check_empty $github_ver + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + #kernel_version=$github_ver + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + #headurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/kernel-headers-${github_ver}-1.x86_64.rpm + #imgurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/kernel-${github_ver}-1.x86_64.rpm + + headurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_6.1.35_latest_bbr_2023.06.22-0855/kernel-headers-6.1.35-1.x86_64.rpm + imgurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_6.1.35_latest_bbr_2023.06.22-0855/kernel-6.1.35-1.x86_64.rpm + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl kernel-headers-c7.rpm + download_file $imgurl kernel-c7.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Debian_Kernel' | grep '_latest_bbr_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + check_empty $github_ver + echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=$github_ver + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}') + imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + #headurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-headers-${github_ver}_${github_ver}-1_amd64.deb + #imgurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-image-${github_ver}_${github_ver}-1_amd64.deb + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + elif [[ ${bit} == "aarch64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Debian_Kernel' | grep '_arm64_' | grep '_bbr_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=$github_ver + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}') + imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + #headurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-headers-${github_ver}_${github_ver}-1_amd64.deb + #imgurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-image-${github_ver}_${github_ver}-1_amd64.deb + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + else + echo -e "${Error} 不支持x86_64及arm64/aarch64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf bbr + + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBR${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBR,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装BBRplus内核 4.14.129 +installbbrplus() { + kernel_version="4.14.160-bbrplus" + bit=$(uname -m) + rm -rf bbrplus + mkdir bbrplus && cd bbrplus || exit + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.14.129_bbrplus" + detele_kernel_head + headurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/centos/7/kernel-headers-4.14.129-bbrplus.rpm + imgurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/centos/7/kernel-4.14.129-bbrplus.rpm + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl kernel-headers-c7.rpm + download_file $imgurl kernel-c7.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.14.129-bbrplus" + detele_kernel_head + headurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/debian-ubuntu/x64/linux-headers-4.14.129-bbrplus.deb + imgurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/debian-ubuntu/x64/linux-image-4.14.129-bbrplus.deb + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O linux-headers.deb $headurl + wget -O linux-image.deb $imgurl + + dpkg -i linux-image.deb + dpkg -i linux-headers.deb + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf bbrplus + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBRplus${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBRplus,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装Lotserver内核 +installlot() { + bit=$(uname -m) + if [[ ${bit} != "x86_64" ]]; then + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + if [[ ${bit} == "x86_64" ]]; then + bit='x64' + fi + if [[ ${bit} == "i386" ]]; then + bit='x32' + fi + if [[ "${OS_type}" == "CentOS" ]]; then + rpm --import http://${github}/lotserver/${release}/RPM-GPG-KEY-elrepo.org + yum remove -y kernel-firmware + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-firmware-${kernel_version}.rpm + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-${kernel_version}.rpm + yum remove -y kernel-headers + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-headers-${kernel_version}.rpm + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-devel-${kernel_version}.rpm + fi + + if [[ "${OS_type}" == "Debian" ]]; then + deb_issue="$(cat /etc/issue)" + deb_relese="$(echo $deb_issue | grep -io 'Ubuntu\|Debian' | sed -r 's/(.*)/\L\1/')" + os_ver="$(dpkg --print-architecture)" + [ -n "$os_ver" ] || exit 1 + if [ "$deb_relese" == 'ubuntu' ]; then + deb_ver="$(echo $deb_issue | grep -o '[0-9]*\.[0-9]*' | head -n1)" + if [ "$deb_ver" == "14.04" ]; then + kernel_version="3.16.0-77-generic" && item="3.16.0-77-generic" && ver='trusty' + elif [ "$deb_ver" == "16.04" ]; then + kernel_version="4.8.0-36-generic" && item="4.8.0-36-generic" && ver='xenial' + elif [ "$deb_ver" == "18.04" ]; then + kernel_version="4.15.0-30-generic" && item="4.15.0-30-generic" && ver='bionic' + else + exit 1 + fi + url='archive.ubuntu.com' + urls='security.ubuntu.com' + elif [ "$deb_relese" == 'debian' ]; then + deb_ver="$(echo $deb_issue | grep -o '[0-9]*' | head -n1)" + if [ "$deb_ver" == "7" ]; then + kernel_version="3.2.0-4-${os_ver}" && item="3.2.0-4-${os_ver}" && ver='wheezy' && url='archive.debian.org' && urls='archive.debian.org' + elif [ "$deb_ver" == "8" ]; then + kernel_version="3.16.0-4-${os_ver}" && item="3.16.0-4-${os_ver}" && ver='jessie' && url='archive.debian.org' && urls='archive.debian.org' + elif [ "$deb_ver" == "9" ]; then + kernel_version="4.9.0-4-${os_ver}" && item="4.9.0-4-${os_ver}" && ver='stretch' && url='archive.debian.org' && urls='archive.debian.org' + else + exit 1 + fi + fi + [ -n "$item" ] && [ -n "$urls" ] && [ -n "$url" ] && [ -n "$ver" ] || exit 1 + if [ "$deb_relese" == 'ubuntu' ]; then + echo "deb http://${url}/${deb_relese} ${ver} main restricted universe multiverse" >/etc/apt/sources.list + echo "deb http://${url}/${deb_relese} ${ver}-updates main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb http://${url}/${deb_relese} ${ver}-backports main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb http://${urls}/${deb_relese} ${ver}-security main restricted universe multiverse" >>/etc/apt/sources.list + + apt-get update || apt-get --allow-releaseinfo-change update + apt-get install --no-install-recommends -y linux-image-${item} + elif [ "$deb_relese" == 'debian' ]; then + echo "deb http://${url}/${deb_relese} ${ver} main" >/etc/apt/sources.list + echo "deb-src http://${url}/${deb_relese} ${ver} main" >>/etc/apt/sources.list + echo "deb http://${urls}/${deb_relese}-security ${ver}/updates main" >>/etc/apt/sources.list + echo "deb-src http://${urls}/${deb_relese}-security ${ver}/updates main" >>/etc/apt/sources.list + + if [ "$deb_ver" == "8" ]; then + dpkg -l | grep -q 'linux-base' || { + wget --no-check-certificate -qO '/tmp/linux-base_3.5_all.deb' 'http://snapshot.debian.org/archive/debian/20120304T220938Z/pool/main/l/linux-base/linux-base_3.5_all.deb' + dpkg -i '/tmp/linux-base_3.5_all.deb' + } + wget --no-check-certificate -qO '/tmp/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' 'http://snapshot.debian.org/archive/debian/20171008T163152Z/pool/main/l/linux/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' + dpkg -i '/tmp/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' + + if [ $? -ne 0 ]; then + exit 1 + fi + elif [ "$deb_ver" == "9" ]; then + dpkg -l | grep -q 'linux-base' || { + wget --no-check-certificate -qO '/tmp/linux-base_4.5_all.deb' 'http://snapshot.debian.org/archive/debian/20160917T042239Z/pool/main/l/linux-base/linux-base_4.5_all.deb' + dpkg -i '/tmp/linux-base_4.5_all.deb' + } + wget --no-check-certificate -qO '/tmp/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' 'http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' + dpkg -i '/tmp/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' + ##备选 + #https://sys.if.ci/download/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://mirror.cs.uchicago.edu/debian-security/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://debian.sipwise.com/debian-security/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://srv24.dsidata.sk/security.debian.org/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://pubmirror.plutex.de/debian-security/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://packages.mendix.com/debian/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3_amd64.deb + #http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://snapshot.debian.org/archive/debian/20171231T180144Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3_amd64.deb + if [ $? -ne 0 ]; then + exit 1 + fi + else + exit 1 + fi + fi + apt-get autoremove -y + [ -d '/var/lib/apt/lists' ] && find /var/lib/apt/lists -type f -delete + fi + + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}Lotserver${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启Lotserver,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装xanmod内核 from xanmod.org +installxanmod() { + kernel_version="5.5.1-xanmod1" + bit=$(uname -m) + if [[ ${bit} != "x86_64" ]]; then + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + rm -rf xanmod + mkdir xanmod && cd xanmod || exit + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_lts_latest_' | grep 'xanmod' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + #kernel_version=$github_ver + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + + headurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2159/kernel-headers-5.15.95_xanmod1-1.x86_64.rpm + imgurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2159/kernel-5.15.95_xanmod1-1.x86_64.rpm + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl kernel-headers-c7.rpm + download_file $imgurl kernel-c7.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + elif [[ ${version} == "8" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_lts_C8_latest_' | grep 'xanmod' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + #kernel_version=$github_ver + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + + headurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.81-xanmod1_lts_C8_latest_2022.12.06-1614/kernel-headers-5.15.81_xanmod1-1.x86_64.rpm + imgurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.81-xanmod1_lts_C8_latest_2022.12.06-1614/kernel-5.15.81_xanmod1-1.x86_64.rpm + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O kernel-headers-c8.rpm $headurl + wget -O kernel-c8.rpm $imgurl + yum install -y kernel-c8.rpm + yum install -y kernel-headers-c8.rpm + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Debian_Kernel' | grep '_lts_latest_' | grep 'xanmod' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + + #check_empty $github_ver + #echo -e "获取的xanmod lts版本号为:${github_ver}" + + #kernel_version=$github_ver + + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + + headurl=https://github.com/ylx2016/kernel/releases/download/Debian_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2210/linux-headers-5.15.95-xanmod1_5.15.95-xanmod1-1_amd64.deb + imgurl=https://github.com/ylx2016/kernel/releases/download/Debian_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2210/linux-image-5.15.95-xanmod1_5.15.95-xanmod1-1_amd64.deb + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf xanmod + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBR${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBR,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装bbr2内核 集成到xanmod内核了 +#安装bbrplus 新内核 +#2021.3.15 开始由https://github.com/UJX6N/bbrplus-5.19 替换bbrplusnew +#2021.4.12 地址更新为https://github.com/ylx2016/kernel/releases +#2021.9.2 再次改为https://github.com/UJX6N/bbrplus +#2022.9.6 改为https://github.com/UJX6N/bbrplus-5.19 +#2022.11.24 改为https://github.com/UJX6N/bbrplus-6.x_stable + +installbbrplusnew() { + github_ver_plus=$(curl -s https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases | grep /bbrplus-6.x_stable/releases/tag/ | head -1 | awk -F "[/]" '{print $8}' | awk -F "[\"]" '{print $1}') + github_ver_plus_num=$(curl -s https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases | grep /bbrplus-6.x_stable/releases/tag/ | head -1 | awk -F "[/]" '{print $8}' | awk -F "[\"]" '{print $1}' | awk -F "[-]" '{print $1}') + echo -e "获取的UJX6N的bbrplus-6.x_stable版本号为:${Green_font_prefix}${github_ver_plus}${Font_color_suffix}" + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + echo -e "${Green_font_prefix}安装失败这边反馈,内核问题给UJX6N反馈${Font_color_suffix}" + # kernel_version=$github_ver_plus + + bit=$(uname -m) + #if [[ ${bit} != "x86_64" ]]; then + # echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + #fi + rm -rf bbrplusnew + mkdir bbrplusnew && cd bbrplusnew || exit + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep 'headers' | grep 'el7' | awk -F '"' '{print $4}' | grep 'http') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep -v 'devel' | grep -v 'headers' | grep -v 'Source' | grep 'el7' | awk -F '"' '{print $4}' | grep 'http') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O kernel-c7.rpm $headurl + wget -O kernel-headers-c7.rpm $imgurl + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + if [[ ${version} == "8" ]]; then + if [[ ${bit} == "x86_64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep 'headers' | grep 'el8.x86_64' | grep 'https' | awk -F '"' '{print $4}' | grep 'http') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep -v 'devel' | grep -v 'headers' | grep -v 'Source' | grep 'el8.x86_64' | grep 'https' | awk -F '"' '{print $4}' | grep 'http') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O kernel-c8.rpm $headurl + wget -O kernel-headers-c8.rpm $imgurl + yum install -y kernel-c8.rpm + yum install -y kernel-headers-c8.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + elif [[ "${OS_type}" == "Debian" ]]; then + if [[ ${bit} == "x86_64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Ubuntu_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'http s://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'amd64.deb' | grep 'headers' | awk -F '"' '{print $4}' | grep 'http') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'amd64.deb' | grep 'image' | awk -F '"' '{print $4}' | grep 'http') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + elif [[ ${bit} == "aarch64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Ubuntu_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'http s://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'arm64.deb' | grep 'headers' | awk -F '"' '{print $4}') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'arm64.deb' | grep 'image' | awk -F '"' '{print $4}') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + else + echo -e "${Error} 不支持x86_64及arm64/aarch64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf bbrplusnew + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBRplus${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBRplus,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" + +} + +#安装cloud内核 +installcloud() { + + # 检查当前系统发行版 + local DISTRO=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') + local ARCH=$(uname -m) + local VERSIONS=() + local VERSION_MAP_FILE="/tmp/version_map.txt" + + # 检查架构并设置 IMAGE_URL 和 IMAGE_PATTERN + local IMAGE_URL + local IMAGE_PATTERN + if [ "$ARCH" == "x86_64" ]; then + IMAGE_URL="https://deb.debian.org/debian/pool/main/l/linux-signed-amd64/" + IMAGE_PATTERN='linux-image-[^"]+cloud-amd64_[^"]+_amd64\.deb' + elif [ "$ARCH" == "aarch64" ]; then + IMAGE_URL="https://deb.debian.org/debian/pool/main/l/linux-signed-arm64/" + IMAGE_PATTERN='linux-image-[^"]+cloud-arm64_[^"]+_arm64\.deb' + else + echo "不支持的架构:$ARCH,仅支持 x86_64 和 aarch64" + exit 1 + fi + + echo "检测到架构 $ARCH,正在从官方源获取cloud内核版本..." + + # 获取 cloud 内核 .deb 文件列表 + local DEB_FILES_RAW=$(curl -s "$IMAGE_URL" | grep -oP "$IMAGE_PATTERN") + + # 清空临时映射文件 + >"$VERSION_MAP_FILE" + + # 提取 image 版本号并写入映射文件 + while IFS= read -r file; do + if [[ "$file" =~ linux-image-([0-9]+\.[0-9]+(\.[0-9]+)?(-[0-9]+)?) ]]; then + local ver="${BASH_REMATCH[1]}" + echo "$ver:$file" >>"$VERSION_MAP_FILE" + fi + done <<<"$DEB_FILES_RAW" + + # 读取排序并去重后的版本号 + mapfile -t VERSIONS < <(cut -d':' -f1 "$VERSION_MAP_FILE" | sort -V -u) + + # 确保有可用版本 + if [ ${#VERSIONS[@]} -eq 0 ]; then + echo "未找到可用的cloud内核版本,请检查网络或反馈。" + exit 1 + fi + + echo "检测到 $DISTRO 系统(架构 $ARCH),以下是从 Debian 签名cloud内核列表中获取的版本(按从小到大排序,已去重):" + for i in "${!VERSIONS[@]}"; do + echo " $i) [${VERSIONS[$i]}]" + done + + # 默认选择最新版本 + local DEFAULT_INDEX=$((${#VERSIONS[@]} - 1)) + echo "请选择要安装的cloud内核版本(10秒后默认选择最新版本回车加速 ${VERSIONS[$DEFAULT_INDEX]},输入'h'则使用apt安装非最新cloud及headers):" + read -t 10 -p "输入选项编号或'h': " CHOICE + + # 检查是否使用 apt 安装 cloud 及 headers + local USE_APT=false + if [[ "$CHOICE" =~ ^[hH]$ ]]; then + USE_APT=true + if [ "$DISTRO" != "debian" ]; then + echo "错误:使用 'h' 安装 headers 仅支持 Debian 系统,当前系统为 $DISTRO" + exit 1 + fi + CHOICE=$DEFAULT_INDEX + else + CHOICE=${CHOICE:-$DEFAULT_INDEX} + fi + + # 验证输入 + if [[ ! "$CHOICE" =~ ^[0-9]+$ ]] || [ "$CHOICE" -lt 0 ] || [ "$CHOICE" -ge "${#VERSIONS[@]}" ]; then + echo "无效选项,默认安装最新版本 ${VERSIONS[$DEFAULT_INDEX]}..." + CHOICE=$DEFAULT_INDEX + fi + + local SELECTED_VERSION="${VERSIONS[$CHOICE]}" + local IMAGE_DEB_FILE=$(grep "^$SELECTED_VERSION:" "$VERSION_MAP_FILE" | tail -n 1 | cut -d':' -f2) + + kernel_version=$SELECTED_VERSION + + # 如果选择 'h',使用 apt 安装 cloud 内核及 headers + if [ "$USE_APT" = true ]; then + echo "正在使用 apt 安装 linux-image-cloud-${ARCH} 及 headers..." + sudo apt update + if [ "$ARCH" == "x86_64" ]; then + sudo apt install -y "linux-image-cloud-amd64" "linux-headers-cloud-amd64" + elif [ "$ARCH" == "aarch64" ]; then + sudo apt install -y "linux-image-cloud-arm64" "linux-headers-cloud-arm64" + fi + else + # 下载并安装 image + echo "正在下载 $IMAGE_URL$IMAGE_DEB_FILE ..." + curl -O "$IMAGE_URL$IMAGE_DEB_FILE" + echo "正在安装 $IMAGE_DEB_FILE ..." + sudo dpkg -i "$IMAGE_DEB_FILE" + sudo apt-get install -f -y # 解决可能的依赖问题 + fi + + # 清理下载的文件 + rm -f "$IMAGE_DEB_FILE" "$VERSION_MAP_FILE" + + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBRplus${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBRplus,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + +} + +#启用BBR+fq +startbbrfq() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR+FQ修改成功,重启生效!" +} + +#启用BBR+fq_pie +startbbrfqpie() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq_pie" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR+FQ_PIE修改成功,重启生效!" +} + +#启用BBR+cake +startbbrcake() { + remove_bbr_lotserver + echo "net.core.default_qdisc=cake" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR+cake修改成功,重启生效!" +} + +#启用BBRplus +startbbrplus() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbrplus" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBRplus修改成功,重启生效!" +} + +#启用Lotserver +startlotserver() { + remove_bbr_lotserver + if [[ "${OS_type}" == "CentOS" ]]; then + yum install ethtool -y + else + apt-get update || apt-get --allow-releaseinfo-change update + apt-get install ethtool -y + fi + #bash <(wget -qO- https://git.io/lotServerInstall.sh) install + #echo | bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/1265578519/lotServer/main/lotServerInstall.sh) install + echo | bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh) install + sed -i '/advinacc/d' /appex/etc/config + sed -i '/maxmode/d' /appex/etc/config + echo -e "advinacc=\"1\" +maxmode=\"1\"" >>/appex/etc/config + /appex/bin/lotServer.sh restart + start_menu +} + +#启用BBR2+FQ +startbbr2fq() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR2修改成功,重启生效!" +} + +#启用BBR2+FQ_PIE +startbbr2fqpie() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq_pie" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR2修改成功,重启生效!" +} + +#启用BBR2+CAKE +startbbr2cake() { + remove_bbr_lotserver + echo "net.core.default_qdisc=cake" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR2修改成功,重启生效!" +} + +#开启ecn +startecn() { + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + + echo "net.ipv4.tcp_ecn=1" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}开启ecn结束!" +} + +#关闭ecn +closeecn() { + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + + echo "net.ipv4.tcp_ecn=0" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}关闭ecn结束!" +} + +#卸载bbr+锐速 +remove_bbr_lotserver() { + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf + sysctl --system + + rm -rf bbrmod + + if [[ -e /appex/bin/lotServer.sh ]]; then + echo | bash <(wget -qO- https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh) uninstall + fi + clear + # echo -e "${Info}:清除bbr/lotserver加速完成。" + # sleep 1s +} + +#卸载全部加速 +remove_all() { + rm -rf /etc/sysctl.d/*.conf + #rm -rf /etc/sysctl.conf + #touch /etc/sysctl.conf + if [ ! -f "/etc/sysctl.conf" ]; then + touch /etc/sysctl.conf + else + cat /dev/null >/etc/sysctl.conf + fi + sysctl --system + sed -i '/DefaultTimeoutStartSec/d' /etc/systemd/system.conf + sed -i '/DefaultTimeoutStopSec/d' /etc/systemd/system.conf + sed -i '/DefaultRestartSec/d' /etc/systemd/system.conf + sed -i '/DefaultLimitCORE/d' /etc/systemd/system.conf + sed -i '/DefaultLimitNOFILE/d' /etc/systemd/system.conf + sed -i '/DefaultLimitNPROC/d' /etc/systemd/system.conf + + sed -i '/soft nofile/d' /etc/security/limits.conf + sed -i '/hard nofile/d' /etc/security/limits.conf + sed -i '/soft nproc/d' /etc/security/limits.conf + sed -i '/hard nproc/d' /etc/security/limits.conf + + sed -i '/ulimit -SHn/d' /etc/profile + sed -i '/ulimit -SHn/d' /etc/profile + sed -i '/required pam_limits.so/d' /etc/pam.d/common-session + + systemctl daemon-reload + + rm -rf bbrmod + sed -i '/net.ipv4.tcp_retries2/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_slow_start_after_idle/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf + sed -i '/fs.file-max/d' /etc/sysctl.conf + sed -i '/net.core.rmem_max/d' /etc/sysctl.conf + sed -i '/net.core.wmem_max/d' /etc/sysctl.conf + sed -i '/net.core.rmem_default/d' /etc/sysctl.conf + sed -i '/net.core.wmem_default/d' /etc/sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_recycle/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_time/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_rmem/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_wmem/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_mtu_probing/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.conf + sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.conf + sed -i '/net.ipv4.route.gc_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_synack_retries/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syn_retries/d' /etc/sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_timestamps/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_orphans/d' /etc/sysctl.conf + if [[ -e /appex/bin/lotServer.sh ]]; then + bash <(wget -qO- https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh) uninstall + fi + clear + echo -e "${Info}:清除加速完成。" + sleep 1s +} + +optimizing_ddcc() { + sed -i '/net.ipv4.conf.all.rp_filter/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.d/99-sysctl.conf + + echo "net.ipv4.conf.all.rp_filter = 1" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_syncookies = 1" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_max_syn_backlog = 1024" >>/etc/sysctl.d/99-sysctl.conf + sysctl -p + sysctl --system +} + +#更新脚本 +Update_Shell() { + local shell_file + shell_file="$(readlink -f "$0")" + local shell_url="https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcp.sh" + + # 下载最新版本的脚本 + wget -O "/tmp/tcp.sh" "$shell_url" &>/dev/null + + # 比较本地和远程脚本的 md5 值 + local md5_local + local md5_remote + md5_local="$(md5sum "$shell_file" | awk '{print $1}')" + md5_remote="$(md5sum /tmp/tcp.sh | awk '{print $1}')" + + if [ "$md5_local" != "$md5_remote" ]; then + # 替换本地脚本文件 + cp "/tmp/tcp.sh" "$shell_file" + chmod +x "$shell_file" + + echo "脚本已更新,请重新运行。" + exit 0 + else + echo "脚本是最新版本,无需更新。" + fi +} + +#切换到不卸载内核版本 +gototcpx() { + clear + #wget -O tcpx.sh "https://github.com/ylx2016/Linux-NetSpeed/raw/master/tcpx.sh" && chmod +x tcpx.sh && ./tcpx.sh + bash <(wget -qO- https://github.com/ylx2016/Linux-NetSpeed/raw/master/tcpx.sh) +} + +#切换到秋水逸冰BBR安装脚本 +gototeddysun_bbr() { + clear + #wget https://github.com/teddysun/across/raw/master/bbr.sh && chmod +x bbr.sh && ./bbr.sh + bash <(wget -qO- https://github.com/teddysun/across/raw/master/bbr.sh) +} + +#切换到一键DD安装系统脚本 新手勿入 +gotodd() { + clear + echo DD使用git.beta.gs的脚本,知悉 + sleep 1.5 + #wget -O NewReinstall.sh https://github.com/fcurrk/reinstall/raw/master/NewReinstall.sh && chmod a+x NewReinstall.sh && bash NewReinstall.sh + bash <(wget -qO- https://github.com/fcurrk/reinstall/raw/master/NewReinstall.sh) + #wget -qO ~/Network-Reinstall-System-Modify.sh 'https://github.com/ylx2016/reinstall/raw/master/Network-Reinstall-System-Modify.sh' && chmod a+x ~/Network-Reinstall-System-Modify.sh && bash ~/Network-Reinstall-System-Modify.sh -UI_Options +} + +#切换到检查当前IP质量/媒体解锁/邮箱通信脚本 +gotoipcheck() { + clear + sleep 1.5 + bash <(wget -qO- https://raw.githubusercontent.com/xykt/IPQuality/main/ip.sh) + #bash <(wget -qO- https://IP.Check.Place) +} + +#禁用IPv6 +closeipv6() { + clear + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.conf + + echo "net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 +net.ipv6.conf.lo.disable_ipv6 = 1" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}禁用IPv6结束,可能需要重启!" +} + +#开启IPv6 +openipv6() { + clear + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.all.accept_ra/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.accept_ra/d' /etc/sysctl.conf + + echo "net.ipv6.conf.all.disable_ipv6 = 0 +net.ipv6.conf.default.disable_ipv6 = 0 +net.ipv6.conf.lo.disable_ipv6 = 0 +net.ipv6.conf.all.accept_ra = 2 +net.ipv6.conf.default.accept_ra = 2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}开启IPv6结束,可能需要重启!" +} + +#开始菜单 +start_menu() { + clear + echo && echo -e " TCP加速 一键安装管理脚本 ${Red_font_prefix}[v${sh_ver}]${Font_color_suffix} from blog.ylx.me 母鸡慎用 + ${Green_font_prefix}0.${Font_color_suffix} 升级脚本 + ${Green_font_prefix}9.${Font_color_suffix} 切换到不卸载内核版本 ${Green_font_prefix}10.${Font_color_suffix} 切换到一键DD系统脚本 + ${Green_font_prefix}60.${Font_color_suffix} 切换到检查当前IP质量/媒体解锁/邮箱通信脚本 + ${Green_font_prefix}1.${Font_color_suffix} 安装 BBR原版内核 + ${Green_font_prefix}2.${Font_color_suffix} 安装 BBRplus版内核 ${Green_font_prefix}5.${Font_color_suffix} 安装 BBRplus新版内核 + ${Green_font_prefix}3.${Font_color_suffix} 安装 Lotserver(锐速)内核 + ${Green_font_prefix}8.${Font_color_suffix} 安装 官方cloud内核 (支持debian系列) + ${Green_font_prefix}11.${Font_color_suffix} 使用BBR+FQ加速 ${Green_font_prefix}12.${Font_color_suffix} 使用BBR+FQ_PIE加速 + ${Green_font_prefix}13.${Font_color_suffix} 使用BBR+CAKE加速 ${Green_font_prefix}14.${Font_color_suffix} 使用BBR2+FQ加速 + ${Green_font_prefix}15.${Font_color_suffix} 使用BBR2+FQ_PIE加速 ${Green_font_prefix}16.${Font_color_suffix} 使用BBR2+CAKE加速 + ${Green_font_prefix}17.${Font_color_suffix} 开启ECN ${Green_font_prefix}18.${Font_color_suffix} 关闭ECN + ${Green_font_prefix}19.${Font_color_suffix} 使用BBRplus+FQ版加速 + ${Green_font_prefix}20.${Font_color_suffix} 使用Lotserver(锐速)加速 + ${Green_font_prefix}21.${Font_color_suffix} 系统配置优化旧 ${Green_font_prefix}22.${Font_color_suffix} 系统配置优化新 + ${Green_font_prefix}27.${Font_color_suffix} 系统配置优化激进方案 + ${Green_font_prefix}23.${Font_color_suffix} 禁用IPv6 ${Green_font_prefix}24.${Font_color_suffix} 开启IPv6 + ${Green_font_prefix}25.${Font_color_suffix} 卸载全部加速 ${Green_font_prefix}99.${Font_color_suffix} 退出脚本 +————————————————————————————————————————————————————————————————" && + check_status + get_system_info + echo -e " 系统信息: ${Font_color_suffix}$opsy ${Green_font_prefix}$virtual${Font_color_suffix} $arch ${Green_font_prefix}$kern${Font_color_suffix} " + if [[ ${kernel_status} == "noinstall" ]]; then + echo -e " 当前状态: ${Green_font_prefix}未安装${Font_color_suffix} 加速内核 ${Red_font_prefix}请先安装内核${Font_color_suffix}" + else + echo -e " 当前状态: ${Green_font_prefix}已安装${Font_color_suffix} ${Red_font_prefix}${kernel_status}${Font_color_suffix} 加速内核 , ${Green_font_prefix}${run_status}${Font_color_suffix}" + + fi + echo -e " 当前拥塞控制算法为: ${Green_font_prefix}${net_congestion_control}${Font_color_suffix} 当前队列算法为: ${Green_font_prefix}${net_qdisc}${Font_color_suffix} " + + read -p " 请输入数字 :" num + case "$num" in + 0) + Update_Shell + ;; + 1) + check_sys_bbr + ;; + 2) + check_sys_bbrplus + ;; + 3) + check_sys_Lotsever + ;; + 5) + check_sys_bbrplusnew + ;; + 8) + check_sys_cloud + ;; + 9) + gototcpx + ;; + 10) + gotodd + ;; + 60) + gotoipcheck + ;; + 11) + startbbrfq + ;; + 12) + startbbrfqpie + ;; + 13) + startbbrcake + ;; + 14) + startbbr2fq + ;; + 15) + startbbr2fqpie + ;; + 16) + startbbr2cake + ;; + 17) + startecn + ;; + 18) + closeecn + ;; + 19) + startbbrplus + ;; + 20) + startlotserver + ;; + 21) + optimizing_system + ;; + 22) + optimizing_system_johnrosen1 + ;; + 23) + closeipv6 + ;; + 24) + openipv6 + ;; + 25) + remove_all + ;; + 26) + optimizing_ddcc + ;; + 27) + optimizing_system_radicalizate + ;; + 99) + exit 1 + ;; + *) + clear + echo -e "${Error}:请输入正确数字 [0-99]" + sleep 5s + start_menu + ;; + esac +} +#############内核管理组件############# + +#删除多余内核 +detele_kernel() { + if [[ "${OS_type}" == "CentOS" ]]; then + rpm_total=$(rpm -qa | grep kernel | grep -v "${kernel_version}" | grep -v "noarch" | wc -l) + if [ "${rpm_total}" ] >"1"; then + echo -e "检测到 ${rpm_total} 个其余内核,开始卸载..." + for ((integer = 1; integer <= ${rpm_total}; integer++)); do + rpm_del=$(rpm -qa | grep kernel | grep -v "${kernel_version}" | grep -v "noarch" | head -${integer}) + echo -e "开始卸载 ${rpm_del} 内核..." + rpm --nodeps -e ${rpm_del} + echo -e "卸载 ${rpm_del} 内核卸载完成,继续..." + done + echo --nodeps -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + deb_total=$(dpkg -l | grep linux-image | awk '{print $2}' | grep -v "${kernel_version}" | wc -l) + if [ "${deb_total}" ] >"1"; then + echo -e "检测到 ${deb_total} 个其余内核,开始卸载..." + for ((integer = 1; integer <= ${deb_total}; integer++)); do + deb_del=$(dpkg -l | grep linux-image | awk '{print $2}' | grep -v "${kernel_version}" | head -${integer}) + echo -e "开始卸载 ${deb_del} 内核..." + apt-get purge -y ${deb_del} + apt-get autoremove -y + echo -e "卸载 ${deb_del} 内核卸载完成,继续..." + done + echo -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + fi +} + +detele_kernel_head() { + if [[ "${OS_type}" == "CentOS" ]]; then + rpm_total=$(rpm -qa | grep kernel-headers | grep -v "${kernel_version}" | grep -v "noarch" | wc -l) + if [ "${rpm_total}" ] >"1"; then + echo -e "检测到 ${rpm_total} 个其余head内核,开始卸载..." + for ((integer = 1; integer <= ${rpm_total}; integer++)); do + rpm_del=$(rpm -qa | grep kernel-headers | grep -v "${kernel_version}" | grep -v "noarch" | head -${integer}) + echo -e "开始卸载 ${rpm_del} headers内核..." + rpm --nodeps -e ${rpm_del} + echo -e "卸载 ${rpm_del} 内核卸载完成,继续..." + done + echo --nodeps -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + deb_total=$(dpkg -l | grep linux-headers | awk '{print $2}' | grep -v "${kernel_version}" | wc -l) + if [ "${deb_total}" ] >"1"; then + echo -e "检测到 ${deb_total} 个其余head内核,开始卸载..." + for ((integer = 1; integer <= ${deb_total}; integer++)); do + deb_del=$(dpkg -l | grep linux-headers | awk '{print $2}' | grep -v "${kernel_version}" | head -${integer}) + echo -e "开始卸载 ${deb_del} headers内核..." + apt-get purge -y ${deb_del} + apt-get autoremove -y + echo -e "卸载 ${deb_del} 内核卸载完成,继续..." + done + echo -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + fi +} + +#更新引导 +BBR_grub() { + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "6" ]]; then + if [ -f "/boot/grub/grub.conf" ]; then + sed -i 's/^default=.*/default=0/g' /boot/grub/grub.conf + elif [ -f "/boot/grub/grub.cfg" ]; then + grub-mkconfig -o /boot/grub/grub.cfg + grub-set-default 0 + elif [ -f "/boot/efi/EFI/centos/grub.cfg" ]; then + grub-mkconfig -o /boot/efi/EFI/centos/grub.cfg + grub-set-default 0 + elif [ -f "/boot/efi/EFI/redhat/grub.cfg" ]; then + grub-mkconfig -o /boot/efi/EFI/redhat/grub.cfg + grub-set-default 0 + else + echo -e "${Error} grub.conf/grub.cfg 找不到,请检查." + exit + fi + elif [[ ${version} == "7" ]]; then + if [ -f "/boot/grub2/grub.cfg" ]; then + grub2-mkconfig -o /boot/grub2/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/centos/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/redhat/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg + grub2-set-default 0 + else + echo -e "${Error} grub.cfg 找不到,请检查." + exit + fi + elif [[ ${version} == "8" ]]; then + if [ -f "/boot/grub2/grub.cfg" ]; then + grub2-mkconfig -o /boot/grub2/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/centos/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/redhat/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg + grub2-set-default 0 + else + echo -e "${Error} grub.cfg 找不到,请检查." + exit + fi + grubby --info=ALL | awk -F= '$1=="kernel" {print i++ " : " $2}' + fi + elif [[ "${OS_type}" == "Debian" ]]; then + if _exists "update-grub"; then + update-grub + elif [ -f "/usr/sbin/update-grub" ]; then + /usr/sbin/update-grub + else + apt install grub2-common -y + update-grub + fi + #exit 1 + fi +} + +#简单的检查内核 +check_kernel() { + if [[ -z "$(find /boot -type f -name 'vmlinuz-*' ! -name 'vmlinuz-*rescue*')" ]]; then + echo -e "\033[0;31m警告: 未发现内核文件,请勿重启系统,不卸载内核版本选择30安装默认内核救急!\033[0m" + else + echo -e "\033[0;32m发现内核文件,看起来可以重启。\033[0m" + fi +} + +#############内核管理组件############# + +#############系统检测组件############# + +#检查系统 +check_sys() { + if [[ -f /etc/redhat-release ]]; then + release="centos" + elif grep -qi "debian" /etc/issue; then + release="debian" + elif grep -qi "ubuntu" /etc/issue; then + release="ubuntu" + elif grep -qi -E "centos|red hat|redhat" /etc/issue || grep -qi -E "centos|red hat|redhat" /proc/version; then + release="centos" + fi + + if [[ -f /etc/debian_version ]]; then + OS_type="Debian" + echo "检测为Debian通用系统,判断有误请反馈" + elif [[ -f /etc/redhat-release || -f /etc/centos-release || -f /etc/fedora-release ]]; then + OS_type="CentOS" + echo "检测为CentOS通用系统,判断有误请反馈" + else + echo "Unknown" + fi + + #from https://github.com/oooldking + + _exists() { + local cmd="$1" + if eval type type >/dev/null 2>&1; then + eval type "$cmd" >/dev/null 2>&1 + elif command >/dev/null 2>&1; then + command -v "$cmd" >/dev/null 2>&1 + else + which "$cmd" >/dev/null 2>&1 + fi + local rt=$? + return ${rt} + } + + get_opsy() { + if [ -f /etc/os-release ]; then + awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release + elif [ -f /etc/lsb-release ]; then + awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release + elif [ -f /etc/system-release ]; then + cat /etc/system-release | awk '{print $1,$2}' + fi + } + + get_system_info() { + opsy=$(get_opsy) + arch=$(uname -m) + kern=$(uname -r) + virt_check + } + # from LemonBench + virt_check() { + if [ -f "/usr/bin/systemd-detect-virt" ]; then + Var_VirtType="$(/usr/bin/systemd-detect-virt)" + # 虚拟机检测 + if [ "${Var_VirtType}" = "qemu" ]; then + virtual="QEMU" + elif [ "${Var_VirtType}" = "kvm" ]; then + virtual="KVM" + elif [ "${Var_VirtType}" = "zvm" ]; then + virtual="S390 Z/VM" + elif [ "${Var_VirtType}" = "vmware" ]; then + virtual="VMware" + elif [ "${Var_VirtType}" = "microsoft" ]; then + virtual="Microsoft Hyper-V" + elif [ "${Var_VirtType}" = "xen" ]; then + virtual="Xen Hypervisor" + elif [ "${Var_VirtType}" = "bochs" ]; then + virtual="BOCHS" + elif [ "${Var_VirtType}" = "uml" ]; then + virtual="User-mode Linux" + elif [ "${Var_VirtType}" = "parallels" ]; then + virtual="Parallels" + elif [ "${Var_VirtType}" = "bhyve" ]; then + virtual="FreeBSD Hypervisor" + # 容器虚拟化检测 + elif [ "${Var_VirtType}" = "openvz" ]; then + virtual="OpenVZ" + elif [ "${Var_VirtType}" = "lxc" ]; then + virtual="LXC" + elif [ "${Var_VirtType}" = "lxc-libvirt" ]; then + virtual="LXC (libvirt)" + elif [ "${Var_VirtType}" = "systemd-nspawn" ]; then + virtual="Systemd nspawn" + elif [ "${Var_VirtType}" = "docker" ]; then + virtual="Docker" + elif [ "${Var_VirtType}" = "rkt" ]; then + virtual="RKT" + # 特殊处理 + elif [ -c "/dev/lxss" ]; then # 处理WSL虚拟化 + Var_VirtType="wsl" + virtual="Windows Subsystem for Linux (WSL)" + # 未匹配到任何结果, 或者非虚拟机 + elif [ "${Var_VirtType}" = "none" ]; then + Var_VirtType="dedicated" + virtual="None" + local Var_BIOSVendor + Var_BIOSVendor="$(dmidecode -s bios-vendor)" + if [ "${Var_BIOSVendor}" = "SeaBIOS" ]; then + Var_VirtType="Unknown" + virtual="Unknown with SeaBIOS BIOS" + else + Var_VirtType="dedicated" + virtual="Dedicated with ${Var_BIOSVendor} BIOS" + fi + fi + elif [ ! -f "/usr/sbin/virt-what" ]; then + Var_VirtType="Unknown" + virtual="[Error: virt-what not found !]" + elif [ -f "/.dockerenv" ]; then # 处理Docker虚拟化 + Var_VirtType="docker" + virtual="Docker" + elif [ -c "/dev/lxss" ]; then # 处理WSL虚拟化 + Var_VirtType="wsl" + virtual="Windows Subsystem for Linux (WSL)" + else # 正常判断流程 + Var_VirtType="$(virt-what | xargs)" + local Var_VirtTypeCount + Var_VirtTypeCount="$(echo $Var_VirtTypeCount | wc -l)" + if [ "${Var_VirtTypeCount}" -gt "1" ]; then # 处理嵌套虚拟化 + virtual="echo ${Var_VirtType}" + Var_VirtType="$(echo ${Var_VirtType} | head -n1)" # 使用检测到的第一种虚拟化继续做判断 + elif [ "${Var_VirtTypeCount}" -eq "1" ] && [ "${Var_VirtType}" != "" ]; then # 只有一种虚拟化 + virtual="${Var_VirtType}" + else + local Var_BIOSVendor + Var_BIOSVendor="$(dmidecode -s bios-vendor)" + if [ "${Var_BIOSVendor}" = "SeaBIOS" ]; then + Var_VirtType="Unknown" + virtual="Unknown with SeaBIOS BIOS" + else + Var_VirtType="dedicated" + virtual="Dedicated with ${Var_BIOSVendor} BIOS" + fi + fi + fi + } + + #检查依赖 + if [[ "${OS_type}" == "CentOS" ]]; then + # 检查是否安装了 ca-certificates 包,如果未安装则安装 + if ! rpm -q ca-certificates >/dev/null; then + echo '正在安装 ca-certificates 包...' + yum install ca-certificates -y + update-ca-trust force-enable + fi + echo 'CA证书检查OK' + + # 检查并安装 curl、wget 和 dmidecode 包 + for pkg in curl wget dmidecode redhat-lsb-core; do + if ! type $pkg >/dev/null 2>&1; then + echo "未安装 $pkg,正在安装..." + yum install $pkg -y + else + echo "$pkg 已安装。" + fi + done + + if [ -x "$(command -v lsb_release)" ]; then + echo "lsb_release 已安装" + else + echo "lsb_release 未安装,现在开始安装..." + yum install epel-release -y + yum install redhat-lsb-core -y + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + # 检查是否安装了 ca-certificates 包,如果未安装则安装 + if ! dpkg-query -W ca-certificates >/dev/null; then + echo '正在安装 ca-certificates 包...' + apt-get update || apt-get --allow-releaseinfo-change update && apt-get install ca-certificates -y + update-ca-certificates + fi + echo 'CA证书检查OK' + + # 检查并安装 curl、wget 和 dmidecode 包 + for pkg in curl wget dmidecode; do + if ! type $pkg >/dev/null 2>&1; then + echo "未安装 $pkg,正在安装..." + apt-get update || apt-get --allow-releaseinfo-change update && apt-get install $pkg -y + else + echo "$pkg 已安装。" + fi + done + + if [ -x "$(command -v lsb_release)" ]; then + echo "lsb_release 已安装" + else + echo "lsb_release 未安装,现在开始安装..." + apt-get install lsb-release -y + fi + + else + echo "不支持的操作系统发行版:${release}" + exit 1 + fi +} + +#检查Linux版本 +check_version() { + if [[ -s /etc/redhat-release ]]; then + version=$(grep -oE "[0-9.]+" /etc/redhat-release | cut -d . -f 1) + else + version=$(grep -oE "[0-9.]+" /etc/issue | cut -d . -f 1) + fi + bit=$(uname -m) + check_github +} + +#检查安装bbr的系统要求 +check_sys_bbr() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + installbbr + else + echo -e "${Error} BBR内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installbbr + else + echo -e "${Error} BBR内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_bbrplus() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + installbbrplus + else + echo -e "${Error} BBRplus内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installbbrplus + else + echo -e "${Error} BBRplus内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_bbrplusnew() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + #if [[ ${version} == "7" ]]; then + if [[ ${version} == "7" || ${version} == "8" ]]; then + installbbrplusnew + else + echo -e "${Error} BBRplusNew内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installbbrplusnew + else + echo -e "${Error} BBRplusNew内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_xanmod() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" || ${version} == "8" ]]; then + installxanmod + else + echo -e "${Error} xanmod内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installxanmod + else + echo -e "${Error} xanmod内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_cloud() { + check_version + if [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installcloud + else + echo -e "${Error} cloud内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +#检查安装Lotsever的系统要求 +check_sys_Lotsever() { + check_version + bit=$(uname -m) + if [[ ${bit} != "x86_64" ]]; then + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "6" ]]; then + kernel_version="2.6.32-504" + installlot + elif [[ ${version} == "7" ]]; then + yum -y install net-tools + kernel_version="4.11.2-1" + installlot + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${release}" == "debian" ]]; then + if [[ ${version} == "7" || ${version} == "8" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="3.16.0-4" + installlot + elif [[ ${bit} == "i386" ]]; then + kernel_version="3.2.0-4" + installlot + fi + elif [[ ${version} == "9" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.9.0-4" + installlot + fi + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${release}" == "ubuntu" ]]; then + if [[ ${version} -ge "12" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.4.0-47" + installlot + elif [[ ${bit} == "i386" ]]; then + kernel_version="3.13.0-29" + installlot + fi + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +#检查系统当前状态 +check_status() { + kernel_version=$(uname -r | awk -F "-" '{print $1}') + kernel_version_full=$(uname -r) + net_congestion_control=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + net_qdisc=$(cat /proc/sys/net/core/default_qdisc | awk '{print $1}') + if [[ ${kernel_version_full} == *bbrplus* ]]; then + kernel_status="BBRplus" + elif [[ ${kernel_version_full} == *4.9.0-4* || ${kernel_version_full} == *4.15.0-30* || ${kernel_version_full} == *4.8.0-36* || ${kernel_version_full} == *3.16.0-77* || ${kernel_version_full} == *3.16.0-4* || ${kernel_version_full} == *3.2.0-4* || ${kernel_version_full} == *4.11.2-1* || ${kernel_version_full} == *2.6.32-504* || ${kernel_version_full} == *4.4.0-47* || ${kernel_version_full} == *3.13.0-29 || ${kernel_version_full} == *4.4.0-47* ]]; then + kernel_status="Lotserver" + elif [[ $(echo ${kernel_version} | awk -F'.' '{print $1}') == "4" ]] && [[ $(echo ${kernel_version} | awk -F'.' '{print $2}') -ge 9 ]] || [[ $(echo ${kernel_version} | awk -F'.' '{print $1}') == "5" ]] || [[ $(echo ${kernel_version} | awk -F'.' '{print $1}') == "6" ]]; then + kernel_status="BBR" + else + kernel_status="noinstall" + fi + + if [[ ${kernel_status} == "BBR" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbr" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbr" ]]; then + run_status="BBR启动成功" + else + run_status="BBR启动失败" + fi + elif [[ ${run_status} == "bbr2" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbr2" ]]; then + run_status="BBR2启动成功" + else + run_status="BBR2启动失败" + fi + elif [[ ${run_status} == "tsunami" ]]; then + run_status=$(lsmod | grep "tsunami" | awk '{print $1}') + if [[ ${run_status} == "tcp_tsunami" ]]; then + run_status="BBR魔改版启动成功" + else + run_status="BBR魔改版启动失败" + fi + elif [[ ${run_status} == "nanqinlang" ]]; then + run_status=$(lsmod | grep "nanqinlang" | awk '{print $1}') + if [[ ${run_status} == "tcp_nanqinlang" ]]; then + run_status="暴力BBR魔改版启动成功" + else + run_status="暴力BBR魔改版启动失败" + fi + else + run_status="未安装加速模块" + fi + + elif [[ ${kernel_status} == "Lotserver" ]]; then + if [[ -e /appex/bin/lotServer.sh ]]; then + run_status=$(bash /appex/bin/lotServer.sh status | grep "LotServer" | awk '{print $3}') + if [[ ${run_status} == "running!" ]]; then + run_status="启动成功" + else + run_status="启动失败" + fi + else + run_status="未安装加速模块" + fi + elif [[ ${kernel_status} == "BBRplus" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbrplus" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbrplus" ]]; then + run_status="BBRplus启动成功" + else + run_status="BBRplus启动失败" + fi + elif [[ ${run_status} == "bbr" ]]; then + run_status="BBR启动成功" + else + run_status="未安装加速模块" + fi + fi +} + +#############系统检测组件############# +check_sys +check_version +[[ "${OS_type}" == "Debian" ]] && [[ "${OS_type}" == "CentOS" ]] && echo -e "${Error} 本脚本不支持当前系统 ${release} !" && exit 1 +check_github +start_menu diff --git a/tcp_react_rc2.c b/tcp_react_rc2.c new file mode 100644 index 0000000..a9d686d --- /dev/null +++ b/tcp_react_rc2.c @@ -0,0 +1,433 @@ +/* React congestion control */ + +#include +#include +#include + +#define BW_SCALE 24 + +#define REACT_SCALE 8 +#define REACT_UNIT (1 << REACT_SCALE) + +#define REACT_INIT_CWND 25 + +#define DO_CONDITIONAL_OPT(a, b, c) ((c) ^ ((!(a) - 1) & ((b) ^ (c)))) + +#define REACT_MIN(a, b) DO_CONDITIONAL_OPT((a) < (b), a, b) +#define REACT_MAX(a, b) DO_CONDITIONAL_OPT((a) > (b), a, b) +#define REACT_MAX3(a, b, c) REACT_MAX(REACT_MAX(a, b), c) + +#define REACT_SGN32(x) -(-((x) >> 31) | (-(x) >> 31)) + +/* window length of min_rtt filter (in sec): */ +static const u32 react_min_rtt_win_sec = 10; + +static const int react_high_gain = REACT_UNIT * 2885 / 1000 + 1; +static const int react_drain_gain = REACT_UNIT * 3 / 4; +static const int react_cwnd_gain = REACT_UNIT * 2; +static const int react_probe_gain = REACT_UNIT * 3 / 2; + +static const u8 react_full_bw_cnt = 3; + +/* sampling windows size react_grad used for smoothing moving: */ +static unsigned int window __read_mostly = 4; +/* Window length of bw filter (in rounds): */ +static unsigned int react_bw_rtts __read_mostly = 15; + +module_param(window, int, 0444); +MODULE_PARM_DESC(window, "gradient window size (power of two <= 256)"); +module_param(react_bw_rtts, uint, 0644); +MODULE_PARM_DESC(react_bw_rtts, "window length of bw filter (in rounds)"); + +struct cdg_minmax { + union { + struct { + s32 min; + s32 max; + }; + u64 v64; + }; +}; + +enum react_state { + CDG_UNKNOWN = 0, + CDG_NONFULL = 1, + CDG_FULL = 2 +}; + +/* React congestion control block */ +struct react { + struct cdg_minmax rtt; + struct cdg_minmax rtt_prev; + struct cdg_minmax *gradients; + struct cdg_minmax gsum; + struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */ + u32 cwnd_gain, + pacing_gain, + min_rtt_us, /* min RTT in min_rtt_win_sec window */ + rtt_seq, + min_rtt_stamp, /* timestamp of min_rtt_us */ + next_rtt_delivered; /* scb->tx.delivered at end of round */ + u16 rtt_cnt; /* count of packet-timed rounds elapsed */ + u8 tail, + state, + full_bw_cnt; + bool drain_queue, + round_restart, + packet_conservation; +}; + +static inline u32 react_max_bw(struct react *ca) +{ + return minmax_get(&ca->bw); +} + +static inline u64 react_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain) +{ + rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache); + rate *= gain; + rate >>= REACT_SCALE; + rate *= USEC_PER_SEC; + return rate >> BW_SCALE; +} + +static inline void react_set_pacing_rate(struct sock *sk, u64 bw, int gain) +{ + u64 rate = bw; + + rate = react_rate_bytes_per_sec(sk, rate, gain); + rate = REACT_MIN(rate, sk->sk_max_pacing_rate); + sk->sk_pacing_rate = REACT_MAX(rate, sk->sk_pacing_rate); +} + +/* Find target cwnd. Right-size the cwnd based on min RTT and the +* estimated bottleneck bandwidth: +* +* cwnd = bw * min_rtt * gain = BDP * gain +* +* The key factor, gain, controls the amount of queue. While a small gain +* builds a smaller queue, it becomes more vulnerable to noise in RTT +* measurements (e.g., delayed ACKs or other ACK compression effects). This +* noise may cause BBR to under-estimate the rate. +*/ + +static u32 react_target_cwnd(struct sock *sk, struct react *ca, int gain) +{ + u64 w; + u32 bw, cwnd; + + if (unlikely(ca->min_rtt_us == ~0U)) /* no valid RTT samples yet? */ + return REACT_INIT_CWND; /* be safe: cap at default initial cwnd */ + + bw = react_max_bw(ca); + + w = (u64)bw * ca->min_rtt_us; + + cwnd = ((w * gain) >> (BW_SCALE + REACT_SCALE)) + 18; + + /* Reduce delayed ACKs by rounding up cwnd to the next even number. */ + cwnd = (cwnd + 1) & ~1U; + + return cwnd; +} + +static inline void react_set_cwnd(struct sock *sk, const struct rate_sample *rs, int gain) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct react *ca = inet_csk_ca(sk); + u32 cwnd = tp->snd_cwnd, target_cwnd; + + target_cwnd = DO_CONDITIONAL_OPT(ca->packet_conservation, REACT_MAX(cwnd, tcp_packets_in_flight(tp) + rs->acked_sacked), react_target_cwnd(sk, ca, gain)); + + cwnd = REACT_MAX(target_cwnd, 4); + + tp->snd_cwnd = REACT_MIN(cwnd, tp->snd_cwnd_clamp); + tp->rcv_ssthresh = TCP_INFINITE_SSTHRESH; + tp->rcv_wnd = REACT_MAX(cwnd, tp->rcv_wnd); + + ca->packet_conservation = 0; +} + + +static void react_check_drain(struct sock *sk, const struct rate_sample *rs, struct react *ca) +{ + bool non_cong = (ca->state != CDG_FULL); + + if (!ca->drain_queue && !ca->round_restart) { + struct tcp_sock *tp = tcp_sk(sk); + u32 inflight = REACT_MIN(tcp_packets_in_flight(tp), rs->prior_in_flight); + ca->cwnd_gain = DO_CONDITIONAL_OPT(non_cong, react_high_gain, react_cwnd_gain); + if (inflight < tp->snd_cwnd) + ca->pacing_gain = DO_CONDITIONAL_OPT(non_cong, react_high_gain, react_probe_gain); + else + ca->pacing_gain = DO_CONDITIONAL_OPT(non_cong, react_probe_gain, REACT_UNIT); + } + else if (ca->drain_queue && !ca->round_restart) { + ca->cwnd_gain = react_high_gain; + ca->pacing_gain = DO_CONDITIONAL_OPT(non_cong, REACT_UNIT, react_drain_gain); + ca->state = CDG_UNKNOWN; + ca->packet_conservation = 1; + } + ca->round_restart = 0; +} + +/* We use the delay gradient as a congestion signal. */ +static void react_grad(struct react *ca) +{ + s32 gmin = ca->rtt.min - ca->rtt_prev.min; + s32 gmax = ca->rtt.max - ca->rtt_prev.max; + + if (ca->gradients) { + ca->gsum.min += gmin - ca->gradients[ca->tail].min; + ca->gsum.max += gmax - ca->gradients[ca->tail].max; + ca->gradients[ca->tail].min = gmin; + ca->gradients[ca->tail].max = gmax; + ca->tail = (ca->tail + 1) & (window - 1); + gmin = ca->gsum.min; + gmax = ca->gsum.max; + } + + gmin += 32; + gmax += 32; + + if (gmin > 0 && gmax <= 0) + ca->state = CDG_FULL; + else if ((gmin > 0 && gmax > 0) || gmax < 0) { + ca->state = CDG_NONFULL; + ca->full_bw_cnt = 0; + } +} + +static void react_update_rtt_grad(struct sock *sk, const struct rate_sample *rs, struct react *ca) +{ + if (likely(rs->rtt_us)) { + ca->rtt.min = REACT_MIN(DO_CONDITIONAL_OPT(ca->rtt.min > 0, ca->rtt.min, 1), rs->rtt_us); + ca->rtt.max = REACT_MAX(ca->rtt.max, rs->rtt_us); + } + + if (after(tcp_sk(sk)->snd_una, ca->rtt_seq + 1) && ca->rtt.v64) { + if (ca->rtt_prev.v64) + react_grad(ca); + ca->rtt_seq = tcp_sk(sk)->snd_nxt; + ca->rtt_prev = ca->rtt; + ca->rtt.v64 = 0; + } +} + +static void react_update_min_rtt(struct sock *sk, const struct rate_sample *rs, struct react *ca) +{ + bool filter_expired; + + /* Track min RTT seen in the min_rtt_win_sec filter window: */ + filter_expired = after(tcp_time_stamp, + ca->min_rtt_stamp + react_min_rtt_win_sec * HZ); + if (rs->rtt_us >= 0 && + (rs->rtt_us <= ca->min_rtt_us || filter_expired)) { + ca->min_rtt_us = rs->rtt_us; + ca->min_rtt_stamp = tcp_time_stamp; + } + + ca->drain_queue = (filter_expired || (ca->state == CDG_FULL && ca->full_bw_cnt >= react_full_bw_cnt)); +} + +static void react_update_bw(struct sock *sk, const struct rate_sample *rs, struct react *ca) +{ + u64 bw, bw_thresh; + + if (rs->delivered < 0 || rs->interval_us <= 0) + return; /* Not a valid observation */ + + /* See if we've reached the next RTT */ + if (!before(rs->prior_delivered, ca->next_rtt_delivered)) { + ca->next_rtt_delivered = tcp_sk(sk)->delivered; + ca->rtt_cnt++; + } + + /* Divide delivered by the interval to find a (lower bound) bottleneck + * bandwidth sample. Delivered is in packets and interval_us in uS and + * ratio will be <<1 for most connections. So delivered is first scaled. + */ + bw = ((u64)rs->delivered << BW_SCALE); + do_div(bw, rs->interval_us); + + bw_thresh = (((u64)react_max_bw(ca) >> 3) * 9); + + ++ca->full_bw_cnt; + if (bw >= bw_thresh) { + ca->full_bw_cnt = 0; + ca->state = CDG_UNKNOWN; + } + + ca->full_bw_cnt = REACT_MIN(ca->full_bw_cnt, react_full_bw_cnt); + + /* If this sample is application-limited, it is likely to have a very + * low delivered count that represents application behavior rather than + * the available network rate. Such a sample could drag down estimated + * bw, causing needless slow-down. Thus, to continue to send at the + * last measured network rate, we filter out app-limited samples unless + * they describe the path bw at least as well as our bw model. + * + * So the goal during app-limited phase is to proceed with the best + * network rate no matter how long. We automatically leave this + * phase when app writes faster than the network can deliver :) + */ + if (!rs->is_app_limited || bw >= react_max_bw(ca)) { + /* Incorporate new sample into our max bw filter. */ + minmax_running_max(&ca->bw, react_bw_rtts, (u32)ca->rtt_cnt, bw); + } +} + + +static inline void react_update_model(struct sock *sk, const struct rate_sample *rs, struct react *ca) +{ + react_update_bw(sk, rs, ca); + react_update_min_rtt(sk, rs, ca); + react_update_rtt_grad(sk, rs, ca); + react_check_drain(sk, rs, ca); +} + +static void react_main(struct sock *sk, const struct rate_sample *rs) +{ + struct react *ca = inet_csk_ca(sk); + + react_update_model(sk, rs, ca); + + react_set_cwnd(sk, rs, ca->cwnd_gain); + react_set_pacing_rate(sk, react_max_bw(ca), ca->pacing_gain); +} + +static void react_set_state(struct sock *sk, u8 new_state) +{ + struct react *ca = inet_csk_ca(sk); + + switch (new_state) { + case TCP_CA_Loss: + if (ca->state != CDG_FULL) + /* Reset zero-window probe timer to push pending frames. */ + inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0, + tcp_probe0_base(sk), TCP_RTO_MAX); + ca->round_restart = 1; + ca->pacing_gain = react_high_gain; + ca->full_bw_cnt = 0; + break; + case TCP_CA_Recovery: + if (ca->state != CDG_NONFULL) { + ca->packet_conservation = 1; + ca->next_rtt_delivered = tcp_sk(sk)->delivered; + } + break; + default: + break; + } +} + +static void react_init(struct sock *sk) +{ + struct react *ca = inet_csk_ca(sk); + struct tcp_sock *tp = tcp_sk(sk); + + /* We silently fall back to window = 1 if allocation fails. */ + ca->gradients = kcalloc(window, sizeof(ca->gradients[0]), + GFP_NOWAIT | __GFP_NOWARN); + ca->rtt_seq = tp->snd_nxt; + + ca->min_rtt_stamp = tcp_time_stamp; + ca->min_rtt_us = tcp_min_rtt(tp); + + ca->state = CDG_NONFULL; + + ca->full_bw_cnt = 0; + + ca->rtt_cnt = 0; + ca->next_rtt_delivered = 0; + + ca->round_restart = 1; + ca->packet_conservation = 0; + + ca->pacing_gain = react_high_gain; + ca->cwnd_gain = react_high_gain; + + minmax_reset(&ca->bw, (u32)ca->rtt_cnt, 0); /* init max bw to 0 */ +} + +static void react_cwnd_event(struct sock *sk, const enum tcp_ca_event ev) +{ + struct react *ca = inet_csk_ca(sk); + struct cdg_minmax *gradients; + + switch (ev) { + case CA_EVENT_TX_START: + ca->state = CDG_NONFULL; + ca->pacing_gain = react_high_gain; + ca->cwnd_gain = react_high_gain; + ca->round_restart = 1; + break; + case CA_EVENT_CWND_RESTART: + gradients = ca->gradients; + if (gradients) + memset(gradients, 0, window * sizeof(gradients[0])); + memset(ca, 0, sizeof(*ca)); + ca->state = CDG_UNKNOWN; + ca->gradients = gradients; + ca->rtt_seq = tcp_sk(sk)->snd_nxt; + break; + default: + break; + } +} + +static u32 react_undo_cwnd(struct sock *sk) +{ + return tcp_sk(sk)->snd_cwnd; +} + +static void react_release(struct sock *sk) +{ + struct react *ca = inet_csk_ca(sk); + + kfree(ca->gradients); +} + +static u32 react_sndbuf_expand(struct sock *sk) +{ + return 3; +} + +static u32 react_ssthresh(struct sock *sk) +{ + return TCP_INFINITE_SSTHRESH; +} + +static struct tcp_congestion_ops react_cong_ops __read_mostly = { + .flags = TCP_CONG_NON_RESTRICTED, + .name = "react_rc2", + .owner = THIS_MODULE, + .init = react_init, + .cong_control = react_main, + .cwnd_event = react_cwnd_event, + .release = react_release, + .sndbuf_expand = react_sndbuf_expand, + .undo_cwnd = react_undo_cwnd, + .ssthresh = react_ssthresh, + .set_state = react_set_state, +}; + +static int __init react_register(void) +{ + BUILD_BUG_ON(sizeof(struct react) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&react_cong_ops); +} + +static void __exit react_unregister(void) +{ + tcp_unregister_congestion_control(&react_cong_ops); +} + +module_init(react_register); +module_exit(react_unregister); + +MODULE_AUTHOR("Neal Cardwell "); +MODULE_AUTHOR("Yuchung Cheng "); +MODULE_AUTHOR("Kenneth Klette Jonassen"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TCP React"); \ No newline at end of file diff --git a/tcp_tsunami.c b/tcp_tsunami.c new file mode 100644 index 0000000..4d6ffe8 --- /dev/null +++ b/tcp_tsunami.c @@ -0,0 +1,905 @@ +/* Bottleneck Bandwidth and RTT (BBR) congestion control + * + * BBR congestion control computes the sending rate based on the delivery + * rate (throughput) estimated from ACKs. In a nutshell: + * + * On each ACK, update our model of the network path: + * bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips) + * min_rtt = windowed_min(rtt, 10 seconds) + * pacing_rate = pacing_gain * bottleneck_bandwidth + * cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4) + * + * The core algorithm does not react directly to packet losses or delays, + * although BBR may adjust the size of next send per ACK when loss is + * observed, or adjust the sending rate if it estimates there is a + * traffic policer, in order to keep the drop rate reasonable. + * + * BBR is described in detail in: + * "BBR: Congestion-Based Congestion Control", + * Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, + * Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016. + * + * There is a public e-mail list for discussing BBR development and testing: + * https://groups.google.com/forum/#!forum/bbr-dev + * + * NOTE: BBR *must* be used with the fq qdisc ("man tc-fq") with pacing enabled, + * since pacing is integral to the BBR design and implementation. + * BBR without pacing would not function properly, and may incur unnecessary + * high packet loss rates. + */ +#include +#include +#include +#include +#include +#include + +/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth + * estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps. + * This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32. + * Since the minimum window is >=4 packets, the lower bound isn't + * an issue. The upper bound isn't an issue with existing technologies. + */ +#define BW_SCALE 24 +#define BW_UNIT (1 << BW_SCALE) + +#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */ +#define BBR_UNIT (1 << BBR_SCALE) + +/* BBR has the following modes for deciding how fast to send: */ +enum bbr_mode { + BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ + BBR_DRAIN, /* drain any queue created during startup */ + BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ + BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */ +}; + +/* BBR congestion control block */ +struct bbr { + u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */ +//deprecated u32 rtt_us; + u32 min_rtt_stamp; /* timestamp of min_rtt_us */ + u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */ +//deprecated struct minmax max_rtt; + struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */ + u32 rtt_cnt; /* count of packet-timed rounds elapsed */ + u32 next_rtt_delivered; /* scb->tx.delivered at end of round */ + struct skb_mstamp cycle_mstamp; /* time of this cycle phase start */ + u32 mode:3, /* current bbr_mode in state machine */ + prev_ca_state:3, /* CA state on previous ACK */ + packet_conservation:1, /* use packet conservation? */ + restore_cwnd:1, /* decided to revert cwnd to old value */ + round_start:1, /* start of packet-timed tx->ack round? */ + tso_segs_goal:7, /* segments we want in each skb we send */ + idle_restart:1, /* restarting after idle? */ + probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */ + unused:5, + lt_is_sampling:1, /* taking long-term ("LT") samples now? */ + lt_rtt_cnt:7, /* round trips in long-term interval */ + lt_use_bw:1; /* use lt_bw as our bw estimate? */ + u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */ + u32 lt_last_delivered; /* LT intvl start: tp->delivered */ + u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */ + u32 lt_last_lost; /* LT intvl start: tp->lost */ + u32 pacing_gain:10, /* current gain for setting pacing rate */ + cwnd_gain:10, /* current gain for setting cwnd */ + full_bw_cnt:3, /* number of rounds without large bw gains */ + cycle_idx:3, /* current index in pacing_gain cycle array */ + unused_b:6; + u32 prior_cwnd; /* prior cwnd upon entering loss recovery */ + u32 full_bw; /* recent bw, to estimate if pipe is full */ +}; + +#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */ + +/* Window length of bw filter (in rounds): */ +static const int bbr_bw_rtts = CYCLE_LEN + 7; +/* Window length of min_rtt filter (in sec): */ +static const u32 bbr_min_rtt_win_sec = 20; +/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */ +static const u32 bbr_probe_rtt_mode_ms = 200; +/* Skip TSO below the following bandwidth (bits/sec): */ +static const int bbr_min_tso_rate = 1200000; + +/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain + * that will allow a smoothly increasing pacing rate that will double each RTT + * and send the same number of packets per RTT that an un-paced, slow-starting + * Reno or CUBIC flow would: + */ +static const int bbr_high_gain = BBR_UNIT * 2885 / 1000 + 1; +/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain + * the queue created in BBR_STARTUP in a single round: + */ +static const int bbr_drain_gain = BBR_UNIT * 1200 / 2885; +/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */ +static const int bbr_cwnd_gain = BBR_UNIT * 2; +/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */ +static const int bbr_pacing_gain[] = { + BBR_UNIT * 3 / 2, /* probe for more available bw */ + BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */ + BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, /* cruise at 1.0*bw to utilize pipe, */ + BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8 /* without creating excess queue... */ +}; +/* Randomize the starting gain cycling phase over N phases: */ +static const u32 bbr_cycle_rand = 7; + +/* Try to keep at least this many packets in flight, if things go smoothly. For + * smooth functioning, a sliding window protocol ACKing every other packet + * needs at least 4 packets in flight: + */ +static const u32 bbr_cwnd_min_target = 4; + +/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */ +/* If bw has increased significantly (1.25x), there may be more bw available: */ +static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4; +/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */ +static const u32 bbr_full_bw_cnt = 3; + +/* "long-term" ("LT") bandwidth estimator parameters... */ +/* The minimum number of rounds in an LT bw sampling interval: */ +static const u32 bbr_lt_intvl_min_rtts = 4; +/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */ +static const u32 bbr_lt_loss_thresh = 50; +/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */ +static const u32 bbr_lt_bw_ratio = BBR_UNIT / 8; +/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */ +static const u32 bbr_lt_bw_diff = 4000 / 8; +/* If we estimate we're policed, use lt_bw for this many round trips: */ +static const u32 bbr_lt_bw_max_rtts = 48; + +/* Do we estimate that STARTUP filled the pipe? */ +static bool bbr_full_bw_reached(const struct sock *sk) +{ + const struct bbr *bbr = inet_csk_ca(sk); + + return bbr->full_bw_cnt >= bbr_full_bw_cnt; +} + +/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */ +static u32 bbr_max_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return minmax_get(&bbr->bw); +} + +/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */ +static u32 bbr_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk); +} + +/* Return rate in bytes per second, optionally with a gain. + * The order here is chosen carefully to avoid overflow of u64. This should + * work for input rates of up to 2.9Tbit/sec and gain of 2.89x. + */ +static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain) +{ + rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache); + rate *= gain; + rate >>= BBR_SCALE; + rate *= USEC_PER_SEC; + return rate >> BW_SCALE; +} + +/* Pace using current bw estimate and a gain factor. In order to help drive the + * network toward lower queues while maintaining high utilization and low + * latency, the average pacing rate aims to be slightly (~1%) lower than the + * estimated bandwidth. This is an important aspect of the design. In this + * implementation this slightly lower pacing rate is achieved implicitly by not + * including link-layer headers in the packet size used for the pacing rate. + */ +static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u64 rate = bw; + + rate = bbr_rate_bytes_per_sec(sk, rate, gain); + rate = min_t(u64, rate, sk->sk_max_pacing_rate); + if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate) + sk->sk_pacing_rate = rate; +} + +/* Return count of segments we want in the skbs we send, or 0 for default. */ +static u32 bbr_tso_segs_goal(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->tso_segs_goal; +} + +static void bbr_set_tso_segs_goal(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 min_segs; + + min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2; + bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs), + 0x7FU); +} + +/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */ +static void bbr_save_cwnd(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT) + bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */ + else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */ + bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd); +} + +static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (event == CA_EVENT_TX_START && tp->app_limited) { + bbr->idle_restart = 1; + /* Avoid pointless buffer overflows: pace at est. bw if we don't + * need more speed (we're restarting from idle and app-limited). + */ + if (bbr->mode == BBR_PROBE_BW) + bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT); + } +} + +/* Find target cwnd. Right-size the cwnd based on min RTT and the + * estimated bottleneck bandwidth: + * + * cwnd = bw * min_rtt * gain = BDP * gain + * + * The key factor, gain, controls the amount of queue. While a small gain + * builds a smaller queue, it becomes more vulnerable to noise in RTT + * measurements (e.g., delayed ACKs or other ACK compression effects). This + * noise may cause BBR to under-estimate the rate. + * + * To achieve full performance in high-speed paths, we budget enough cwnd to + * fit full-sized skbs in-flight on both end hosts to fully utilize the path: + * - one skb in sending host Qdisc, + * - one skb in sending host TSO/GSO engine + * - one skb being received by receiver host LRO/GRO/delayed-ACK engine + * Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because + * in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets, + * which allows 2 outstanding 2-packet sequences, to try to keep pipe + * full even with ACK-every-other-packet delayed ACKs. + */ +static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd; + u64 w; + + /* If we've never had a valid RTT sample, cap cwnd at the initial + * default. This should only happen when the connection is not using TCP + * timestamps and has retransmitted all of the SYN/SYNACK/data packets + * ACKed so far. In this case, an RTO can cut cwnd to 1, in which + * case we need to slow-start up toward something safe: TCP_INIT_CWND. + */ + if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */ + return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/ + + w = (u64)bw * bbr->min_rtt_us; + + /* Apply a gain to the given value, then remove the BW_SCALE shift. */ + cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT; + + /* Allow enough full-sized skbs in flight to utilize end systems. */ + cwnd += 3 * bbr->tso_segs_goal; + + /* Reduce delayed ACKs by rounding up cwnd to the next even number. */ + cwnd = (cwnd + 1) & ~1U; + + return cwnd; +} + +/* An optimization in BBR to reduce losses: On the first round of recovery, we + * follow the packet conservation principle: send P packets per P packets acked. + * After that, we slow-start and send at most 2*P packets per P packets acked. + * After recovery finishes, or upon undo, we restore the cwnd we had when + * recovery started (capped by the target cwnd based on estimated BDP). + * + * TODO(ycheng/ncardwell): implement a rate-based approach. + */ +static bool bbr_set_cwnd_to_recover_or_restore( + struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state; + u32 cwnd = tp->snd_cwnd; + + /* An ACK for P pkts should release at most 2*P packets. We do this + * in two steps. First, here we deduct the number of lost packets. + * Then, in bbr_set_cwnd() we slow start up toward the target cwnd. + */ + if (rs->losses > 0) + cwnd = max_t(s32, cwnd - rs->losses, 1); + + if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) { + /* Starting 1st round of Recovery, so do packet conservation. */ + bbr->packet_conservation = 1; + bbr->next_rtt_delivered = tp->delivered; /* start round now */ + /* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */ + cwnd = tcp_packets_in_flight(tp) + acked; + } else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) { + /* Exiting loss recovery; restore cwnd saved before recovery. */ + bbr->restore_cwnd = 1; + bbr->packet_conservation = 0; + } + bbr->prev_ca_state = state; + + if (bbr->restore_cwnd) { + /* Restore cwnd after exiting loss recovery or PROBE_RTT. */ + cwnd = max(cwnd, bbr->prior_cwnd); + bbr->restore_cwnd = 0; + } + + if (bbr->packet_conservation) { + *new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked); + return true; /* yes, using packet conservation */ + } + *new_cwnd = cwnd; + return false; +} + +/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss + * has drawn us down below target), or snap down to target if we're above it. + */ +static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs, + u32 acked, u32 bw, int gain) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd = 0, target_cwnd = 0; + + if (!acked) + return; + + if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd)) + goto done; + + /* If we're below target cwnd, slow start cwnd toward target cwnd. */ + target_cwnd = bbr_target_cwnd(sk, bw, gain); + if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */ + cwnd = min(cwnd + acked, target_cwnd); + else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND) + cwnd = cwnd + acked; + cwnd = max(cwnd, bbr_cwnd_min_target); + +done: + tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */ + if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */ + tp->snd_cwnd = max(tp->snd_cwnd >> 1, bbr_cwnd_min_target); +} + +/* End cycle phase if it's time and/or we hit the phase's in-flight target. */ +static bool bbr_is_next_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool is_full_length = + skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) > + bbr->min_rtt_us; + u32 inflight, bw; + + /* The pacing_gain of 1.0 paces at the estimated bw to try to fully + * use the pipe without increasing the queue. + */ + if (bbr->pacing_gain == BBR_UNIT) + return is_full_length; /* just use wall clock time */ + + inflight = rs->prior_in_flight; /* what was in-flight before ACK? */ + bw = bbr_max_bw(sk); + + /* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at + * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is + * small (e.g. on a LAN). We do not persist if packets are lost, since + * a path with small buffers may not hold that much. + */ + if (bbr->pacing_gain > BBR_UNIT) + return is_full_length && + (rs->losses || /* perhaps pacing_gain*BDP won't fit */ + inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain)); + + /* A pacing_gain < 1.0 tries to drain extra queue we added if bw + * probing didn't find more bw. If inflight falls to match BDP then we + * estimate queue is drained; persisting would underutilize the pipe. + */ + return is_full_length || + inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT); +} + +static void bbr_advance_cycle_phase(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1); + bbr->cycle_mstamp = tp->delivered_mstamp; + bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx]; +} + +/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */ +static void bbr_update_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw && + bbr_is_next_cycle_phase(sk, rs)) + bbr_advance_cycle_phase(sk); +} + +static void bbr_reset_startup_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_STARTUP; + bbr->pacing_gain = bbr_high_gain; + bbr->cwnd_gain = bbr_high_gain; +} + +static void bbr_reset_probe_bw_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_PROBE_BW; + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = bbr_cwnd_gain; + bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand); + bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */ +} + +static void bbr_reset_mode(struct sock *sk) +{ + if (!bbr_full_bw_reached(sk)) + bbr_reset_startup_mode(sk); + else + bbr_reset_probe_bw_mode(sk); +} + +/* Start a new long-term sampling interval. */ +static void bbr_reset_lt_bw_sampling_interval(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_last_stamp = tp->delivered_mstamp.stamp_jiffies; + bbr->lt_last_delivered = tp->delivered; + bbr->lt_last_lost = tp->lost; + bbr->lt_rtt_cnt = 0; +} + +/* Completely reset long-term bandwidth sampling. */ +static void bbr_reset_lt_bw_sampling(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_bw = 0; + bbr->lt_use_bw = 0; + bbr->lt_is_sampling = false; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Long-term bw sampling interval is done. Estimate whether we're policed. */ +static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 diff; + + if (bbr->lt_bw) { /* do we have bw from a previous interval? */ + /* Is new bw close to the lt_bw from the previous interval? */ + diff = abs(bw - bbr->lt_bw); + if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) || + (bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <= + bbr_lt_bw_diff)) { + /* All criteria are met; estimate we're policed. */ + bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */ + bbr->lt_use_bw = 1; + bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */ + bbr->lt_rtt_cnt = 0; + return; + } + } + bbr->lt_bw = bw; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of + * Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and + * explicitly models their policed rate, to reduce unnecessary losses. We + * estimate that we're policed if we see 2 consecutive sampling intervals with + * consistent throughput and high packet loss. If we think we're being policed, + * set lt_bw to the "long-term" average delivery rate from those 2 intervals. + */ +static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 lost, delivered; + u64 bw; + s32 t; + + if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */ + if (bbr->mode == BBR_PROBE_BW && bbr->round_start && + ++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) { + bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */ + bbr_reset_probe_bw_mode(sk); /* restart gain cycling */ + } + return; + } + + /* Wait for the first loss before sampling, to let the policer exhaust + * its tokens and estimate the steady-state rate allowed by the policer. + * Starting samples earlier includes bursts that over-estimate the bw. + */ + if (!bbr->lt_is_sampling) { + if (!rs->losses) + return; + bbr_reset_lt_bw_sampling_interval(sk); + bbr->lt_is_sampling = true; + } + + /* To avoid underestimates, reset sampling if we run out of data. */ + if (rs->is_app_limited) { + bbr_reset_lt_bw_sampling(sk); + return; + } + + if (bbr->round_start) + bbr->lt_rtt_cnt++; /* count round trips in this interval */ + if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts) + return; /* sampling interval needs to be longer */ + if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) { + bbr_reset_lt_bw_sampling(sk); /* interval is too long */ + return; + } + + /* End sampling interval when a packet is lost, so we estimate the + * policer tokens were exhausted. Stopping the sampling before the + * tokens are exhausted under-estimates the policed rate. + */ + if (!rs->losses) + return; + + /* Calculate packets lost and delivered in sampling interval. */ + lost = tp->lost - bbr->lt_last_lost; + delivered = tp->delivered - bbr->lt_last_delivered; + /* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */ + if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered) + return; + + /* Find average delivery rate in this sampling interval. */ + t = (s32)(tp->delivered_mstamp.stamp_jiffies - bbr->lt_last_stamp); + if (t < 1) + return; /* interval is less than one jiffy, so wait */ + t = jiffies_to_usecs(t); + /* Interval long enough for jiffies_to_usecs() to return a bogus 0? */ + if (t < 1) { + bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */ + return; + } + bw = (u64)delivered * BW_UNIT; + do_div(bw, t); + bbr_lt_bw_interval_done(sk, bw); +} + +/* Estimate the bandwidth based on how fast packets are delivered */ +static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->round_start = 0; + if (rs->delivered < 0 || rs->interval_us <= 0) + return; /* Not a valid observation */ + + /* See if we've reached the next RTT */ + if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) { + bbr->next_rtt_delivered = tp->delivered; + bbr->rtt_cnt++; + bbr->round_start = 1; + bbr->packet_conservation = 0; + } + + bbr_lt_bw_sampling(sk, rs); + + /* Divide delivered by the interval to find a (lower bound) bottleneck + * bandwidth sample. Delivered is in packets and interval_us in uS and + * ratio will be <<1 for most connections. So delivered is first scaled. + */ + bw = (u64)rs->delivered * BW_UNIT; + do_div(bw, rs->interval_us); + + /* If this sample is application-limited, it is likely to have a very + * low delivered count that represents application behavior rather than + * the available network rate. Such a sample could drag down estimated + * bw, causing needless slow-down. Thus, to continue to send at the + * last measured network rate, we filter out app-limited samples unless + * they describe the path bw at least as well as our bw model. + * + * So the goal during app-limited phase is to proceed with the best + * network rate no matter how long. We automatically leave this + * phase when app writes faster than the network can deliver :) + */ + if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) { + /* Incorporate new sample into our max bw filter. */ + minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw); + } +} + +/* Estimate when the pipe is full, using the change in delivery rate: BBR + * estimates that STARTUP filled the pipe if the estimated bw hasn't changed by + * at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited + * rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the + * higher rwin, 3: we get higher delivery rate samples. Or transient + * cross-traffic or radio noise can go away. CUBIC Hystart shares a similar + * design goal, but uses delay and inter-ACK spacing instead of bandwidth. + */ +static void bbr_check_full_bw_reached(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw_thresh; + + if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited) + return; + + bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE; + if (bbr_max_bw(sk) >= bw_thresh) { + bbr->full_bw = bbr_max_bw(sk); + bbr->full_bw_cnt = 0; + return; + } + ++bbr->full_bw_cnt; +} + +/* If pipe is probably full, drain the queue and then enter steady-state. */ +static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) { + bbr->mode = BBR_DRAIN; /* drain queue we created */ + bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */ + bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */ + } /* fall through to check if in-flight is already small: */ + if (bbr->mode == BBR_DRAIN && + tcp_packets_in_flight(tcp_sk(sk)) <= + bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT)) + bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */ +} + +/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and + * periodically drain the bottleneck queue, to converge to measure the true + * min_rtt (unloaded propagation delay). This allows the flows to keep queues + * small (reducing queuing delay and packet loss) and achieve fairness among + * BBR flows. + * + * The min_rtt filter window is 10 seconds. When the min_rtt estimate expires, + * we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets. + * After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed + * round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and + * re-enter the previous mode. BBR uses 200ms to approximately bound the + * performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s). + * + * Note that flows need only pay 2% if they are busy sending over the last 10 + * seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have + * natural silences or low-rate periods within 10 seconds where the rate is low + * enough for long enough to drain its queue in the bottleneck. We pick up + * these min RTT measurements opportunistically with our min_rtt filter. :-) + */ +static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); +//deprecated u32 rtt_prior = 0; + bool filter_expired; + + /* Track min RTT seen in the min_rtt_win_sec filter window: */ + filter_expired = after(tcp_time_stamp, + bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ); + if (rs->rtt_us >= 0 && + (rs->rtt_us <= bbr->min_rtt_us || filter_expired)) { + bbr->min_rtt_us = rs->rtt_us; + bbr->min_rtt_stamp = tcp_time_stamp; +//deprecated bbr->rtt_us = rs->rtt_us; + } +//deprecated bbr->rtt_us = rs->rtt_us; +//deprecated rtt_prior = minmax_get(&bbr->max_rtt); +//deprecated bbr->rtt_us = min(bbr->rtt_us, rtt_prior); + +//deprecated minmax_running_max(&bbr->max_rtt, bbr_bw_rtts, bbr->rtt_cnt, rs->rtt_us); + + if (bbr_probe_rtt_mode_ms > 0 && filter_expired && + !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) { + bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */ + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = BBR_UNIT; + bbr_save_cwnd(sk); /* note cwnd so we can restore it */ + bbr->probe_rtt_done_stamp = 0; + } + + if (bbr->mode == BBR_PROBE_RTT) { + /* Ignore low rate samples during this mode. */ + tp->app_limited = + (tp->delivered + tcp_packets_in_flight(tp)) ? : 1; + /* Maintain min packets in flight for max(200 ms, 1 round). */ + if (!bbr->probe_rtt_done_stamp && + tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) { + bbr->probe_rtt_done_stamp = tcp_time_stamp + + msecs_to_jiffies(bbr_probe_rtt_mode_ms >> 1); + bbr->probe_rtt_round_done = 0; + bbr->next_rtt_delivered = tp->delivered; + } else if (bbr->probe_rtt_done_stamp) { + if (bbr->round_start) + bbr->probe_rtt_round_done = 1; + if (bbr->probe_rtt_round_done && + after(tcp_time_stamp, bbr->probe_rtt_done_stamp)) { + bbr->min_rtt_stamp = tcp_time_stamp; + bbr->restore_cwnd = 1; /* snap to prior_cwnd */ + bbr_reset_mode(sk); + } + } + } + bbr->idle_restart = 0; +} + +static void bbr_update_model(struct sock *sk, const struct rate_sample *rs) +{ + bbr_update_bw(sk, rs); + bbr_update_cycle_phase(sk, rs); + bbr_check_full_bw_reached(sk, rs); + bbr_check_drain(sk, rs); + bbr_update_min_rtt(sk, rs); +} + +static void bbr_main(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw; + + bbr_update_model(sk, rs); + + bw = bbr_bw(sk); + bbr_set_pacing_rate(sk, bw, bbr->pacing_gain); + bbr_set_tso_segs_goal(sk); + bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain); +} + +static void bbr_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->prior_cwnd = 0; + bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */ + bbr->rtt_cnt = 0; + bbr->next_rtt_delivered = 0; + bbr->prev_ca_state = TCP_CA_Open; + bbr->packet_conservation = 0; + + bbr->probe_rtt_done_stamp = 0; + bbr->probe_rtt_round_done = 0; + bbr->min_rtt_us = tcp_min_rtt(tp); + bbr->min_rtt_stamp = tcp_time_stamp; + + minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */ + + /* Initialize pacing rate to: high_gain * init_cwnd / RTT. */ + bw = (u64)tp->snd_cwnd * BW_UNIT; + do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC); + sk->sk_pacing_rate = 0; /* force an update of sk_pacing_rate */ + bbr_set_pacing_rate(sk, bw, bbr_high_gain); + + bbr->restore_cwnd = 0; + bbr->round_start = 0; + bbr->idle_restart = 0; + bbr->full_bw = 0; + bbr->full_bw_cnt = 0; + bbr->cycle_mstamp.v64 = 0; + bbr->cycle_idx = 0; + bbr_reset_lt_bw_sampling(sk); + bbr_reset_startup_mode(sk); +} + +static u32 bbr_sndbuf_expand(struct sock *sk) +{ + /* Provision 3 * cwnd since BBR may slow-start even during recovery. */ + return 3; +} + +/* In theory BBR does not need to undo the cwnd since it does not + * always reduce cwnd on losses (see bbr_main()). Keep it for now. + */ +static u32 bbr_undo_cwnd(struct sock *sk) +{ + return tcp_sk(sk)->snd_cwnd; +} + +/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */ +static u32 bbr_ssthresh(struct sock *sk) +{ + bbr_save_cwnd(sk); + return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */ +} + +static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr, + union tcp_cc_info *info) +{ + if (ext & (1 << (INET_DIAG_BBRINFO - 1)) || + ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw = bbr_bw(sk); + + bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE; + memset(&info->bbr, 0, sizeof(info->bbr)); + info->bbr.bbr_bw_lo = (u32)bw; + info->bbr.bbr_bw_hi = (u32)(bw >> 32); + info->bbr.bbr_min_rtt = bbr->min_rtt_us; + info->bbr.bbr_pacing_gain = bbr->pacing_gain; + info->bbr.bbr_cwnd_gain = bbr->cwnd_gain; + *attr = INET_DIAG_BBRINFO; + return sizeof(info->bbr); + } + return 0; +} + +static void bbr_set_state(struct sock *sk, u8 new_state) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (new_state == TCP_CA_Loss) { + struct rate_sample rs = { .losses = 1 }; + + bbr->prev_ca_state = TCP_CA_Loss; + bbr->full_bw = 0; + bbr->round_start = 1; /* treat RTO like end of a round */ + bbr_lt_bw_sampling(sk, &rs); + } +} + +static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = { + .flags = TCP_CONG_NON_RESTRICTED, + .name = "tsunami", + .owner = THIS_MODULE, + .init = bbr_init, + .cong_control = bbr_main, + .sndbuf_expand = bbr_sndbuf_expand, + .undo_cwnd = bbr_undo_cwnd, + .cwnd_event = bbr_cwnd_event, + .ssthresh = bbr_ssthresh, + .tso_segs_goal = bbr_tso_segs_goal, + .get_info = bbr_get_info, + .set_state = bbr_set_state, +}; + +static int __init bbr_register(void) +{ + BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_bbr_cong_ops); +} + +static void __exit bbr_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_bbr_cong_ops); +} + +module_init(bbr_register); +module_exit(bbr_unregister); + +MODULE_AUTHOR("Van Jacobson "); +MODULE_AUTHOR("Neal Cardwell "); +MODULE_AUTHOR("Yuchung Cheng "); +MODULE_AUTHOR("Soheil Hassas Yeganeh "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)"); \ No newline at end of file diff --git a/tcp_tsunami_lon.c b/tcp_tsunami_lon.c new file mode 100644 index 0000000..916b405 --- /dev/null +++ b/tcp_tsunami_lon.c @@ -0,0 +1,905 @@ +/* Bottleneck Bandwidth and RTT (BBR) congestion control + * + * BBR congestion control computes the sending rate based on the delivery + * rate (throughput) estimated from ACKs. In a nutshell: + * + * On each ACK, update our model of the network path: + * bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips) + * min_rtt = windowed_min(rtt, 10 seconds) + * pacing_rate = pacing_gain * bottleneck_bandwidth + * cwnd = max(cwnd_gain * bottleneck_bandwidth * min_rtt, 4) + * + * The core algorithm does not react directly to packet losses or delays, + * although BBR may adjust the size of next send per ACK when loss is + * observed, or adjust the sending rate if it estimates there is a + * traffic policer, in order to keep the drop rate reasonable. + * + * BBR is described in detail in: + * "BBR: Congestion-Based Congestion Control", + * Neal Cardwell, Yuchung Cheng, C. Stephen Gunn, Soheil Hassas Yeganeh, + * Van Jacobson. ACM Queue, Vol. 14 No. 5, September-October 2016. + * + * There is a public e-mail list for discussing BBR development and testing: + * https://groups.google.com/forum/#!forum/bbr-dev + * + * NOTE: BBR *must* be used with the fq qdisc ("man tc-fq") with pacing enabled, + * since pacing is integral to the BBR design and implementation. + * BBR without pacing would not function properly, and may incur unnecessary + * high packet loss rates. + */ +#include +#include +#include +#include +#include +#include + +/* Scale factor for rate in pkt/uSec unit to avoid truncation in bandwidth + * estimation. The rate unit ~= (1500 bytes / 1 usec / 2^24) ~= 715 bps. + * This handles bandwidths from 0.06pps (715bps) to 256Mpps (3Tbps) in a u32. + * Since the minimum window is >=4 packets, the lower bound isn't + * an issue. The upper bound isn't an issue with existing technologies. + */ +#define BW_SCALE 24 +#define BW_UNIT (1 << BW_SCALE) + +#define BBR_SCALE 8 /* scaling factor for fractions in BBR (e.g. gains) */ +#define BBR_UNIT (1 << BBR_SCALE) + +/* BBR has the following modes for deciding how fast to send: */ +enum bbr_mode { + BBR_STARTUP, /* ramp up sending rate rapidly to fill pipe */ + BBR_DRAIN, /* drain any queue created during startup */ + BBR_PROBE_BW, /* discover, share bw: pace around estimated bw */ + BBR_PROBE_RTT, /* cut cwnd to min to probe min_rtt */ +}; + +/* BBR congestion control block */ +struct bbr { + u32 min_rtt_us; /* min RTT in min_rtt_win_sec window */ +//deprecated u32 rtt_us; + u32 min_rtt_stamp; /* timestamp of min_rtt_us */ + u32 probe_rtt_done_stamp; /* end time for BBR_PROBE_RTT mode */ +//deprecated struct minmax max_rtt; + struct minmax bw; /* Max recent delivery rate in pkts/uS << 24 */ + u32 rtt_cnt; /* count of packet-timed rounds elapsed */ + u32 next_rtt_delivered; /* scb->tx.delivered at end of round */ + struct skb_mstamp cycle_mstamp; /* time of this cycle phase start */ + u32 mode:3, /* current bbr_mode in state machine */ + prev_ca_state:3, /* CA state on previous ACK */ + packet_conservation:1, /* use packet conservation? */ + restore_cwnd:1, /* decided to revert cwnd to old value */ + round_start:1, /* start of packet-timed tx->ack round? */ + tso_segs_goal:1, /* segments we want in each skb we send */ + idle_restart:1, /* restarting after idle? */ + probe_rtt_round_done:1, /* a BBR_PROBE_RTT round at 4 pkts? */ + unused:5, + lt_is_sampling:1, /* taking long-term ("LT") samples now? */ + lt_rtt_cnt:7, /* round trips in long-term interval */ + lt_use_bw:1; /* use lt_bw as our bw estimate? */ + u32 lt_bw; /* LT est delivery rate in pkts/uS << 24 */ + u32 lt_last_delivered; /* LT intvl start: tp->delivered */ + u32 lt_last_stamp; /* LT intvl start: tp->delivered_mstamp */ + u32 lt_last_lost; /* LT intvl start: tp->lost */ + u32 pacing_gain:10, /* current gain for setting pacing rate */ + cwnd_gain:10, /* current gain for setting cwnd */ + full_bw_cnt:3, /* number of rounds without large bw gains */ + cycle_idx:3, /* current index in pacing_gain cycle array */ + unused_b:6; + u32 prior_cwnd; /* prior cwnd upon entering loss recovery */ + u32 full_bw; /* recent bw, to estimate if pipe is full */ +}; + +#define CYCLE_LEN 8 /* number of phases in a pacing gain cycle */ + +/* Window length of bw filter (in rounds): */ +static const int bbr_bw_rtts = CYCLE_LEN + 10; +/* Window length of min_rtt filter (in sec): */ +static const u32 bbr_min_rtt_win_sec = 30; +/* Minimum time (in ms) spent at bbr_cwnd_min_target in BBR_PROBE_RTT mode: */ +static const u32 bbr_probe_rtt_mode_ms = 180; +/* Skip TSO below the following bandwidth (bits/sec): */ +static const int bbr_min_tso_rate = 120000000; + +/* We use a high_gain value of 2/ln(2) because it's the smallest pacing gain + * that will allow a smoothly increasing pacing rate that will double each RTT + * and send the same number of packets per RTT that an un-paced, slow-starting + * Reno or CUBIC flow would: + */ +static const int bbr_high_gain = BBR_UNIT * 3921 / 1000 + 1; +/* The pacing gain of 1/high_gain in BBR_DRAIN is calculated to typically drain + * the queue created in BBR_STARTUP in a single round: + */ +static const int bbr_drain_gain = BBR_UNIT * 1200 / 2885; +/* The gain for deriving steady-state cwnd tolerates delayed/stretched ACKs: */ +static const int bbr_cwnd_gain = BBR_UNIT * 2; +/* The pacing_gain values for the PROBE_BW gain cycle, to discover/share bw: */ +static const int bbr_pacing_gain[] = { + BBR_UNIT * 3 / 2, /* probe for more available bw */ + BBR_UNIT * 3 / 4, /* drain queue and/or yield bw to other flows */ + BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, /* cruise at 1.0*bw to utilize pipe, */ + BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8, BBR_UNIT * 9 / 8 /* without creating excess queue... */ +}; +/* Randomize the starting gain cycling phase over N phases: */ +static const u32 bbr_cycle_rand = 7; + +/* Try to keep at least this many packets in flight, if things go smoothly. For + * smooth functioning, a sliding window protocol ACKing every other packet + * needs at least 4 packets in flight: + */ +static const u32 bbr_cwnd_min_target = 6; + +/* To estimate if BBR_STARTUP mode (i.e. high_gain) has filled pipe... */ +/* If bw has increased significantly (1.25x), there may be more bw available: */ +static const u32 bbr_full_bw_thresh = BBR_UNIT * 5 / 4; +/* But after 3 rounds w/o significant bw growth, estimate pipe is full: */ +static const u32 bbr_full_bw_cnt = 4; + +/* "long-term" ("LT") bandwidth estimator parameters... */ +/* The minimum number of rounds in an LT bw sampling interval: */ +static const u32 bbr_lt_intvl_min_rtts = 4; +/* If lost/delivered ratio > 20%, interval is "lossy" and we may be policed: */ +static const u32 bbr_lt_loss_thresh = 50; +/* If 2 intervals have a bw ratio <= 1/8, their bw is "consistent": */ +static const u32 bbr_lt_bw_ratio = BBR_UNIT / 8; +/* If 2 intervals have a bw diff <= 4 Kbit/sec their bw is "consistent": */ +static const u32 bbr_lt_bw_diff = 4000 / 8; +/* If we estimate we're policed, use lt_bw for this many round trips: */ +static const u32 bbr_lt_bw_max_rtts = 48; + +/* Do we estimate that STARTUP filled the pipe? */ +static bool bbr_full_bw_reached(const struct sock *sk) +{ + const struct bbr *bbr = inet_csk_ca(sk); + + return bbr->full_bw_cnt >= bbr_full_bw_cnt; +} + +/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */ +static u32 bbr_max_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return minmax_get(&bbr->bw); +} + +/* Return the estimated bandwidth of the path, in pkts/uS << BW_SCALE. */ +static u32 bbr_bw(const struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->lt_use_bw ? bbr->lt_bw : bbr_max_bw(sk); +} + +/* Return rate in bytes per second, optionally with a gain. + * The order here is chosen carefully to avoid overflow of u64. This should + * work for input rates of up to 2.9Tbit/sec and gain of 2.89x. + */ +static u64 bbr_rate_bytes_per_sec(struct sock *sk, u64 rate, int gain) +{ + rate *= tcp_mss_to_mtu(sk, tcp_sk(sk)->mss_cache); + rate *= gain; + rate >>= BBR_SCALE; + rate *= USEC_PER_SEC; + return rate >> BW_SCALE; +} + +/* Pace using current bw estimate and a gain factor. In order to help drive the + * network toward lower queues while maintaining high utilization and low + * latency, the average pacing rate aims to be slightly (~1%) lower than the + * estimated bandwidth. This is an important aspect of the design. In this + * implementation this slightly lower pacing rate is achieved implicitly by not + * including link-layer headers in the packet size used for the pacing rate. + */ +static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u64 rate = bw; + + rate = bbr_rate_bytes_per_sec(sk, rate, gain); + rate = min_t(u64, rate, sk->sk_max_pacing_rate); + if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate) + sk->sk_pacing_rate = rate; +} + +/* Return count of segments we want in the skbs we send, or 0 for default. */ +static u32 bbr_tso_segs_goal(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + return bbr->tso_segs_goal; +} + +static void bbr_set_tso_segs_goal(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 min_segs; + + min_segs = sk->sk_pacing_rate < (bbr_min_tso_rate >> 3) ? 1 : 2; + bbr->tso_segs_goal = min(tcp_tso_autosize(sk, tp->mss_cache, min_segs), + 0x7FU); +} + +/* Save "last known good" cwnd so we can restore it after losses or PROBE_RTT */ +static void bbr_save_cwnd(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->prev_ca_state < TCP_CA_Recovery && bbr->mode != BBR_PROBE_RTT) + bbr->prior_cwnd = tp->snd_cwnd; /* this cwnd is good enough */ + else /* loss recovery or BBR_PROBE_RTT have temporarily cut cwnd */ + bbr->prior_cwnd = max(bbr->prior_cwnd, tp->snd_cwnd); +} + +static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + if (event == CA_EVENT_TX_START && tp->app_limited) { + bbr->idle_restart = 1; + /* Avoid pointless buffer overflows: pace at est. bw if we don't + * need more speed (we're restarting from idle and app-limited). + */ + if (bbr->mode == BBR_PROBE_BW) + bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT); + } +} + +/* Find target cwnd. Right-size the cwnd based on min RTT and the + * estimated bottleneck bandwidth: + * + * cwnd = bw * min_rtt * gain = BDP * gain + * + * The key factor, gain, controls the amount of queue. While a small gain + * builds a smaller queue, it becomes more vulnerable to noise in RTT + * measurements (e.g., delayed ACKs or other ACK compression effects). This + * noise may cause BBR to under-estimate the rate. + * + * To achieve full performance in high-speed paths, we budget enough cwnd to + * fit full-sized skbs in-flight on both end hosts to fully utilize the path: + * - one skb in sending host Qdisc, + * - one skb in sending host TSO/GSO engine + * - one skb being received by receiver host LRO/GRO/delayed-ACK engine + * Don't worry, at low rates (bbr_min_tso_rate) this won't bloat cwnd because + * in such cases tso_segs_goal is 1. The minimum cwnd is 4 packets, + * which allows 2 outstanding 2-packet sequences, to try to keep pipe + * full even with ACK-every-other-packet delayed ACKs. + */ +static u32 bbr_target_cwnd(struct sock *sk, u32 bw, int gain) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd; + u64 w; + + /* If we've never had a valid RTT sample, cap cwnd at the initial + * default. This should only happen when the connection is not using TCP + * timestamps and has retransmitted all of the SYN/SYNACK/data packets + * ACKed so far. In this case, an RTO can cut cwnd to 1, in which + * case we need to slow-start up toward something safe: TCP_INIT_CWND. + */ + if (unlikely(bbr->min_rtt_us == ~0U)) /* no valid RTT samples yet? */ + return TCP_INIT_CWND; /* be safe: cap at default initial cwnd*/ + + w = (u64)bw * bbr->min_rtt_us; + + /* Apply a gain to the given value, then remove the BW_SCALE shift. */ + cwnd = (((w * gain) >> BBR_SCALE) + BW_UNIT - 1) / BW_UNIT; + + /* Allow enough full-sized skbs in flight to utilize end systems. */ + cwnd += 3 * bbr->tso_segs_goal; + + /* Reduce delayed ACKs by rounding up cwnd to the next even number. */ + cwnd = (cwnd + 1) & ~1U; + + return cwnd; +} + +/* An optimization in BBR to reduce losses: On the first round of recovery, we + * follow the packet conservation principle: send P packets per P packets acked. + * After that, we slow-start and send at most 2*P packets per P packets acked. + * After recovery finishes, or upon undo, we restore the cwnd we had when + * recovery started (capped by the target cwnd based on estimated BDP). + * + * TODO(ycheng/ncardwell): implement a rate-based approach. + */ +static bool bbr_set_cwnd_to_recover_or_restore( + struct sock *sk, const struct rate_sample *rs, u32 acked, u32 *new_cwnd) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u8 prev_state = bbr->prev_ca_state, state = inet_csk(sk)->icsk_ca_state; + u32 cwnd = tp->snd_cwnd; + + /* An ACK for P pkts should release at most 2*P packets. We do this + * in two steps. First, here we deduct the number of lost packets. + * Then, in bbr_set_cwnd() we slow start up toward the target cwnd. + */ + if (rs->losses > 0) + cwnd = max_t(s32, cwnd - rs->losses, 1); + + if (state == TCP_CA_Recovery && prev_state != TCP_CA_Recovery) { + /* Starting 1st round of Recovery, so do packet conservation. */ + bbr->packet_conservation = 1; + bbr->next_rtt_delivered = tp->delivered; /* start round now */ + /* Cut unused cwnd from app behavior, TSQ, or TSO deferral: */ + cwnd = tcp_packets_in_flight(tp) + acked; + } else if (prev_state >= TCP_CA_Recovery && state < TCP_CA_Recovery) { + /* Exiting loss recovery; restore cwnd saved before recovery. */ + bbr->restore_cwnd = 1; + bbr->packet_conservation = 0; + } + bbr->prev_ca_state = state; + + if (bbr->restore_cwnd) { + /* Restore cwnd after exiting loss recovery or PROBE_RTT. */ + cwnd = max(cwnd, bbr->prior_cwnd); + bbr->restore_cwnd = 0; + } + + if (bbr->packet_conservation) { + *new_cwnd = max(cwnd, tcp_packets_in_flight(tp) + acked); + return true; /* yes, using packet conservation */ + } + *new_cwnd = cwnd; + return false; +} + +/* Slow-start up toward target cwnd (if bw estimate is growing, or packet loss + * has drawn us down below target), or snap down to target if we're above it. + */ +static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs, + u32 acked, u32 bw, int gain) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 cwnd = 0, target_cwnd = 0; + + if (!acked) + return; + + if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd)) + goto done; + + /* If we're below target cwnd, slow start cwnd toward target cwnd. */ + target_cwnd = bbr_target_cwnd(sk, bw, gain); + if (bbr_full_bw_reached(sk)) /* only cut cwnd if we filled the pipe */ + cwnd = max(cwnd + acked, target_cwnd); + else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND) + cwnd = cwnd + acked; + cwnd = max(cwnd, bbr_cwnd_min_target); + +done: + tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); /* apply global cap */ + if (bbr->mode == BBR_PROBE_RTT) /* drain queue, refresh min_rtt */ + tp->snd_cwnd = max(tp->snd_cwnd >> 1, bbr_cwnd_min_target); +} + +/* End cycle phase if it's time and/or we hit the phase's in-flight target. */ +static bool bbr_is_next_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + bool is_full_length = + skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) > + bbr->min_rtt_us; + u32 inflight, bw; + + /* The pacing_gain of 1.0 paces at the estimated bw to try to fully + * use the pipe without increasing the queue. + */ + if (bbr->pacing_gain == BBR_UNIT) + return is_full_length; /* just use wall clock time */ + + inflight = rs->prior_in_flight; /* what was in-flight before ACK? */ + bw = bbr_max_bw(sk); + + /* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at + * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is + * small (e.g. on a LAN). We do not persist if packets are lost, since + * a path with small buffers may not hold that much. + */ + if (bbr->pacing_gain > BBR_UNIT) + return is_full_length && + (rs->losses || /* perhaps pacing_gain*BDP won't fit */ + inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain)); + + /* A pacing_gain < 1.0 tries to drain extra queue we added if bw + * probing didn't find more bw. If inflight falls to match BDP then we + * estimate queue is drained; persisting would underutilize the pipe. + */ + return is_full_length || + inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT); +} + +static void bbr_advance_cycle_phase(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->cycle_idx = (bbr->cycle_idx + 1) & (CYCLE_LEN - 1); + bbr->cycle_mstamp = tp->delivered_mstamp; + bbr->pacing_gain = bbr_pacing_gain[bbr->cycle_idx]; +} + +/* Gain cycling: cycle pacing gain to converge to fair share of available bw. */ +static void bbr_update_cycle_phase(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if ((bbr->mode == BBR_PROBE_BW) && !bbr->lt_use_bw && + bbr_is_next_cycle_phase(sk, rs)) + bbr_advance_cycle_phase(sk); +} + +static void bbr_reset_startup_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_STARTUP; + bbr->pacing_gain = bbr_high_gain; + bbr->cwnd_gain = bbr_high_gain; +} + +static void bbr_reset_probe_bw_mode(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->mode = BBR_PROBE_BW; + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = bbr_cwnd_gain; + bbr->cycle_idx = CYCLE_LEN - 1 - prandom_u32_max(bbr_cycle_rand); + bbr_advance_cycle_phase(sk); /* flip to next phase of gain cycle */ +} + +static void bbr_reset_mode(struct sock *sk) +{ + if (!bbr_full_bw_reached(sk)) + bbr_reset_startup_mode(sk); + else + bbr_reset_probe_bw_mode(sk); +} + +/* Start a new long-term sampling interval. */ +static void bbr_reset_lt_bw_sampling_interval(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_last_stamp = tp->delivered_mstamp.stamp_jiffies; + bbr->lt_last_delivered = tp->delivered; + bbr->lt_last_lost = tp->lost; + bbr->lt_rtt_cnt = 0; +} + +/* Completely reset long-term bandwidth sampling. */ +static void bbr_reset_lt_bw_sampling(struct sock *sk) +{ + struct bbr *bbr = inet_csk_ca(sk); + + bbr->lt_bw = 0; + bbr->lt_use_bw = 0; + bbr->lt_is_sampling = false; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Long-term bw sampling interval is done. Estimate whether we're policed. */ +static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 diff; + + if (bbr->lt_bw) { /* do we have bw from a previous interval? */ + /* Is new bw close to the lt_bw from the previous interval? */ + diff = abs(bw - bbr->lt_bw); + if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) || + (bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <= + bbr_lt_bw_diff)) { + /* All criteria are met; estimate we're policed. */ + bbr->lt_bw = (bw + bbr->lt_bw) >> 1; /* avg 2 intvls */ + bbr->lt_use_bw = 1; + bbr->pacing_gain = BBR_UNIT; /* try to avoid drops */ + bbr->lt_rtt_cnt = 0; + return; + } + } + bbr->lt_bw = bw; + bbr_reset_lt_bw_sampling_interval(sk); +} + +/* Token-bucket traffic policers are common (see "An Internet-Wide Analysis of + * Traffic Policing", SIGCOMM 2016). BBR detects token-bucket policers and + * explicitly models their policed rate, to reduce unnecessary losses. We + * estimate that we're policed if we see 2 consecutive sampling intervals with + * consistent throughput and high packet loss. If we think we're being policed, + * set lt_bw to the "long-term" average delivery rate from those 2 intervals. + */ +static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u32 lost, delivered; + u64 bw; + s32 t; + + if (bbr->lt_use_bw) { /* already using long-term rate, lt_bw? */ + if (bbr->mode == BBR_PROBE_BW && bbr->round_start && + ++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) { + bbr_reset_lt_bw_sampling(sk); /* stop using lt_bw */ + bbr_reset_probe_bw_mode(sk); /* restart gain cycling */ + } + return; + } + + /* Wait for the first loss before sampling, to let the policer exhaust + * its tokens and estimate the steady-state rate allowed by the policer. + * Starting samples earlier includes bursts that over-estimate the bw. + */ + if (!bbr->lt_is_sampling) { + if (!rs->losses) + return; + bbr_reset_lt_bw_sampling_interval(sk); + bbr->lt_is_sampling = true; + } + + /* To avoid underestimates, reset sampling if we run out of data. */ + if (rs->is_app_limited) { + bbr_reset_lt_bw_sampling(sk); + return; + } + + if (bbr->round_start) + bbr->lt_rtt_cnt++; /* count round trips in this interval */ + if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts) + return; /* sampling interval needs to be longer */ + if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) { + bbr_reset_lt_bw_sampling(sk); /* interval is too long */ + return; + } + + /* End sampling interval when a packet is lost, so we estimate the + * policer tokens were exhausted. Stopping the sampling before the + * tokens are exhausted under-estimates the policed rate. + */ + if (!rs->losses) + return; + + /* Calculate packets lost and delivered in sampling interval. */ + lost = tp->lost - bbr->lt_last_lost; + delivered = tp->delivered - bbr->lt_last_delivered; + /* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */ + if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered) + return; + + /* Find average delivery rate in this sampling interval. */ + t = (s32)(tp->delivered_mstamp.stamp_jiffies - bbr->lt_last_stamp); + if (t < 1) + return; /* interval is less than one jiffy, so wait */ + t = jiffies_to_usecs(t); + /* Interval long enough for jiffies_to_usecs() to return a bogus 0? */ + if (t < 1) { + bbr_reset_lt_bw_sampling(sk); /* interval too long; reset */ + return; + } + bw = (u64)delivered * BW_UNIT; + do_div(bw, t); + bbr_lt_bw_interval_done(sk, bw); +} + +/* Estimate the bandwidth based on how fast packets are delivered */ +static void bbr_update_bw(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->round_start = 0; + if (rs->delivered < 0 || rs->interval_us <= 0) + return; /* Not a valid observation */ + + /* See if we've reached the next RTT */ + if (!before(rs->prior_delivered, bbr->next_rtt_delivered)) { + bbr->next_rtt_delivered = tp->delivered; + bbr->rtt_cnt++; + bbr->round_start = 1; + bbr->packet_conservation = 0; + } + + bbr_lt_bw_sampling(sk, rs); + + /* Divide delivered by the interval to find a (lower bound) bottleneck + * bandwidth sample. Delivered is in packets and interval_us in uS and + * ratio will be <<1 for most connections. So delivered is first scaled. + */ + bw = (u64)rs->delivered * BW_UNIT; + do_div(bw, rs->interval_us); + + /* If this sample is application-limited, it is likely to have a very + * low delivered count that represents application behavior rather than + * the available network rate. Such a sample could drag down estimated + * bw, causing needless slow-down. Thus, to continue to send at the + * last measured network rate, we filter out app-limited samples unless + * they describe the path bw at least as well as our bw model. + * + * So the goal during app-limited phase is to proceed with the best + * network rate no matter how long. We automatically leave this + * phase when app writes faster than the network can deliver :) + */ + if (!rs->is_app_limited || bw >= bbr_max_bw(sk)) { + /* Incorporate new sample into our max bw filter. */ + minmax_running_max(&bbr->bw, bbr_bw_rtts, bbr->rtt_cnt, bw); + } +} + +/* Estimate when the pipe is full, using the change in delivery rate: BBR + * estimates that STARTUP filled the pipe if the estimated bw hasn't changed by + * at least bbr_full_bw_thresh (25%) after bbr_full_bw_cnt (3) non-app-limited + * rounds. Why 3 rounds: 1: rwin autotuning grows the rwin, 2: we fill the + * higher rwin, 3: we get higher delivery rate samples. Or transient + * cross-traffic or radio noise can go away. CUBIC Hystart shares a similar + * design goal, but uses delay and inter-ACK spacing instead of bandwidth. + */ +static void bbr_check_full_bw_reached(struct sock *sk, + const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw_thresh; + + if (bbr_full_bw_reached(sk) || !bbr->round_start || rs->is_app_limited) + return; + + bw_thresh = (u64)bbr->full_bw * bbr_full_bw_thresh >> BBR_SCALE; + if (bbr_max_bw(sk) >= bw_thresh) { + bbr->full_bw = bbr_max_bw(sk); + bbr->full_bw_cnt = 0; + return; + } + ++bbr->full_bw_cnt; +} + +/* If pipe is probably full, drain the queue and then enter steady-state. */ +static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) { + bbr->mode = BBR_DRAIN; /* drain queue we created */ + bbr->pacing_gain = bbr_drain_gain; /* pace slow to drain */ + bbr->cwnd_gain = bbr_high_gain; /* maintain cwnd */ + } /* fall through to check if in-flight is already small: */ + if (bbr->mode == BBR_DRAIN && + tcp_packets_in_flight(tcp_sk(sk)) <= + bbr_target_cwnd(sk, bbr_max_bw(sk), BBR_UNIT)) + bbr_reset_probe_bw_mode(sk); /* we estimate queue is drained */ +} + +/* The goal of PROBE_RTT mode is to have BBR flows cooperatively and + * periodically drain the bottleneck queue, to converge to measure the true + * min_rtt (unloaded propagation delay). This allows the flows to keep queues + * small (reducing queuing delay and packet loss) and achieve fairness among + * BBR flows. + * + * The min_rtt filter window is 10 seconds. When the min_rtt estimate expires, + * we enter PROBE_RTT mode and cap the cwnd at bbr_cwnd_min_target=4 packets. + * After at least bbr_probe_rtt_mode_ms=200ms and at least one packet-timed + * round trip elapsed with that flight size <= 4, we leave PROBE_RTT mode and + * re-enter the previous mode. BBR uses 200ms to approximately bound the + * performance penalty of PROBE_RTT's cwnd capping to roughly 2% (200ms/10s). + * + * Note that flows need only pay 2% if they are busy sending over the last 10 + * seconds. Interactive applications (e.g., Web, RPCs, video chunks) often have + * natural silences or low-rate periods within 10 seconds where the rate is low + * enough for long enough to drain its queue in the bottleneck. We pick up + * these min RTT measurements opportunistically with our min_rtt filter. :-) + */ +static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); +//deprecated u32 rtt_prior = 0; + bool filter_expired; + + /* Track min RTT seen in the min_rtt_win_sec filter window: */ + filter_expired = after(tcp_time_stamp, + bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ); + if (rs->rtt_us >= 0 && + (rs->rtt_us <= bbr->min_rtt_us || filter_expired)) { + bbr->min_rtt_us = rs->rtt_us; + bbr->min_rtt_stamp = tcp_time_stamp; +//deprecated bbr->rtt_us = rs->rtt_us; + } +//deprecated bbr->rtt_us = rs->rtt_us; +//deprecated rtt_prior = minmax_get(&bbr->max_rtt); +//deprecated bbr->rtt_us = min(bbr->rtt_us, rtt_prior); + +//deprecated minmax_running_max(&bbr->max_rtt, bbr_bw_rtts, bbr->rtt_cnt, rs->rtt_us); + + if (bbr_probe_rtt_mode_ms > 0 && filter_expired && + !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) { + bbr->mode = BBR_PROBE_RTT; /* dip, drain queue */ + bbr->pacing_gain = BBR_UNIT; + bbr->cwnd_gain = BBR_UNIT; + bbr_save_cwnd(sk); /* note cwnd so we can restore it */ + bbr->probe_rtt_done_stamp = 0; + } + + if (bbr->mode == BBR_PROBE_RTT) { + /* Ignore low rate samples during this mode. */ + tp->app_limited = + (tp->delivered + tcp_packets_in_flight(tp)) ? : 1; + /* Maintain min packets in flight for max(200 ms, 1 round). */ + if (!bbr->probe_rtt_done_stamp && + tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) { + bbr->probe_rtt_done_stamp = tcp_time_stamp + + msecs_to_jiffies(bbr_probe_rtt_mode_ms >> 1); + bbr->probe_rtt_round_done = 0; + bbr->next_rtt_delivered = tp->delivered; + } else if (bbr->probe_rtt_done_stamp) { + if (bbr->round_start) + bbr->probe_rtt_round_done = 1; + if (bbr->probe_rtt_round_done && + after(tcp_time_stamp, bbr->probe_rtt_done_stamp)) { + bbr->min_rtt_stamp = tcp_time_stamp; + bbr->restore_cwnd = 1; /* snap to prior_cwnd */ + bbr_reset_mode(sk); + } + } + } + bbr->idle_restart = 0; +} + +static void bbr_update_model(struct sock *sk, const struct rate_sample *rs) +{ + bbr_update_bw(sk, rs); + bbr_update_cycle_phase(sk, rs); + bbr_check_full_bw_reached(sk, rs); + bbr_check_drain(sk, rs); + bbr_update_min_rtt(sk, rs); +} + +static void bbr_main(struct sock *sk, const struct rate_sample *rs) +{ + struct bbr *bbr = inet_csk_ca(sk); + u32 bw; + + bbr_update_model(sk, rs); + + bw = bbr_bw(sk); + bbr_set_pacing_rate(sk, bw, bbr->pacing_gain); + bbr_set_tso_segs_goal(sk); + bbr_set_cwnd(sk, rs, rs->acked_sacked, bw, bbr->cwnd_gain); +} + +static void bbr_init(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw; + + bbr->prior_cwnd = 0; + bbr->tso_segs_goal = 0; /* default segs per skb until first ACK */ + bbr->rtt_cnt = 0; + bbr->next_rtt_delivered = 0; + bbr->prev_ca_state = TCP_CA_Open; + bbr->packet_conservation = 0; + + bbr->probe_rtt_done_stamp = 0; + bbr->probe_rtt_round_done = 0; + bbr->min_rtt_us = tcp_min_rtt(tp); + bbr->min_rtt_stamp = tcp_time_stamp; + + minmax_reset(&bbr->bw, bbr->rtt_cnt, 0); /* init max bw to 0 */ + + /* Initialize pacing rate to: high_gain * init_cwnd / RTT. */ + bw = (u64)tp->snd_cwnd * BW_UNIT; + do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC); + sk->sk_pacing_rate = 0; /* force an update of sk_pacing_rate */ + bbr_set_pacing_rate(sk, bw, bbr_high_gain); + + bbr->restore_cwnd = 0; + bbr->round_start = 0; + bbr->idle_restart = 0; + bbr->full_bw = 0; + bbr->full_bw_cnt = 0; + bbr->cycle_mstamp.v64 = 0; + bbr->cycle_idx = 0; + bbr_reset_lt_bw_sampling(sk); + bbr_reset_startup_mode(sk); +} + +static u32 bbr_sndbuf_expand(struct sock *sk) +{ + /* Provision 3 * cwnd since BBR may slow-start even during recovery. */ + return 3; +} + +/* In theory BBR does not need to undo the cwnd since it does not + * always reduce cwnd on losses (see bbr_main()). Keep it for now. + */ +static u32 bbr_undo_cwnd(struct sock *sk) +{ + return tcp_sk(sk)->snd_cwnd; +} + +/* Entering loss recovery, so save cwnd for when we exit or undo recovery. */ +static u32 bbr_ssthresh(struct sock *sk) +{ + bbr_save_cwnd(sk); + return TCP_INFINITE_SSTHRESH; /* BBR does not use ssthresh */ +} + +static size_t bbr_get_info(struct sock *sk, u32 ext, int *attr, + union tcp_cc_info *info) +{ + if (ext & (1 << (INET_DIAG_BBRINFO - 1)) || + ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcp_sock *tp = tcp_sk(sk); + struct bbr *bbr = inet_csk_ca(sk); + u64 bw = bbr_bw(sk); + + bw = bw * tp->mss_cache * USEC_PER_SEC >> BW_SCALE; + memset(&info->bbr, 0, sizeof(info->bbr)); + info->bbr.bbr_bw_lo = (u32)bw; + info->bbr.bbr_bw_hi = (u32)(bw >> 32); + info->bbr.bbr_min_rtt = bbr->min_rtt_us; + info->bbr.bbr_pacing_gain = bbr->pacing_gain; + info->bbr.bbr_cwnd_gain = bbr->cwnd_gain; + *attr = INET_DIAG_BBRINFO; + return sizeof(info->bbr); + } + return 0; +} + +static void bbr_set_state(struct sock *sk, u8 new_state) +{ + struct bbr *bbr = inet_csk_ca(sk); + + if (new_state == TCP_CA_Loss) { + struct rate_sample rs = { .losses = 1 }; + + bbr->prev_ca_state = TCP_CA_Loss; + bbr->full_bw = 0; + bbr->round_start = 1; /* treat RTO like end of a round */ + bbr_lt_bw_sampling(sk, &rs); + } +} + +static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = { + .flags = TCP_CONG_NON_RESTRICTED, + .name = "tsunami_lon", + .owner = THIS_MODULE, + .init = bbr_init, + .cong_control = bbr_main, + .sndbuf_expand = bbr_sndbuf_expand, + .undo_cwnd = bbr_undo_cwnd, + .cwnd_event = bbr_cwnd_event, + .ssthresh = bbr_ssthresh, + .tso_segs_goal = bbr_tso_segs_goal, + .get_info = bbr_get_info, + .set_state = bbr_set_state, +}; + +static int __init bbr_register(void) +{ + BUILD_BUG_ON(sizeof(struct bbr) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_bbr_cong_ops); +} + +static void __exit bbr_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_bbr_cong_ops); +} + +module_init(bbr_register); +module_exit(bbr_unregister); + +MODULE_AUTHOR("Van Jacobson "); +MODULE_AUTHOR("Neal Cardwell "); +MODULE_AUTHOR("Yuchung Cheng "); +MODULE_AUTHOR("Soheil Hassas Yeganeh "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("TCP BBR (Bottleneck Bandwidth and RTT)"); \ No newline at end of file diff --git a/tcpx.sh b/tcpx.sh new file mode 100644 index 0000000..b7dc296 --- /dev/null +++ b/tcpx.sh @@ -0,0 +1,2668 @@ +#!/usr/bin/env bash + +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +# ================================================= +# 全局配置区 (Configuration as Data) +# ================================================= +readonly SH_VER="100.0.6.8" +readonly GITHUB_RAW_URL="https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master" +readonly CLOUD_STATE_FILE="/etc/tcpx_cloud_lastver" # installcloud 记忆上次探测到的最高可安装 Cloud 内核 + +# 自安装:把脚本落地到 /usr/local/bin/tcpx,安装后可直接输入 tcpx 运行。 +# 以 bash <(curl -fsSL ) 这类管道方式运行时本地没有文件副本,只能从下面的地址重新下载。 +# 默认使用与 Update_Shell 相同的权威地址;也可用环境变量 TCPX_URL 临时覆盖: +# TCPX_URL=<地址> bash <(curl -fsSL <地址>) +readonly TCPX_URL_DEFAULT="${GITHUB_RAW_URL}/tcpx.sh" +readonly TCPX_SELF_URL="${TCPX_URL:-$TCPX_URL_DEFAULT}" +readonly TCPX_INSTALL_PATH="/usr/local/bin/tcpx" + +# 颜色变量定义 +readonly GREEN_FONT_PREFIX="\033[32m" +readonly RED_FONT_PREFIX="\033[31m" +readonly YELLOW_FONT_PREFIX="\033[33m" +readonly FONT_COLOR_SUFFIX="\033[0m" +readonly INFO="${GREEN_FONT_PREFIX}[信息]${FONT_COLOR_SUFFIX}" +readonly ERROR="${RED_FONT_PREFIX}[错误]${FONT_COLOR_SUFFIX}" +readonly TIP="${YELLOW_FONT_PREFIX}[注意]${FONT_COLOR_SUFFIX}" + +# 系统信息全局变量 (初始化) +OS_TYPE="" +OS_ID="" +OS_VERSION_ID="" +OS_ARCH="" + +# 检查当前用户是否为 root +if [ "$EUID" -ne 0 ]; then + echo -e "${ERROR} 请使用 root 用户身份运行此脚本" + exit 1 +fi + +# ================================================= +# 系统检测模块 +# ================================================= +check_sys() { + # 1. 检测架构 (使用最通用的 uname) + OS_ARCH=$(uname -m) + + # 2. 现代化系统信息获取 + if [[ -f /etc/os-release ]]; then + # 直接 source 解析标准的 os-release 文件 + . /etc/os-release + OS_ID="${ID:-unknown}" + OS_VERSION_ID="${VERSION_ID:-}" + OS_ID_LIKE="${ID_LIKE:-}" # 新增:获取上游衍生关系 + # 兼容 Debian testing/sid 没有 VERSION_ID 的情况 + if [[ -z "$OS_VERSION_ID" && "$OS_ID" == "debian" && -f /etc/debian_version ]]; then + OS_VERSION_ID=$(grep -oE '^[0-9]+' /etc/debian_version | head -n 1) + [[ -z "$OS_VERSION_ID" ]] && OS_VERSION_ID=$(awk -F'/' '{print $1}' /etc/debian_version) + fi + [[ -z "$OS_VERSION_ID" ]] && OS_VERSION_ID="unknown" + elif [[ -f /etc/redhat-release || -f /etc/centos-release ]]; then + # 兼容极少数没有 os-release 的老旧 CentOS + OS_ID="centos" + OS_VERSION_ID=$(grep -oE '[0-9.]+' /etc/redhat-release | awk -F'.' '{print $1}') + else + echo -e "${ERROR} 无法检测到受支持的系统版本。此脚本仅支持现代 Debian/Ubuntu/CentOS/Alma/Rocky 系统。" + exit 1 + fi + + # 3. 规范化 OS_TYPE (引入 ID_LIKE 增强泛衍生版兼容性) + if [[ "$OS_ID" =~ ^(centos|rhel|almalinux|rocky|oracle|fedora)$ ]] || [[ "$OS_ID_LIKE" =~ (rhel|centos|fedora) ]]; then + OS_TYPE="CentOS" + # 提取主版本号 + OS_VERSION_ID=$(echo "$OS_VERSION_ID" | awk -F'.' '{print $1}') + elif [[ "$OS_ID" =~ ^(debian|ubuntu|pop|kali|linuxmint|deepin|elementary|zorin|armbian)$ ]] || [[ "$OS_ID_LIKE" =~ (debian|ubuntu) ]]; then + OS_TYPE="Debian" + else + echo -e "${ERROR} 不支持的系统分支: ${OS_ID} (ID_LIKE: ${OS_ID_LIKE})" + exit 1 + fi + + echo -e "${INFO} 检测到系统: ${OS_TYPE} (${OS_ID} ${OS_VERSION_ID}) - 架构: ${OS_ARCH}" + + # 4. 精简依赖检查 (抛弃笨重的 lsb_release,引入轻量的 jq 用于后续 API 解析) + local required_cmds=("curl" "wget" "awk" "jq") + + if [[ "${OS_TYPE}" == "CentOS" ]]; then + for cmd in "${required_cmds[@]}"; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo -e "${INFO} 正在安装缺失依赖: $cmd ..." + if [[ "$cmd" == "jq" ]] && ! rpm -q epel-release >/dev/null 2>&1; then + yum install -y epel-release >/dev/null 2>&1 + fi + yum install -y "$cmd" >/dev/null 2>&1 + fi + done + # CA 证书更新 + if ! rpm -q ca-certificates >/dev/null 2>&1; then + yum install ca-certificates -y >/dev/null 2>&1 + update-ca-trust force-enable + fi + elif [[ "${OS_TYPE}" == "Debian" ]]; then + local need_update=0 + for cmd in "${required_cmds[@]}"; do + if ! command -v "$cmd" >/dev/null 2>&1; then + if [[ $need_update -eq 0 ]]; then + apt-get update >/dev/null 2>&1 + need_update=1 + fi + echo -e "${INFO} 正在安装缺失依赖: $cmd ..." + apt-get install -y "$cmd" >/dev/null 2>&1 + fi + done + # CA 证书更新 + if ! dpkg-query -W ca-certificates >/dev/null 2>&1; then + [[ $need_update -eq 0 ]] && apt-get update >/dev/null 2>&1 + apt-get install ca-certificates -y >/dev/null 2>&1 + update-ca-certificates >/dev/null 2>&1 + fi + fi + + # 4.1 依赖复检:上面所有安装命令的输出都被重定向丢弃了,装失败也毫无提示。 + # 若不在此处硬失败,后续 get_github_asset 会因缺少 jq 而静默返回空, + # 用户只能看到"无法获取资源列表"这种完全误导性的报错。 + local missing_cmds=() + for cmd in "${required_cmds[@]}"; do + command -v "$cmd" >/dev/null 2>&1 || missing_cmds+=("$cmd") + done + if [[ ${#missing_cmds[@]} -gt 0 ]]; then + echo -e "${ERROR} 以下必需依赖安装失败,脚本无法继续运行:" + for cmd in "${missing_cmds[@]}"; do + echo -e " - ${RED_FONT_PREFIX}${cmd}${FONT_COLOR_SUFFIX}" + done + echo -e "${TIP} 请先手动安装后重试,例如:" + if [[ "${OS_TYPE}" == "CentOS" ]]; then + echo -e " yum install -y epel-release && yum install -y ${missing_cmds[*]}" + else + echo -e " apt-get update && apt-get install -y ${missing_cmds[*]}" + fi + exit 1 + fi + + # 5. 补充底层内核模块管理依赖 (应对极简版 LXC/VPS 模板) + if ! command -v lsmod >/dev/null 2>&1; then + echo -e "${INFO} 正在补齐系统核心依赖: kmod (提供 lsmod/rmmod 命令) ..." + if [[ "${OS_TYPE}" == "CentOS" ]]; then + yum install -y kmod >/dev/null 2>&1 + elif [[ "${OS_TYPE}" == "Debian" ]]; then + apt-get install -y kmod >/dev/null 2>&1 + fi + fi +} + +# ================================================= +# 安装前置守卫 (容器环境 / /boot 空间) +# ================================================= + +# 检测是否运行在容器中。容器共用宿主机内核,装内核毫无意义且可能破坏容器。 +check_container() { + local virt="" + if command -v systemd-detect-virt >/dev/null 2>&1; then + virt=$(systemd-detect-virt 2>/dev/null) + elif command -v virt-what >/dev/null 2>&1; then + virt=$(virt-what 2>/dev/null | head -n 1) + fi + + case "$virt" in + lxc | lxc-libvirt | openvz | docker | podman | rkt | wsl | systemd-nspawn) + echo -e "${ERROR} 检测到当前为容器环境 (${virt}),容器共用宿主机内核,无法安装内核!" + return 1 + ;; + esac + + # 兜底: 部分老旧 OpenVZ/LXC 无 systemd-detect-virt,用特征文件识别 + if [[ -d /proc/vz && ! -d /proc/bc ]]; then + echo -e "${ERROR} 检测到 OpenVZ 容器环境,无法安装内核!" + return 1 + fi + if grep -qaE '(lxc|docker|containerd)' /proc/1/cgroup 2>/dev/null; then + echo -e "${ERROR} 检测到容器环境 (cgroup 特征),无法安装内核!" + return 1 + fi + return 0 +} + +# 检查 /boot 剩余空间。小 /boot 在生成 initramfs 阶段中途失败会留下半配置状态,重启即失联。 +check_boot_space() { + local need_mb="${1:-300}" + local avail_mb + avail_mb=$(df -Pm /boot 2>/dev/null | awk 'NR==2 {print $4}') + + if [[ -z "$avail_mb" ]]; then + echo -e "${TIP} 无法获取 /boot 剩余空间,跳过检查。" + return 0 + fi + + if [[ "$avail_mb" -lt "$need_mb" ]]; then + echo -e "${ERROR} /boot 剩余空间不足: 当前 ${avail_mb}MB,建议至少 ${need_mb}MB。" + echo -e "${TIP} 内核安装极可能在生成 initramfs 时中途失败,导致系统无法引导!" + echo -e "${TIP} 请先使用菜单 [52] 删除不再使用的旧内核后重试。" + read -rp "确认无视风险并继续?(请输入大写 YES 确认): " force_go + [[ "$force_go" != "YES" ]] && { + echo -e "${INFO} 已取消安装。" + return 1 + } + else + echo -e "${INFO} /boot 剩余空间检查通过: ${avail_mb}MB 可用。" + fi + return 0 +} + +# 内核安装统一前置检查 +pre_install_check() { + check_container || return 1 + check_boot_space 300 || return 1 + return 0 +} + +# ================================================= +# 网络通信与下载模块 +# ================================================= + +# 全局变量:是否在中国大陆 +IS_CN=0 + +# 全局最优镜像变量 +BEST_MIRROR="" + +# 判断 URL 是否为 GitHub 系资源 (只有这些才需要走加速镜像) +# 非 GitHub 域名 (如 deb.debian.org / snapshot.debian.org) 套镜像前缀必然 404, +# 白白消耗 4 次超时,故必须先行识别。 +is_github_url() { + local url="$1" + [[ "$url" =~ ^https?://(www\.)?(github\.com|raw\.githubusercontent\.com|objects\.githubusercontent\.com|codeload\.github\.com)/ ]] +} + +# 统一的镜像 URL 组装规则 (测速与实际下载共用,保证两者行为一致) +build_mirror_url() { + local prefix="$1" + local url="$2" + if [[ -z "$prefix" ]]; then + echo "$url" + else + # 镜像站约定: 前缀后直接接不带协议头的原始地址 + echo "${prefix}$(echo "$url" | sed 's|^https\?://||')" + fi +} + +# 1. 极其稳定且快速的 CN 节点检测 (利用 Cloudflare CDN Trace) 与镜像测速 +check_cn_status() { + # 设置 3 秒超时,获取 Cloudflare 边缘节点看到的 IP 归属地 + local cf_trace=$(curl -sL --max-time 3 https://www.cloudflare.com/cdn-cgi/trace || echo "") + if echo "$cf_trace" | grep -q "loc=CN"; then + IS_CN=1 + echo -e "${INFO} 检测到当前节点位于中国大陆,正在为您测速并选择最快的 GitHub 镜像..." + + # 优先测速:只探测 3 个高质量镜像,选最快者作为首选 + local mirrors=( + "https://gh-proxy.com/" + "https://ghproxy.net/" + "https://fastgit.cc/" + ) + + # 使用极小的 releases 校验文件作为测速目标 (不到 100 字节),完美适配镜像站的 release 代理规则 + local test_url="https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64.sha256" + local best_time=9999 + local failed_mirrors=() + + for prefix in "${mirrors[@]}"; do + # 与 safe_wget 保持完全一致的 URL 改写规则,确保测速结果能代表实际下载可用性 + local target_url=$(build_mirror_url "$prefix" "$test_url") + + # 测速: 限定最多 2 秒超时,-r 0-512 限制只读取头部数据,防止大文件卡死 + # 同时取回 http_code,避免"快速返回 403/404 错误页"的失效镜像在测速中胜出 + local probe=$(curl -sL -m 2 -r 0-512 -o /dev/null -w "%{http_code} %{time_total}" "$target_url" 2>/dev/null) + local http_code=$(echo "$probe" | awk '{print $1}') + local time_cost=$(echo "$probe" | awk '{print $2}') + + # 仅 200/206 视为有效;其余(含连接失败的空输出)一律记为失败节点 + if [[ ! "$http_code" =~ ^(200|206)$ ]] || [[ -z "$time_cost" ]]; then + failed_mirrors+=("$prefix") + else + # 使用 awk 比较浮点数时间,找出延迟最低的 + if awk "BEGIN {exit !($time_cost < $best_time)}"; then + best_time=$time_cost + BEST_MIRROR=$prefix + fi + fi + done + + if [[ "$best_time" == "9999" ]]; then + echo -e "${TIP} 所有镜像测速超时,将使用默认轮询模式。" + BEST_MIRROR="poll" + else + echo -e "${INFO} 测速完成!当前网络最优镜像为: ${GREEN_FONT_PREFIX}${BEST_MIRROR}${FONT_COLOR_SUFFIX} (响应时间: ${best_time}s)" + fi + + # 集中打印出测速失败的镜像,方便您排查和后续替换 + if [[ ${#failed_mirrors[@]} -gt 0 ]]; then + echo -e "${TIP} 以下镜像测速超时或失效 (已自动剔除首选):" + for fail_m in "${failed_mirrors[@]}"; do + echo -e " - ${RED_FONT_PREFIX}${fail_m}${FONT_COLOR_SUFFIX}" + done + fi + else + IS_CN=0 + echo -e "${INFO} 当前节点位于海外,使用 GitHub 直连网络。" + fi +} + +# 2. 安全可靠的下载函数 (自带多镜像轮询 failover,支持动态最优节点) +safe_wget() { + local url="$1" + local dest="$2" + local timeout=15 + + # 定义默认的加速镜像前缀池 + # 下载兜底池:3 个高质量镜像在前,其余可用镜像随后 + local mirrors=( + "https://gh-proxy.com/" + "https://ghproxy.net/" + "https://fastgit.cc/" + "https://githubdog.com/" + "https://tvv.tw/" + "https://ghfast.top/" + ) + + # 核心逻辑:根据测速结果重构下载队列 + # 非 GitHub 资源 (Debian 官方源等) 直接直连,套镜像只会白等 4 次超时 + if [[ $IS_CN -eq 0 ]] || ! is_github_url "$url"; then + mirrors=("") # 海外节点 / 非 GitHub 资源只保留原生空前缀 + else + # 如果测速成功找到了最优节点,将其提取到数组最前面 + if [[ -n "$BEST_MIRROR" && "$BEST_MIRROR" != "poll" ]]; then + local new_mirrors=("$BEST_MIRROR") + for m in "${mirrors[@]}"; do + [[ "$m" != "$BEST_MIRROR" ]] && new_mirrors+=("$m") + done + mirrors=("${new_mirrors[@]}") + fi + # 给国内机加上原生链接作为终极保底 + mirrors+=("") + fi + + for prefix in "${mirrors[@]}"; do + # 组装最终下载链接 (与测速阶段共用同一套规则) + local target_url=$(build_mirror_url "$prefix" "$url") + + if [[ -z "$prefix" ]]; then + echo -e "${INFO} 正在请求: ${dest} (直连网络) ..." + else + echo -e "${INFO} 正在请求: ${dest} (镜像: ${prefix}) ..." + fi + + # 使用 wget 下载,跳过证书校验 + if wget --no-check-certificate -qT "$timeout" -t 2 -O "$dest" "$target_url"; then + # 防御半截包/错误页: 空文件视为失败,继续切换节点 + if [[ -s "$dest" ]]; then + echo -e "${INFO} ${dest} 下载成功!" + return 0 + fi + echo -e "${TIP} 下载内容为空,判定为无效响应。" + rm -f "$dest" + fi + + # 当前节点失败时提示并继续下一个循环 + echo -e "${TIP} 当前节点下载异常,正在无缝切换备用节点..." + done + + echo -e "${ERROR} 文件 ${dest} 所有下载节点均失效,请检查网络或稍后再试!" + return 1 +} + +# 3. 稳健的 GitHub 资源获取函数 (使用 jq 提取 JSON,再通过 grep 多重过滤) +# 用法: get_github_asset <仓库名> <文件名关键词> +# 示例: get_github_asset "ylx2016/kernel" "Debian_Kernel" "headers" +get_github_asset() { + local repo="$1" + local tag_kw="$2" + local ast_kw="$3" + local arch_kw="$4" # 可选的架构关键词 + local api_url="https://api.github.com/repos/${repo}/releases" + + local response=$(curl -sL --max-time 10 "$api_url") + if echo "$response" | grep -q "API rate limit exceeded"; then + echo -e "${ERROR} 触发 GitHub API 频率限制!(当前 IP 请求过多)" >&2 + return 1 + fi + + # 提取出该仓库所有的下载直链 + local all_urls=$(echo "$response" | jq -r '.[].assets[]?.browser_download_url' 2>/dev/null) + if [[ -z "$all_urls" ]]; then + echo -e "${ERROR} 无法从 ${repo} 获取资源列表,请检查网络或稍后再试!" >&2 + return 1 + fi + + # 利用 grep -iE 进行层层精准过滤 + local result=$(echo "$all_urls" | grep -iE "$tag_kw" | grep -iE "$ast_kw") + [[ -n "$arch_kw" ]] && result=$(echo "$result" | grep -iE "$arch_kw") + + # 排除调试符号包与未签名包:dbg 包体积可达数百 MB 且完全不可用于安装, + # 而 grep "image" 会同时命中 linux-image-*-dbg_*.deb,head -n 1 有取错风险 + result=$(echo "$result" | grep -viE 'dbg|debug|unsigned') + + # 终极防呆机制:如果是 x86_64 架构,且关键词中没有声明要找 arm64,则强行排除带 arm64/aarch64 的链接,防止模糊匹配误伤 + if [[ "$arch_kw" != *"arm64"* && "$tag_kw" != *"arm64"* && "$OS_ARCH" != "aarch64" ]]; then + result=$(echo "$result" | grep -viE "arm64|aarch64") + fi + + local asset_url=$(echo "$result" | head -n 1) + + if [[ -z "$asset_url" ]]; then + echo -e "${ERROR} 无法在 ${repo} 中解析到匹配关键字 (${tag_kw} -> ${ast_kw} -> ${arch_kw}) 的文件!" >&2 + return 1 + fi + + echo "$asset_url" +} + +# ================================================= +# 内核安装核心引擎 +# ================================================= + +# 清理旧的 Headers (精简重构) +remove_old_headers() { + echo -e "${INFO} 正在清理旧的内核 Headers 防止冲突..." + if [[ "${OS_TYPE}" == "CentOS" ]]; then + # 找出不是当前正在运行的 kernel-headers 并卸载 + local current_ker=$(uname -r) + rpm -qa | grep -E 'kernel(-ml|-lt|-uek|-rt|-plus)?-headers' | grep -v "$current_ker" | xargs -r rpm -e --nodeps >/dev/null 2>&1 + elif [[ "${OS_TYPE}" == "Debian" ]]; then + dpkg -l | grep -E '(linux|proxmox|pve|raspberrypi)-headers' | awk '{print $2}' | grep -v "$(uname -r)" | xargs -r apt-get purge -y >/dev/null 2>&1 + apt-get autoremove -y >/dev/null 2>&1 + fi +} + +# 终极内核安装函数 +# 用法: install_kernel_generic <内核描述名称> <版本号> +install_kernel_generic() { + local kernel_desc="$1" + local head_url="$2" + local img_url="$3" + local kernel_version="$4" # 新增参数,用于 UI 显示 + + echo -e "${INFO} ================================================" + if [[ -n "$kernel_version" ]]; then + echo -e "${INFO} 开始安装: ${kernel_desc} (版本: \033[32m${kernel_version}\033[0m)" + else + echo -e "${INFO} 开始安装: ${kernel_desc}" + fi + echo -e "${INFO} ================================================" + + # 只强制检查 img_url,因为某些内核(如 Cloud)本身可能不强制要求 Headers + if [[ -z "$img_url" ]]; then + echo -e "${ERROR} 传入的镜像文件下载链接为空,可能是 API 解析失败或上游移除了文件!" + return 1 + fi + + # 创建独立的工作目录 (mktemp 避免 /tmp 下可预测目录名被抢先创建) + local work_dir + work_dir=$(mktemp -d /tmp/kernel_install.XXXXXX) || { + echo -e "${ERROR} 无法创建临时目录!" + return 1 + } + # apt 会用沙箱用户 _apt 访问此目录;700 权限会触发 "unsandboxed" 警告。 + # 755 仅允许读/遍历,.deb 文件本身仍由 root 拥有,不存在安全问题。 + chmod 755 "$work_dir" + trap 'cd /tmp; rm -rf "$work_dir"; trap - RETURN' RETURN + cd "$work_dir" || { + echo -e "${ERROR} 无法进入临时目录 ${work_dir}!" + return 1 + } + + # 安装前记录 /boot 中已有的内核镜像数量,用于事后验证是否真的装上了 + local vmlinuz_before=$(ls -1 /boot/vmlinuz-* 2>/dev/null | wc -l) + + # 根据系统执行不同的下载和安装逻辑 + # 关键顺序: 先把所有包下载并校验完成,再清理旧 headers,最后安装。 + # 若先清理后下载,一旦下载失败就会停在"旧 headers 已删、新内核没装"的破损状态。 + if [[ "${OS_TYPE}" == "CentOS" ]]; then + local head_file="kernel-headers.rpm" + local img_file="kernel-image.rpm" + + [[ -n "$head_url" ]] && { safe_wget "$head_url" "$head_file" || return 1; } + safe_wget "$img_url" "$img_file" || return 1 + + # 下载全部成功后才动系统 + remove_old_headers + + echo -e "${INFO} 正在执行 YUM 安装..." + if [[ -n "$head_url" ]]; then + yum install -y "./$img_file" "./$head_file" || { + echo -e "${ERROR} 内核包安装失败,请检查上方 yum 输出!" + return 1 + } + else + yum install -y "./$img_file" || { + echo -e "${ERROR} 内核包安装失败,请检查上方 yum 输出!" + return 1 + } + fi + + elif [[ "${OS_TYPE}" == "Debian" ]]; then + local head_file="linux-headers.deb" + local img_file="linux-image.deb" + local deb_list=() + + [[ -n "$head_url" ]] && { safe_wget "$head_url" "$head_file" || return 1; } + safe_wget "$img_url" "$img_file" || return 1 + + # 下载全部成功后才动系统 + remove_old_headers + + deb_list+=("./$img_file") + [[ -n "$head_url" && -s "$head_file" ]] && deb_list+=("./$head_file") + + # 用 apt-get install ./xxx.deb 取代 dpkg -i + apt-get -f。 + # 前者是事务性的: 会自动从源里解析 linux-modules-* 等依赖,失败时干净回滚; + # 后者在依赖无解时 apt-get -f 可能选择"卸载刚装上的内核"来修复,且返回值常为 0。 + echo -e "${INFO} 正在执行 APT 安装 (自动解析依赖)..." + if ! DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${deb_list[@]}"; then + echo -e "${ERROR} 内核包安装失败,依赖无法满足或包不兼容当前系统!" + echo -e "${TIP} 系统未被改动,可尝试菜单 [4] 中的 'a' 自动探测可安装版本。" + return 1 + fi + fi + + # 验证 /boot 中确实新增了内核镜像,避免"装失败却报成功" + local vmlinuz_after=$(ls -1 /boot/vmlinuz-* 2>/dev/null | wc -l) + if [[ "$vmlinuz_after" -le "$vmlinuz_before" ]]; then + echo -e "${ERROR} /boot 中未检测到新增的内核镜像,安装可能未真正生效!" + echo -e "${TIP} 请勿重启,先用菜单 [51] 确认当前内核状态。" + return 1 + fi + + echo -e "${INFO} ${kernel_desc} 内核包安装完成,正在更新系统引导..." + BBR_grub +} + +# 安装 BBR 原版内核 (调用引擎) +installbbr() { + pre_install_check || return 1 + local head_url="" + local img_url="" + local tag_kw="" + local arch_kw="" + local img_kw="" + + if [[ "${OS_TYPE}" == "CentOS" ]]; then + # 适配新编译的 CentOS Cloud 内核 + tag_kw="CentOS_Kernel_Cloud" + arch_kw="x86_64" + # 【核心修复 1】直接匹配 kernel-加数字,从源头完美避开 headers 和 devel + img_kw="kernel-[0-9]" + elif [[ "${OS_TYPE}" == "Debian" ]]; then + # 适配新编译的 Debian Cloud 内核 + tag_kw="Debian_Kernel_Cloud" + arch_kw="amd64" + img_kw="image" # Debian dpkg 生成的包名为 linux-image 开头 + if [[ "$OS_ARCH" == "aarch64" ]]; then + tag_kw="Debian_Kernel_Cloud_arm64" + arch_kw="arm64" + fi + fi + + echo -e "${INFO} 正在向 Github/ylx2016 请求最新 ${tag_kw} 内核数据..." + + # 获取下载链接 (现在抓取到的绝对纯净,无需二次 grep -v) + head_url=$(get_github_asset "ylx2016/kernel" "${tag_kw}" "headers" "${arch_kw}") + img_url=$(get_github_asset "ylx2016/kernel" "${tag_kw}" "${img_kw}" "${arch_kw}") + + # 【核心修复 2】利用 -oE 标准正则提取版本号,避免部分系统不支持 -P 导致版本号变空 + local kernel_version=$(echo "$img_url" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1) + + if [[ -n "$kernel_version" ]]; then + echo -e "${INFO} 解析成功!获取到的最新云端内核版本为: \033[32m${kernel_version}\033[0m" + else + echo -e "${INFO} 解析成功!已获取到下载链接,但未能匹配出纯净版本号。" + fi + + # 将解析出的版本号作为第四个参数传递给安装函数 + install_kernel_generic "BBR Cloud 优化内核" "$head_url" "$img_url" "$kernel_version" +} + +# 安装 BBRplus 新版内核 (调用引擎) +installbbrplusnew() { + pre_install_check || return 1 + local head_url="" + local img_url="" + local tag_kw="bbrplus-6." + local ext="deb" + local arch_kw="amd64" + # Debian 的包名为 linux-image-*,而 RPM 的包名是 kernel-<版本号>,二者必须区分 + local img_kw="image" + + if [[ "${OS_TYPE}" == "CentOS" ]]; then + ext="rpm" + arch_kw="x86_64" # RPM 文件名用 x86_64,沿用 amd64 会过滤为空 + img_kw="kernel-[0-9]" # 直接匹配 kernel-加数字,从源头避开 headers/devel + [[ "$OS_ARCH" == "aarch64" ]] && arch_kw="aarch64" + else + [[ "$OS_ARCH" == "aarch64" ]] && arch_kw="arm64" + fi + + echo -e "${INFO} 正在向 UJX6N/bbrplus-6.x_stable 请求数据..." + # 利用精准的参数向下传递 + head_url=$(get_github_asset "UJX6N/bbrplus-6.x_stable" "${tag_kw}" "headers" "${arch_kw}.*${ext}") + img_url=$(get_github_asset "UJX6N/bbrplus-6.x_stable" "${tag_kw}" "${img_kw}" "${arch_kw}.*${ext}") + + local kernel_version=$(echo "$img_url" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n 1) + install_kernel_generic "BBRplus(UJX6N)新版内核" "$head_url" "$img_url" "$kernel_version" +} + +# 安装 BBRplus 内核 4.14.129 (cx9208版) +installbbrplus() { + pre_install_check || return 1 + local head_url="" + local img_url="" + + if [[ "${OS_TYPE}" == "CentOS" && "${OS_VERSION_ID}" == "7" ]]; then + head_url="https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/centos/7/kernel-headers-4.14.129-bbrplus.rpm" + img_url="https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/centos/7/kernel-4.14.129-bbrplus.rpm" + elif [[ "${OS_TYPE}" == "Debian" && "${OS_ARCH}" == "x86_64" ]]; then + head_url="https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/debian-ubuntu/x64/linux-headers-4.14.129-bbrplus.deb" + img_url="https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/debian-ubuntu/x64/linux-image-4.14.129-bbrplus.deb" + else + echo -e "${ERROR} BBRplus 4.14.129 仅支持 CentOS 7 或 Debian x86_64!" + return 1 + fi + + install_kernel_generic "BBRplus 4.14.129" "$head_url" "$img_url" +} + +# ================================================= +# 自动探测 Cloud 内核最高可安装版本 (依赖解析) +# ================================================= +# 用法: detect_cloud_max ... +# 原理: pool 中混着 stable/testing/sid/experimental 全部内核,版本号最大 ≠ 能装。 +# 太新的内核依赖独立的 linux-modules-*-cloud-* 包(只存在于其自身发行版源), +# 并可能要求更新的 linux-base / initramfs-tools / libc6。因此从新到旧逐个 +# 下载 image 包交给 `apt-get install -s` 做模拟安装(真实依赖解析器),第一个 +# “干净解析”的版本即当前系统最高可安装版本。 +# 结果写入全局变量 CLOUD_MAX_IDX / CLOUD_MAX_FILE (仍为 -1 / 空 表示无可用版本) +CLOUD_MAX_IDX=-1 +CLOUD_MAX_FILE="" +detect_cloud_max() { + local img_url_base="$1" + shift + local versions_array=("$@") + CLOUD_MAX_IDX=-1 + CLOUD_MAX_FILE="" + + local scan_work + scan_work=$(mktemp -d /tmp/cloud_kernel_scan.XXXXXX) || return 1 + trap 'cd /tmp; rm -rf "$scan_work"; trap - RETURN' RETURN + cd "$scan_work" || return 1 + + for ((i = ${#versions_array[@]} - 1; i >= 0; i--)); do + local f="${versions_array[$i]}" + echo -e "${INFO} 正在探测候选内核: ${f} ..." + if ! safe_wget "${img_url_base}${f}" "probe.deb" >/dev/null 2>&1; then + continue + fi + # 校验是合法 ar 归档,防止半截包造成误判 + if ! head -c 8 probe.deb | grep -q "!"; then + rm -f probe.deb + continue + fi + + # 必须强制 LC_ALL=C: 下面全部靠 grep apt 的英文输出判断依赖是否可满足, + # 在 zh_CN 等非英文 locale 下 apt 输出被翻译,所有 grep 都匹配不到, + # 会导致每个版本都被误判为"依赖可干净满足",本函数彻底失效。 + local scan_log=$(LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get install -s --no-install-recommends "./probe.deb" 2>&1) + rm -f probe.deb + + local ok=1 + # 1) 依赖无法满足 / 包破损 + if echo "$scan_log" | grep -qiE "unmet dependencies|broken packages|E: "; then + ok=0 + fi + # 2) 会卸载现有软件包 = 必然冲突 + if echo "$scan_log" | grep -q "will be REMOVED"; then + ok=0 + fi + # 3) 会升级到跨发行版核心基础库 (libc6/base-files/dpkg/systemd) = 硬装,跳过。 + # 允许升级 linux-base/initramfs-tools 等内核生态包 (当前源内可正常解决) + if echo "$scan_log" | grep -q "will be upgraded"; then + local upgraded=$(echo "$scan_log" | sed -n '/will be upgraded/,/^[^ ]/p' | tail -n +2) + if echo "$upgraded" | grep -qwE "libc6|base-files|dpkg|systemd|systemd-sysv|bash|login|tzdata"; then + ok=0 + fi + fi + + if [[ $ok -eq 1 ]]; then + CLOUD_MAX_IDX=$i + CLOUD_MAX_FILE="$f" + echo -e "${GREEN_FONT_PREFIX} ✓ 该版本依赖可干净满足${FONT_COLOR_SUFFIX}" + break + else + echo -e "${TIP} ✗ 该版本依赖冲突,已跳过" + fi + done + # 临时目录由 RETURN trap 统一清理 +} + +# 由 signed image 包名推导配套的 headers 下载地址 +# 例: linux-image-6.1.0-18-cloud-amd64_6.1.76-1_amd64.deb +# -> linux-headers-6.1.0-18-cloud-amd64_6.1.76-1_amd64.deb (位于 pool/main/l/linux/) +# Headers 缺失会导致后续 brutal / LotSpeed 模块无法编译,故尽量一并安装。 +get_cloud_headers_url() { + local img_file="$1" + local debarch="$2" + local hdr_base="https://deb.debian.org/debian/pool/main/l/linux/" + + # 提取 ABI 版本 (如 6.1.0-18-cloud-amd64) + local abi=$(echo "$img_file" | sed -nE "s/^linux-image-(.*-cloud-${debarch})_.*/\1/p") + [[ -z "$abi" ]] && return 0 + + # 从 pool 目录列表中查找完全匹配该 ABI 的 headers 包 + local hdr_pattern="linux-headers-${abi}_[^\" ]+_${debarch}\.deb" + local hdr_file=$(curl -sL --max-time 10 "$hdr_base" | grep -oE "$hdr_pattern" | sort -V | uniq | tail -n 1) + + if [[ -z "$hdr_file" ]]; then + echo -e "${TIP} 未找到与 ${abi} 配套的 Headers,将仅安装内核镜像。" >&2 + return 0 + fi + echo "${hdr_base}${hdr_file}" +} + +# 安装官方 Cloud 内核 +installcloud() { + pre_install_check || return 1 + + [[ "${OS_TYPE}" != "Debian" ]] && { + echo -e "${ERROR} Cloud 内核仅支持 Debian 系系统" + return 1 + } + + local debarch + if [[ "$OS_ARCH" == "x86_64" ]]; then + debarch="amd64" + elif [[ "$OS_ARCH" == "aarch64" ]]; then + debarch="arm64" + else + echo -e "${ERROR} 不支持的架构:$OS_ARCH" + return 1 + fi + + local img_url_base="https://deb.debian.org/debian/pool/main/l/linux-signed-${debarch}/" + local img_pattern='linux-image-[^" ]+cloud-'"${debarch}"'_[^" ]+_'"${debarch}"'\.deb' + + echo -e "${INFO} 正在从 Debian 官方源获取 Cloud 内核列表..." + local deb_files=$(curl -sL --max-time 10 "$img_url_base" | grep -oE "$img_pattern" | sort -V | uniq) + if [[ -z "$deb_files" ]]; then + echo -e "${ERROR} 未找到可用的 Cloud 内核版本,请检查网络!" + return 1 + fi + mapfile -t versions_array <<<"$deb_files" + + # ---- 读取上次记录的最高可安装版本 (用于默认选中与高亮提示) ---- + local remembered="" + [[ -f "$CLOUD_STATE_FILE" ]] && remembered=$(cat "$CLOUD_STATE_FILE" 2>/dev/null) + local remembered_idx=-1 + if [[ -n "$remembered" ]]; then + for i in "${!versions_array[@]}"; do + [[ "${versions_array[$i]}" == "$remembered" ]] && { + remembered_idx=$i + break + } + done + fi + + # ---- 展示全部版本 (保持原始行为) ---- + echo -e "${INFO} 检测到以下 Cloud 内核版本 (pool 全部候选):" + for i in "${!versions_array[@]}"; do + local v_show=$(echo "${versions_array[$i]}" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+-[0-9]+') + if [[ $i -eq $remembered_idx ]]; then + echo -e " ${GREEN_FONT_PREFIX}$i) [$v_show]${FONT_COLOR_SUFFIX} ${RED_FONT_PREFIX}← 上次最高可安装版本${FONT_COLOR_SUFFIX} -> ${versions_array[$i]}" + else + echo " $i) [$v_show] -> ${versions_array[$i]}" + fi + done + echo -e " ${GREEN_FONT_PREFIX}a) 自动逐个探测并判断最高可安装版本${FONT_COLOR_SUFFIX} ${YELLOW_FONT_PREFIX}h) 使用 apt 安装${FONT_COLOR_SUFFIX}" + + local last_idx=$((${#versions_array[@]} - 1)) + local default_idx=$last_idx + [[ $remembered_idx -ge 0 ]] && default_idx=$remembered_idx + + local def_label="$default_idx" + [[ $remembered_idx -ge 0 ]] && def_label="${default_idx} (上次记忆)" + echo -e "${TIP} 输入 'a' 自动判断最高可安装版本,输入 'h' 使用 apt 安装:" + # 内核安装不可逆,超时一律取消而非自动执行;60 秒留足阅读上面版本列表的时间 + if ! read -t 60 -rp "输入选项 [0-${last_idx} / a / h / q 取消,回车默认 ${def_label}]: " choice; then + echo "" + echo -e "${TIP} 等待超时,出于安全考虑已自动取消操作 (未做任何更改)。" + return 0 + fi + + # ---- q: 显式取消 ---- + if [[ "$choice" =~ ^[qQ]$ ]]; then + echo -e "${INFO} 已取消操作。" + return 0 + fi + + # ---- a: 自动逐个尝试判断最高可安装版本 ---- + if [[ "$choice" =~ ^[aA]$ ]]; then + echo -e "${INFO} 开始自动探测最高可安装的 Cloud 内核版本(从新到旧逐个依赖校验)..." + detect_cloud_max "$img_url_base" "${versions_array[@]}" + if [[ $CLOUD_MAX_IDX -lt 0 || -z "$CLOUD_MAX_FILE" ]]; then + echo -e "${TIP} 未找到可干净安装的 pool 版本,已回退到 apt 官方源安装 (最稳妥)。" + echo -e "${INFO} 正在使用 apt 安装 Cloud 内核及 Headers..." + apt-get update >/dev/null 2>&1 + apt-get install -y "linux-image-cloud-${debarch}" "linux-headers-cloud-${debarch}" + BBR_grub + return 0 + fi + # 记忆本次探测结果,下次进入默认选中 + echo "$CLOUD_MAX_FILE" >"$CLOUD_STATE_FILE" + echo -e "${INFO} 探测完成!当前系统最高可安装版本: ${GREEN_FONT_PREFIX}${CLOUD_MAX_FILE}${FONT_COLOR_SUFFIX},开始安装..." + install_kernel_generic "Debian 官方 Cloud" "$(get_cloud_headers_url "$CLOUD_MAX_FILE" "$debarch")" "${img_url_base}${CLOUD_MAX_FILE}" + return 0 + fi + + # ---- h: 直接使用 apt 安装当前发行版云内核 ---- + if [[ "$choice" =~ ^[hH]$ ]]; then + echo -e "${INFO} 正在使用 apt 安装 Cloud 内核及 Headers..." + apt-get update >/dev/null 2>&1 + apt-get install -y "linux-image-cloud-${debarch}" "linux-headers-cloud-${debarch}" + BBR_grub + return 0 + fi + + # ---- 数字选择或回车默认 ---- + choice=${choice:-$default_idx} + if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 0 ] || [ "$choice" -gt "$last_idx" ]; then + echo -e "${TIP} 无效选项,默认安装 ${default_idx} 号版本..." + choice=$default_idx + fi + + local selected_file="${versions_array[$choice]}" + # 一并抓取配套 Headers,否则装完无法编译 brutal/LotSpeed 模块 + install_kernel_generic "Debian 官方 Cloud" "$(get_cloud_headers_url "$selected_file" "$debarch")" "${img_url_base}${selected_file}" +} + +# 安装 Lotserver (锐速) 专属内核 +installlot() { + pre_install_check || return 1 + + [[ "$OS_ARCH" != "x86_64" ]] && { + echo -e "${ERROR} Lotserver 仅支持 x86_64 架构!" + return 1 + } + + # 上游仅提供 CentOS 6/7 的 Lotserver 内核包,8/9/10 请求的是不存在的路径 + if [[ "${OS_TYPE}" == "CentOS" && "${OS_VERSION_ID}" != "6" && "${OS_VERSION_ID}" != "7" ]]; then + echo -e "${ERROR} Lotserver 内核仅支持 CentOS 6/7,当前为 CentOS ${OS_VERSION_ID}!" + return 1 + fi + + local work_dir + work_dir=$(mktemp -d /tmp/lot_install.XXXXXX) || { + echo -e "${ERROR} 无法创建临时目录!" + return 1 + } + # 无论从哪条路径返回,都保证清理临时目录且回到安全的 CWD + trap 'cd /tmp; rm -rf "$work_dir"; trap - RETURN' RETURN + cd "$work_dir" || { + echo -e "${ERROR} 无法进入临时目录 ${work_dir}!" + return 1 + } + + if [[ "${OS_TYPE}" == "CentOS" ]]; then + local lot_ver="4.11.2-1" # CentOS 7 默认 + [[ "${OS_VERSION_ID}" == "6" ]] && lot_ver="2.6.32-504" + + local base_url="${GITHUB_RAW_URL}/lotserver/centos/${OS_VERSION_ID}/x64" + + # 先把 4 个包全部下载并校验成功,再动系统 (避免删了旧包却没装上新包) + local lot_pkgs=( + "kernel-firmware-${lot_ver}.rpm|kernel-firmware.rpm" + "kernel-${lot_ver}.rpm|kernel.rpm" + "kernel-headers-${lot_ver}.rpm|kernel-headers.rpm" + "kernel-devel-${lot_ver}.rpm|kernel-devel.rpm" + ) + local item + for item in "${lot_pkgs[@]}"; do + local remote="${item%%|*}" + local local_f="${item##*|}" + if ! safe_wget "${base_url}/${remote}" "$local_f"; then + echo -e "${ERROR} Lotserver 组件 ${remote} 下载失败,已中止 (系统未做任何更改)。" + return 1 + fi + done + + rpm --import "${GITHUB_RAW_URL}/lotserver/centos/RPM-GPG-KEY-elrepo.org" >/dev/null 2>&1 + remove_old_headers + yum remove -y kernel-firmware kernel-headers >/dev/null 2>&1 + + echo -e "${INFO} 正在安装 Lotserver 专属内核组件..." + # 显式列出文件名,避免下载失败时把字面量 *.rpm 传给 yum + if ! yum install -y ./kernel-firmware.rpm ./kernel.rpm ./kernel-headers.rpm ./kernel-devel.rpm; then + echo -e "${ERROR} Lotserver 内核安装失败!" + return 1 + fi + + elif [[ "${OS_TYPE}" == "Debian" ]]; then + # Debian/Ubuntu 走老旧的 snapshot.debian.org 源 + local base_deb="" img_deb="" + if [[ "$OS_ID" == "debian" && "$OS_VERSION_ID" == "8" ]]; then + base_deb="http://snapshot.debian.org/archive/debian/20120304T220938Z/pool/main/l/linux-base/linux-base_3.5_all.deb" + img_deb="http://snapshot.debian.org/archive/debian/20171008T163152Z/pool/main/l/linux/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb" + elif [[ "$OS_ID" == "debian" && "$OS_VERSION_ID" == "9" ]]; then + base_deb="http://snapshot.debian.org/archive/debian/20160917T042239Z/pool/main/l/linux-base/linux-base_4.5_all.deb" + img_deb="http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb" + else + echo -e "${ERROR} Lotserver 不支持当前系统版本!" + return 1 + fi + + if ! safe_wget "$base_deb" "linux-base.deb" || ! safe_wget "$img_deb" "linux-image.deb"; then + echo -e "${ERROR} Lotserver 内核组件下载失败,已中止 (系统未做任何更改)。" + return 1 + fi + + apt-get autoremove -y >/dev/null 2>&1 + remove_old_headers + + dpkg-query -W -f='${Status}' linux-base 2>/dev/null | grep -q "install ok installed" || dpkg -i linux-base.deb + if ! apt-get install -y --allow-downgrades ./linux-image.deb; then + echo -e "${ERROR} Lotserver 内核安装失败!" + return 1 + fi + fi + + echo -e "${INFO} Lotserver 内核包安装完成,正在更新系统引导..." + BBR_grub +} + +# 询问本机用途,决定"场景相关"参数(内核转发/合包/conntrack)。其余基座参数三种场景通用。 +# 交互提示走 stderr,仅最终结果(web/proxy/forward)走 stdout,便于 $(...) 捕获。 +# 10 秒无输入或直接回车 → 默认 proxy(机场代理)。 +ask_workload_profile() { + local ans + echo -e "${INFO} 请选择本机用途 (影响 合包/conntrack 等场景参数;内核转发默认全开):" >&2 + echo -e " [1] 网站 / 通用 (Nginx/PHP/DB,主要入站短连接)" >&2 + echo -e " [2] 代理 / 转发组网 (SS/Xray/Trojan/Hysteria/WireGuard/tun) <-- 默认" >&2 + read -t 10 -rp " 输入 1/2 (10 秒无输入或回车默认 [2] 代理): " ans + echo >&2 + case "$ans" in + 1) echo "web" ;; + *) echo "proxy" ;; + esac +} + +# ================================================= +# 系统级网络与资源自适应优化 (替换旧版优化) +# ================================================= +optimizing_system() { + # 与 tcpfit 共存时让路:tcpfit 会按实测 BDP/内存推导整套 sysctl,并刻意省略 + # 某些参数 (如 tcp_notsent_lowat)。本函数整文件覆盖会与其重叠、甚至塞回它故意 + # 不设的项,且因 99-tcpfit.conf 加载在后而多半失效。故先警告并要求显式确认。 + if tcpfit_present; then + echo -e "${TIP} 检测到 tcpfit 正在管理网络 sysctl (/etc/sysctl.d/99-tcpfit.conf)。" + echo -e "${TIP} tcpx 的整体优化会与其重叠,重叠键最终以 tcpfit 为准,且可能引入" + echo -e " tcpfit 有意省略的参数。建议改用菜单 [60] 调起 tcpfit 精调。" + read -rp "仍要继续执行 tcpx 的系统优化吗?(请输入大写 YES 继续): " _go + if [[ "$_go" != "YES" ]]; then + echo -e "${INFO} 已跳过,网络精调交由 tcpfit 负责。" + return 0 + fi + fi + + echo -e "${INFO} 开始进行系统级网络优化 (自适应 CPU/内存/内核版本)..." + + # 1. 动态获取系统硬件与内核参数 + local total_mem_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo) + local total_mem_mb=$((total_mem_kb / 1024)) + local cpu_cores=$(nproc) + local kernel_major=$(uname -r | cut -d. -f1) + local kernel_minor=$(uname -r | cut -d. -f2) + + # 新增:动态获取当前正在使用的拥塞控制算法,防止覆盖 LotSpeed 或其它自定义算法 + local current_cc=$(cat /proc/sys/net/ipv4/tcp_congestion_control 2>/dev/null || echo "bbr") + local current_qdisc=$(cat /proc/sys/net/core/default_qdisc 2>/dev/null || echo "fq") + [[ "$current_cc" == "unknown" || -z "$current_cc" ]] && current_cc="bbr" + [[ "$current_qdisc" == "unknown" || -z "$current_qdisc" ]] && current_qdisc="fq" + + # 同理继承当前 IPv6 开关状态。 + # 否则用户先执行 [35] 禁用 IPv6、再执行 [32] 网络优化时, + # 本函数会无条件写回 disable_ipv6 = 0,把 IPv6 静默重新打开。 + local current_disable_ipv6=$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null || echo "0") + [[ "$current_disable_ipv6" != "1" ]] && current_disable_ipv6="0" + if [[ "$current_disable_ipv6" == "1" ]]; then + echo -e "${TIP} 检测到当前已禁用 IPv6,本次优化将保持该状态 (如需开启请用菜单 [36])。" + fi + + # 1.5 询问本机用途,决定场景相关参数(合包/conntrack),其余基座参数通用。 + # 内核转发按用户要求默认全开,不再随用途区分。 + local workload autocork tune_conntrack + workload=$(ask_workload_profile) + case "$workload" in + web) + autocork=1 + tune_conntrack=0 + echo -e "${INFO} 用途=网站/通用:开启合包(autocorking 提升小响应效率)。" + ;; + *) + autocork=0 + tune_conntrack=1 + echo -e "${INFO} 用途=代理/转发:关闭合包(低延迟)、抬高 conntrack 上限。" + ;; + esac + + # 2. 根据内存大小动态适配 (socket 缓冲上限 / 并发连接 / 文件描述符) + # 注意: sock_buf_max 只是"每 socket 自动调优的上限",不是默认值。默认值必须 + # 保持很小 (见下方 rmem_default/wmem_default),否则每条连接都按上限预留内存, + # 高并发下极易 OOM —— 这是旧版最严重的负优化 (8G 机器默认缓冲写到了 64MB)。 + local sock_buf_max somaxconn file_max syn_backlog + if [ "$total_mem_mb" -ge 8192 ]; then + # 8GB 及以上高配机器: 每 socket 上限 64MB (足够覆盖高 BDP 链路,且不虚高) + sock_buf_max=67108864 + somaxconn=1048576 + file_max=2097152 + syn_backlog=65535 + elif [ "$total_mem_mb" -ge 2048 ]; then + # 2GB - 8GB 中等配置: 32MB + sock_buf_max=33554432 + somaxconn=65535 + file_max=1048576 + syn_backlog=32768 + else + # 2GB 以下小内存机器: 16MB + sock_buf_max=16777216 + somaxconn=32768 + file_max=524288 + syn_backlog=16384 + fi + + # 3. 根据 CPU 核心数动态适配网卡队列与积压 + local netdev_max_backlog=$((10000 * cpu_cores)) + [[ $netdev_max_backlog -lt 32768 ]] && netdev_max_backlog=32768 + [[ $netdev_max_backlog -gt 100000 ]] && netdev_max_backlog=100000 + + local netdev_budget=$((300 + 20 * cpu_cores)) + [[ $netdev_budget -gt 50000 ]] && netdev_budget=50000 + + # 4. 生成统一的 sysctl 配置文件 + local sysctl_conf="/etc/sysctl.d/99-sysctl.conf" + + # 备份并清空原文件(比几十行 sed -i 速度快且更安全) + [[ -f "$sysctl_conf" ]] && cp "$sysctl_conf" "${sysctl_conf}.bak" + cat /dev/null >"$sysctl_conf" + + # 写入基础通用优化 (兼容 CentOS 7-9, Debian 9-12, Ubuntu 18-24) + cat >>"$sysctl_conf" </dev/null 2>&1 + echo "nf_conntrack" >/etc/modules-load.d/tcpx-conntrack.conf + # 只加大 max 不加大 hashsize(桶数) 会让哈希链变长、查表变慢。 + # hashsize 经验取 max/4,用 modprobe 选项持久化 (重启加载模块时生效),并对当前已加载模块即时生效。 + echo "options nf_conntrack hashsize=${ct_hashsize}" >/etc/modprobe.d/tcpx-conntrack.conf + [[ -w /sys/module/nf_conntrack/parameters/hashsize ]] && + echo "$ct_hashsize" >/sys/module/nf_conntrack/parameters/hashsize 2>/dev/null + cat >>"$sysctl_conf" <>"$sysctl_conf" + fi + + # 移除低版本废弃参数: net.ipv4.tcp_fack (在内核 4.11 中已废弃,合并到了通用重传逻辑中) + if [[ "$kernel_major" -lt 4 || ("$kernel_major" -eq 4 && "$kernel_minor" -lt 11) ]]; then + echo "net.ipv4.tcp_fack = 1" >>"$sysctl_conf" + fi + + # 6. 系统资源限制极限优化 (systemd 与 limits.conf) + echo -e "${INFO} 正在根据内存大小自动优化系统文件描述符限制..." + + # 优化 Systemd 配置 + if [[ -d "/etc/systemd" ]]; then + # 整文件覆盖前先备份用户原有配置。仅在 .bak 不存在时备份, + # 避免重复运行本函数时用"已被我们改写过的版本"覆盖掉最初的原始文件。 + [[ -f /etc/systemd/system.conf && ! -f /etc/systemd/system.conf.bak ]] && + cp /etc/systemd/system.conf /etc/systemd/system.conf.bak + cat >/etc/systemd/system.conf </dev/null 2>&1 + fi + + # 优化 limits.conf (同样先备份原始文件,仅首次备份) + [[ -f /etc/security/limits.conf && ! -f /etc/security/limits.conf.bak ]] && + cp /etc/security/limits.conf /etc/security/limits.conf.bak + cat >/etc/security/limits.conf <>/etc/profile + + # 修复 Pam 会话限制 + if [[ -f "/etc/pam.d/common-session" ]] && ! grep -q "pam_limits.so" /etc/pam.d/common-session; then + echo "session required pam_limits.so" >>/etc/pam.d/common-session + fi + + # 7. 应用内核与系统参数 + echo -e "${INFO} 正在应用自适应内核配置..." + # sysctl --system 会按序加载 /etc/sysctl.d/ 下全部文件(含本文件), + # 前面单独的 sysctl -p "$sysctl_conf" 属于重复执行,已移除。 + sysctl --system >/dev/null 2>&1 + + # 透明大页设为 madvise: 仅对显式 madvise(MADV_HUGEPAGE) 的程序启用大页。 + # 相比 always,可避免 Redis/数据库等延迟敏感服务因大页整理(khugepaged) + # 产生的卡顿与内存放大;同时不影响真正需要大页的程序主动申请。 + if [[ -f /sys/kernel/mm/transparent_hugepage/enabled ]]; then + echo madvise >/sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null + # 上面的 echo 仅本次运行生效,重启回默认。用一个 oneshot 服务在开机时重设。 + cat >/etc/systemd/system/tcpx-thp.service <<'EOF' +[Unit] +Description=Set Transparent HugePage to madvise (tcpx) +After=local-fs.target + +[Service] +Type=oneshot +ExecStart=/bin/sh -c 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload >/dev/null 2>&1 + systemctl enable tcpx-thp.service >/dev/null 2>&1 + fi + + echo -e "${INFO} 系统网络与资源限制自适应优化完成!(建议完成后重启服务器以全面生效)" +} + +# ================================================= +# 网络加速统一切换引擎 (替代原来十几个 startxxx 函数) +# ================================================= + +# 检测本机是否已被 tcpfit "应用"(真正在管理网络参数),而非仅仅"装了 tcpfit 这个工具"。 +# 关键区别: /usr/local/bin/tcpfit 只代表命令行工具已安装,用户可能从未 apply 过; +# 此时不应弹出"tcpfit 正在管理本机"的警告与 YES 确认。已应用的证据是: +# - /etc/sysctl.d/99-tcpfit.conf : apply 时写入的 sysctl (最确定的已应用标志) +# - tcpfit-qdisc.service : apply 出向整形时创建的 systemd 单元 +# - /var/lib/tcpfit : 运行后产生的状态目录 +# 故此处刻意不再检测裸二进制文件,避免"只装未用"被误判为正在管理。 +tcpfit_present() { + [[ -f /etc/sysctl.d/99-tcpfit.conf ]] && return 0 + [[ -d /var/lib/tcpfit ]] && return 0 + systemctl list-unit-files 2>/dev/null | grep -q '^tcpfit-qdisc\.service' && return 0 + return 1 +} + +# 返回 cc/qdisc/ecn 应写入的 sysctl 文件。 +# sysctl --system 按文件名字典序加载,后者覆盖前者:99-sysctl.conf < 99-tcpfit.conf, +# 故 tcpfit 存在时若仍写 99-sysctl.conf,用户在菜单里选的算法会被 tcpfit 的 bbr 覆盖。 +# 改写到 99-zz-tcpx-accel.conf (排序在 99-tcpfit.conf 之后),让用户的显式选择生效。 +readonly TCPX_ACCEL_DROPIN="/etc/sysctl.d/99-zz-tcpx-accel.conf" +# LotSpeed 开机自启服务:保证重启后 lotspeed 模块被加载且算法被重新应用 (详见 setup_lotspeed_boot_service) +readonly LOTSPEED_BOOT_SERVICE="/etc/systemd/system/lotspeed-boot.service" +accel_conf_path() { + if tcpfit_present; then + echo "$TCPX_ACCEL_DROPIN" + else + echo "/etc/sysctl.d/99-sysctl.conf" + fi +} + +# tcpfit 的出向整形是否正在运行。它对网卡出向限速/整形,会盖过任何拥塞算法 +# (LotSpeed / BBR / 锐速) 的实际吞吐——换算法不会自动解除它。 +tcpfit_shaper_active() { + systemctl is-active tcpfit-qdisc.service >/dev/null 2>&1 +} + +# 开启加速前,若检测到 tcpfit 整形在跑,询问用户是否停用,避免"换了算法却跑不满"。 +maybe_offer_disable_tcpfit_shaper() { + tcpfit_shaper_active || return 0 + echo -e "${TIP} 检测到 tcpfit 出向整形 (tcpfit-qdisc.service) 正在运行。" + echo -e "${TIP} 它会对网卡出向限速/整形,从而限制本次加速算法的实际吞吐。" + read -rp "是否停用 tcpfit 整形以便加速全速生效?(y=停用 / 回车=保留): " _shaper_ans + if [[ "$_shaper_ans" =~ ^[yY]$ ]]; then + systemctl disable --now tcpfit-qdisc.service >/dev/null 2>&1 + echo -e "${INFO} 已停用 tcpfit 出向整形 (恢复: systemctl enable --now tcpfit-qdisc.service,或重新运行 tcpfit)。" + else + echo -e "${INFO} 已保留 tcpfit 整形,加速算法吞吐仍会受其限制。" + fi +} + +# 启用 LotSpeed 时无条件停用 tcpfit 出向整形 (按用户要求不再询问)。 +# tcpfit-qdisc.service 对网卡出向限速/整形,会直接盖过 LotSpeed 的实际吞吐。 +disable_tcpfit_shaper_force() { + tcpfit_shaper_active || return 0 + systemctl disable --now tcpfit-qdisc.service >/dev/null 2>&1 + echo -e "${INFO} 已自动停用 tcpfit 出向整形 (tcpfit-qdisc.service),以便 LotSpeed 全速生效。" + echo -e "${TIP} 如需恢复: systemctl enable --now tcpfit-qdisc.service,或重新运行 tcpfit。" +} + +# 卸载加速器 (清理配置) +remove_bbr_lotserver() { + echo -e "${INFO} 正在清理旧的拥塞控制与队列算法配置..." + # 三处都清: 主文件、传统 /etc/sysctl.conf、以及 tcpfit 共存时用的高优先级 drop-in, + # 避免切换算法后残留旧行 (drop-in 排序最靠后,残留会覆盖新选择)。 + local f + for f in /etc/sysctl.d/99-sysctl.conf /etc/sysctl.conf "$TCPX_ACCEL_DROPIN"; do + # tcp_ecn 用 [[:space:]]*= 收尾锚定,避免顺带删掉 net.ipv4.tcp_ecn_fallback + [[ -f "$f" ]] && sed -i '/net.core.default_qdisc/d; /net.ipv4.tcp_congestion_control/d; /net\.ipv4\.tcp_ecn[[:space:]]*=/d' "$f" + done + + sysctl --system >/dev/null 2>&1 + + # 停用 LotSpeed 开机自启服务:切换到 BBR/锐速 等其它算法时若不取消它, + # 重启后 lotspeed-boot.service 会重新加载模块并把算法顶回 lotspeed, + # 造成"切了却又变回来"的幽灵问题。切到 LotSpeed 的路径会在其后重新启用本服务。 + systemctl disable --now lotspeed-boot.service >/dev/null 2>&1 + + # 修改:停用并卸载 LotSpeed 模块 (但不物理删除文件,以便随时通过菜单快速切换) + if command -v lotspeed >/dev/null 2>&1; then + lotspeed stop >/dev/null 2>&1 + rmmod lotspeed >/dev/null 2>&1 + fi + # 如果没有 helper 脚本但也加载了模块的兜底清理 + if lsmod | grep -q "lotspeed"; then + rmmod lotspeed >/dev/null 2>&1 + fi + + if [[ -e /appex/bin/lotServer.sh ]]; then + local tmp_lot + tmp_lot=$(mktemp /tmp/lotserver.XXXXXX) + if fetch_remote_script "https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh" "$tmp_lot" >/dev/null 2>&1; then + echo | bash "$tmp_lot" uninstall >/dev/null 2>&1 + fi + rm -f "$tmp_lot" + fi +} + +# 确保 qdisc 对应的 sch_ 模块已加载并在重启后自动加载。 +# 拥塞算法(tcp_congestion_control)写入时内核会按需自动 request_module, +# 但 default_qdisc 不会:cake/fq_pie 等模块若未加载,写 default_qdisc 会静默失败。 +ensure_qdisc_module() { + local q="$1" + [[ -z "$q" ]] && return 0 + modprobe "sch_${q}" >/dev/null 2>&1 + echo "sch_${q}" >/etc/modules-load.d/tcpx-qdisc.conf +} + +# 统一加速开启函数 +# 用法: enable_acceleration <队列算法> <拥塞控制算法> +enable_acceleration() { + local qdisc="$1" + local cc="$2" + + # remove_bbr_lotserver 会连同 tcp_ecn 行一并删除。先记住当前 ECN 状态, + # 切换算法后按原样写回,避免用户在菜单 [30] 开启的 ECN 被静默清掉。 + local prev_ecn + prev_ecn=$(cat /proc/sys/net/ipv4/tcp_ecn 2>/dev/null) + [[ ! "$prev_ecn" =~ ^[0-2]$ ]] && prev_ecn="" + + remove_bbr_lotserver + maybe_offer_disable_tcpfit_shaper + + echo -e "${INFO} 正在应用: ${cc} + ${qdisc} ..." + ensure_qdisc_module "$qdisc" + local sysctl_conf + sysctl_conf=$(accel_conf_path) + if [[ "$sysctl_conf" == "$TCPX_ACCEL_DROPIN" ]]; then + echo -e "${TIP} 检测到 tcpfit,已将本次选择写入高优先级文件 ${sysctl_conf}" + echo -e "${TIP} 以覆盖 tcpfit 默认的 bbr/fq (仅覆盖 cc/qdisc,其余精调仍归 tcpfit)。" + fi + echo "net.core.default_qdisc=$qdisc" >>"$sysctl_conf" + echo "net.ipv4.tcp_congestion_control=$cc" >>"$sysctl_conf" + # 保留切换前的 ECN 设置 + [[ -n "$prev_ecn" ]] && echo "net.ipv4.tcp_ecn=$prev_ecn" >>"$sysctl_conf" + + sysctl --system >/dev/null 2>&1 + echo -e "${INFO} 加速算法修改成功!如果未立即生效,请重启服务器。" +} + +# 启用 Lotserver +startlotserver() { + remove_bbr_lotserver + maybe_offer_disable_tcpfit_shaper + if [[ "${OS_TYPE}" == "CentOS" ]]; then + yum install ethtool -y + else + apt-get update && apt-get install ethtool -y + fi + + local tmp_lot + tmp_lot=$(mktemp /tmp/lotserver.XXXXXX) || return 1 + if ! fetch_remote_script "https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh" "$tmp_lot"; then + rm -f "$tmp_lot" + echo -e "${ERROR} lotServer 安装脚本下载失败!" + return 1 + fi + echo | bash "$tmp_lot" install + rm -f "$tmp_lot" + + if [[ ! -f /appex/etc/config ]]; then + echo -e "${ERROR} lotServer 安装失败,未生成配置文件 /appex/etc/config!" + return 1 + fi + sed -i '/advinacc/d; /maxmode/d' /appex/etc/config + echo -e "advinacc=\"1\"\nmaxmode=\"1\"" >>/appex/etc/config + /appex/bin/lotServer.sh restart +} + +# 开启/关闭 ECN (显式控制) +set_ecn() { + local status="$1" + local sysctl_conf + sysctl_conf=$(accel_conf_path) + # 从所有可能位置清掉旧 ecn 行,再写入到当前生效优先级最高的文件 + # 收尾锚定 [[:space:]]*=,只删 tcp_ecn 本身,不误伤 tcp_ecn_fallback + sed -i '/net\.ipv4\.tcp_ecn[[:space:]]*=/d' /etc/sysctl.d/99-sysctl.conf /etc/sysctl.conf "$TCPX_ACCEL_DROPIN" 2>/dev/null + echo "net.ipv4.tcp_ecn=$status" >>"$sysctl_conf" + sysctl --system >/dev/null 2>&1 + [[ "$status" == "1" ]] && echo -e "${INFO} ECN 已开启!" || echo -e "${INFO} ECN 已关闭!" +} + +# 彻底卸载全部加速与优化 (抛弃几十个 sed 删除,直接清空文件) +remove_all() { + echo -e "${INFO} 正在清空网络优化与系统限制..." + rm -f /etc/sysctl.d/99-sysctl.conf + rm -f "$TCPX_ACCEL_DROPIN" + cat /dev/null >/etc/sysctl.conf + sysctl --system >/dev/null 2>&1 + + # 逐个文件加存在性守卫:如 /etc/pam.d/common-session 在 CentOS 上并不存在, + # 无守卫的 sed -i 会打印 "No such file" 噪声错误。 + [[ -f /etc/systemd/system.conf ]] && sed -i '/DefaultTimeoutStopSec/d; /DefaultLimitCORE/d; /DefaultLimitNOFILE/d; /DefaultLimitNPROC/d' /etc/systemd/system.conf + [[ -f /etc/security/limits.conf ]] && sed -i '/soft nofile/d; /hard nofile/d; /soft nproc/d; /hard nproc/d; /soft core/d; /hard core/d' /etc/security/limits.conf + [[ -f /etc/profile ]] && sed -i '/ulimit -SHn/d' /etc/profile + [[ -f /etc/pam.d/common-session ]] && sed -i '/required pam_limits.so/d' /etc/pam.d/common-session + + systemctl daemon-reload + remove_bbr_lotserver + # 新增:彻底卸载时,物理清理 LotSpeed 残留文件 + rm -f /usr/local/bin/lotspeed + rm -rf /opt/lotspeed + # remove_bbr_lotserver 已 disable --now 了开机服务,这里再物理删除 unit 文件并重载 + rm -f "$LOTSPEED_BOOT_SERVICE" + + # 清理系统优化持久化产物: THP 服务、qdisc/conntrack 模块自加载与 modprobe 选项 + systemctl disable --now tcpx-thp.service >/dev/null 2>&1 + rm -f /etc/systemd/system/tcpx-thp.service + rm -f /etc/modules-load.d/tcpx-qdisc.conf + rm -f /etc/modules-load.d/tcpx-conntrack.conf + rm -f /etc/modprobe.d/tcpx-conntrack.conf + systemctl daemon-reload >/dev/null 2>&1 + # tcpfit 是独立工具,其 sysctl/整形/systemd 单元不归本脚本管理,须单独回滚 + if tcpfit_present; then + echo -e "${TIP} 检测到 tcpfit 仍在管理本机网络参数 (99-tcpfit.conf / tcpfit-qdisc.service 等)。" + echo -e "${TIP} tcpx 不会替它卸载,如需彻底还原请另外执行: tcpfit rollback" + fi + echo -e "${INFO} 系统已恢复原生状态。" +} + +# ================================================= +# 系统引导与内核管理引擎 +# ================================================= + +# 现代化更新引导 (GRUB) +BBR_grub() { + echo -e "${INFO} 正在更新系统引导..." + if [[ "${OS_TYPE}" == "CentOS" ]]; then + if command -v grubby >/dev/null 2>&1; then + # grubby 在 BLS (RHEL/CentOS 8-10) 和传统 GRUB2 下均可用。 + # 用 gsub 去掉 RHEL 9 输出中可能带的引号,再按版本号排序取最新内核, + # 避免依赖 index 顺序 (index=0 是当前默认,不一定是版本最新的)。 + local latest_kernel + latest_kernel=$(grubby --info=ALL | + awk -F= '/^kernel/{gsub(/"/,"",$2); print $2}' | + sort -V | tail -n 1) + if [[ -n "$latest_kernel" ]]; then + grubby --set-default="$latest_kernel" >/dev/null 2>&1 + echo -e "${INFO} 默认启动内核已更新为: ${latest_kernel##*/}" + else + echo -e "${TIP} grubby 未找到内核条目,回退到 grub2-mkconfig..." + _bbr_grub2_mkconfig + fi + else + _bbr_grub2_mkconfig + fi + elif [[ "${OS_TYPE}" == "Debian" ]]; then + # systemd-boot: deb 包的 postinst 钩子 (kernel-install) 已自动更新引导条目, + # 此处只需将 systemd-boot 加载器二进制本身更新到最新即可,无需操作 GRUB。 + if command -v bootctl >/dev/null 2>&1 && bootctl is-installed 2>/dev/null; then + echo -e "${INFO} 检测到 systemd-boot,引导条目已由包管理器钩子自动处理。" + bootctl update >/dev/null 2>&1 || true + elif command -v update-grub >/dev/null 2>&1; then + update-grub >/dev/null 2>&1 + else + apt-get install -y grub2-common >/dev/null 2>&1 + update-grub >/dev/null 2>&1 + fi + fi +} + +# grub2-mkconfig 回退辅助函数 (UEFI 优先,再 BIOS legacy) +_bbr_grub2_mkconfig() { + local grub_cfg="" + # UEFI: /boot/efi/EFI//grub.cfg + if [[ -d /boot/efi/EFI ]]; then + grub_cfg=$(find /boot/efi/EFI -maxdepth 2 -name grub.cfg 2>/dev/null | head -n 1) + fi + # BIOS legacy fallback + [[ -z "$grub_cfg" && -f /boot/grub2/grub.cfg ]] && grub_cfg="/boot/grub2/grub.cfg" + if [[ -n "$grub_cfg" ]]; then + grub2-mkconfig -o "$grub_cfg" >/dev/null 2>&1 + fi + grub2-set-default 0 +} + +# 查看已安装的内核与排序 +show_kernels() { + clear + echo -e "${INFO} ===================================================" + echo -e "${INFO} 当前系统中已安装的内核包:" + if [[ "${OS_TYPE}" == "CentOS" ]]; then + rpm -qa | grep -E "^kernel(-ml|-lt|-uek|-rt|-plus)?(-image|-core|-modules|-devel|-headers)?-[0-9]" | sort -V + echo -e "${INFO} ===================================================" + echo -e "${INFO} GRUB 引导项 (通常 index=0 为默认启动项):" + grubby --info=ALL | grep -E "^kernel|^index" + elif [[ "${OS_TYPE}" == "Debian" ]]; then + dpkg -l | grep -E "^ii (linux-(image|headers|modules)|pve-kernel|proxmox-kernel|proxmox-headers|raspberrypi-kernel)" | awk '{print $2, $3}' | column -t | sort -V + echo -e "${INFO} ===================================================" + echo -e "${INFO} /boot 目录下的内核镜像:" + ls -1v /boot/vmlinuz-* 2>/dev/null + fi + echo -e "${INFO} ===================================================" + echo -e "${TIP} 当前实际正在运行的内核: ${GREEN_FONT_PREFIX}$(uname -r)${FONT_COLOR_SUFFIX}" + echo "" + return 0 +} + +# 高级交互式内核管理 (精准多选删除,支持删除当前内核) +delete_kernel_custom() { + clear + echo -e "${INFO} ===================================================" + echo -e "${INFO} 正在扫描系统中已安装的内核包..." + local current_kernel=$(uname -r) + local kernel_list=() + + # 使用更精准的包查询方式,防止名字过长被截断 + if [[ "${OS_TYPE}" == "CentOS" ]]; then + mapfile -t kernel_list < <(rpm -qa | grep -E "^kernel(-ml|-lt|-uek|-rt|-plus)?(-image|-core|-modules|-devel|-headers)?-[0-9]" | sort -V) + elif [[ "${OS_TYPE}" == "Debian" ]]; then + mapfile -t kernel_list < <(dpkg-query -W -f='${Package}\n' | grep -E "^(linux-(image|headers|modules)|pve-kernel|proxmox-kernel|proxmox-headers|raspberrypi-kernel)" | sort -V) + fi + + if [[ ${#kernel_list[@]} -eq 0 ]]; then + echo -e "${ERROR} 未检测到可管理的内核包。" + return + fi + + echo -e "${TIP} 当前正在运行的内核: ${GREEN_FONT_PREFIX}${current_kernel}${FONT_COLOR_SUFFIX}" + echo -e "${INFO} ===================================================" + + # 打印带编号的内核列表 + for i in "${!kernel_list[@]}"; do + local pkg="${kernel_list[$i]}" + if [[ "$pkg" == *"$current_kernel"* ]]; then + echo -e " ${GREEN_FONT_PREFIX}[$i] ${pkg} [*当前运行中*]${FONT_COLOR_SUFFIX}" + else + echo -e " [$i] ${pkg}" + fi + done + echo -e "${INFO} ===================================================" + echo -e "${TIP} 提示: 排序后默认从最高版本内核启动!" + echo "" + read -p "请输入要【删除】的内核编号 (多选请用空格分隔,例如 '0 2 3',直接回车取消): " del_choices + + if [[ -z "$del_choices" ]]; then + echo -e "${INFO} 已取消操作,返回主菜单。" + return + fi + + # 遍历用户输入,提取包名 + local pkgs_to_del="" + local is_del_current=0 + for idx in $del_choices; do + if [[ "$idx" =~ ^[0-9]+$ ]] && [[ "$idx" -ge 0 ]] && [[ "$idx" -lt ${#kernel_list[@]} ]]; then + local selected_pkg="${kernel_list[$idx]}" + pkgs_to_del="$pkgs_to_del $selected_pkg" + # 标记是否包含当前内核 + if [[ "$selected_pkg" == *"$current_kernel"* ]]; then + is_del_current=1 + fi + else + echo -e "${TIP} 无效的编号: $idx,已忽略。" + fi + done + + if [[ -z "$pkgs_to_del" ]]; then + echo -e "${INFO} 没有选择有效的内核,操作结束。" + return + fi + + # 安全屏障:确保删除后至少还剩一个可引导内核镜像,防止变砖 + local remaining_images=0 + for pkg in "${kernel_list[@]}"; do + # 仅统计镜像包 (跳过 headers / modules / devel) + if [[ "${OS_TYPE}" == "Debian" ]]; then + [[ "$pkg" != linux-image-* ]] && continue + else + # CentOS: kernel(-ml|-lt|…)-<版本号> 格式,排除 headers/devel + [[ ! "$pkg" =~ ^kernel(-ml|-lt|-uek|-rt|-plus)?-[0-9] ]] && continue + fi + # 该镜像不在待删除列表中 → 会被保留 + # 用赋值式自增而非 ((remaining_images++)):后者在结果为 0 时返回退出码 1, + # 将来若启用 set -e 会在此静默中断循环。 + [[ " $pkgs_to_del " != *" $pkg "* ]] && remaining_images=$((remaining_images + 1)) + done + if [[ $remaining_images -eq 0 ]]; then + echo -e "${ERROR} 操作已阻止!删除所选包后系统将无任何可引导内核,重启即变砖!" + echo -e "${TIP} 请至少保留一个可引导内核镜像包。" + return 1 + fi + + echo -e "${TIP} 即将从系统中彻底卸载以下内核包:" + echo -e "${RED_FONT_PREFIX}${pkgs_to_del}${FONT_COLOR_SUFFIX}" + + # 强力警告与二次确认机制 + if [[ $is_del_current -eq 1 ]]; then + echo -e "" + echo -e "${ERROR} 高危警告!您选择了删除【当前正在运行的内核】!" + echo -e "${TIP} 卸载当前运行中的内核,可能会导致您的 SSH 连接中断。" + echo -e "${TIP} 请务必确保系统中还有【至少一个其他已正常安装的内核】,否则重启后机器将变砖失联!" + read -p "您确定要继续删除选中的内核包吗?(请输入大写的 YES 确认): " confirm_danger + if [[ "$confirm_danger" != "YES" ]]; then + echo -e "${INFO} 操作已取消,出于安全考虑未执行删除。" + return + fi + else + read -p "请确认是否卸载?(Y/n): " confirm + if [[ "$confirm" =~ ^[nN]$ ]]; then + echo -e "${INFO} 操作已取消。" + return + fi + fi + + echo -e "${INFO} 正在执行卸载,如果遇到断开连接请不要惊慌,稍等几分钟后尝试重启服务器..." + if [[ "${OS_TYPE}" == "CentOS" ]]; then + rpm -e --nodeps $pkgs_to_del + elif [[ "${OS_TYPE}" == "Debian" ]]; then + apt-get purge -y $pkgs_to_del + apt-get autoremove -y >/dev/null 2>&1 + fi + + BBR_grub + echo -e "${INFO} 指定内核卸载完毕!引导项已自动更新。" +} + +# 编译安装 brutal +startbrutal() { + # headers_status 由 check_status 填充。在此处主动刷新, + # 避免依赖菜单循环是否已调用过 check_status 的不确定状态。 + check_status + if [[ "$headers_status" == "已匹配" ]]; then + echo -e "${INFO} Headers 已匹配,开始编译 Brutal..." + # 统一走 fetch_remote_script: 落盘 + 校验 (非空 & 是 shell 脚本) 后再执行, + # 避免 curl | bash 把 404/劫持页直接喂给 bash 以 root 运行。 + local tmp_brutal + tmp_brutal=$(mktemp /tmp/brutal.XXXXXX) || return 1 + if ! fetch_remote_script "https://tcp.hy2.sh/" "$tmp_brutal"; then + rm -f "$tmp_brutal" + echo -e "${ERROR} Brutal 安装脚本下载失败!" + return 1 + fi + bash "$tmp_brutal" + rm -f "$tmp_brutal" + if lsmod | grep -q "brutal"; then + echo -e "${INFO} Brutal 模块已成功加载!" + else + echo -e "${ERROR} Brutal 模块未加载,编译可能失败,请查看上方日志。" + fi + else + echo -e "${ERROR} 当前内核 Headers 不匹配或未安装,无法编译 Brutal。" + echo -e "${TIP} 请先安装对应版本的 Headers (通常随内核包一并安装,也可重新执行安装菜单)。" + fi +} + +# 创建并启用 LotSpeed 开机自启服务。 +# 开机时 systemd-sysctl 加载 sysctl.d 时 lotspeed 模块可能尚未 modprobe, +# 那行 tcp_congestion_control=lotspeed 会静默失败并退回 tcpfit 的 bbr。 +# 故用一个 oneshot 服务:先 lotspeed start 加载模块,再 sysctl --system 重刷, +# 让 99-zz-tcpx-accel.conf 里的 lotspeed 生效 (排序在 99-tcpfit.conf 之后,稳压其默认)。 +setup_lotspeed_boot_service() { + local lotspeed_bin sysctl_bin + lotspeed_bin=$(command -v lotspeed 2>/dev/null || echo "/usr/local/bin/lotspeed") + sysctl_bin=$(command -v sysctl 2>/dev/null || echo "/sbin/sysctl") + cat >"$LOTSPEED_BOOT_SERVICE" </dev/null 2>&1 + if systemctl enable --now lotspeed-boot.service >/dev/null 2>&1; then + echo -e "${INFO} 已启用 LotSpeed 开机自启 (lotspeed-boot.service),重启后仍为默认算法。" + else + echo -e "${TIP} LotSpeed 开机自启服务启用失败 (非 systemd 环境?),重启后可能需手动 lotspeed start。" + fi +} + +# 安装启用 LotSpeed (uk0开发) +install_lotspeed() { + echo -e "${INFO} 准备安装并启用 LotSpeed (ml-tcp 分支) ..." + # 执行官方一键安装脚本 (走 safe_wget 以便国内机可通过镜像获取) + local tmp_ls + tmp_ls=$(mktemp /tmp/lotspeed.XXXXXX) || return 1 + if ! fetch_remote_script "https://raw.githubusercontent.com/uk0/lotspeed/ml-tcp/install.sh" "$tmp_ls"; then + rm -f "$tmp_ls" + echo -e "${ERROR} LotSpeed 安装脚本下载失败!" + return 1 + fi + bash "$tmp_ls" + rm -f "$tmp_ls" + + if lsmod | grep -q "lotspeed"; then + echo -e "${INFO} LotSpeed 模块已成功加载!" + # 将其写入生效优先级最高的文件确保重启后也是默认算法 + local sysctl_conf + sysctl_conf=$(accel_conf_path) + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.d/99-sysctl.conf /etc/sysctl.conf "$TCPX_ACCEL_DROPIN" 2>/dev/null + echo "net.ipv4.tcp_congestion_control=lotspeed" >>"$sysctl_conf" + sysctl --system >/dev/null 2>&1 + echo -e "${INFO} LotSpeed 已设置为默认拥塞控制算法!" + setup_lotspeed_boot_service + disable_tcpfit_shaper_force + else + echo -e "${ERROR} LotSpeed 模块加载失败,请检查上方编译日志(通常是因为内核 Headers 缺失或版本过低)。" + fi +} + +# 单独启用 LotSpeed 加速 (免编译快速切换) +enable_lotspeed_standalone() { + if ! command -v lotspeed >/dev/null 2>&1; then + echo -e "${ERROR} 未检测到 LotSpeed,请先执行菜单 [26] 进行编译安装!" + sleep 3 + return + fi + remove_bbr_lotserver + disable_tcpfit_shaper_force + echo -e "${INFO} 正在启动 LotSpeed 加速..." + lotspeed start >/dev/null 2>&1 + + # 确保将其写死为默认启动项 + local sysctl_conf + sysctl_conf=$(accel_conf_path) + ensure_qdisc_module "fq" + sed -i '/net.ipv4.tcp_congestion_control/d; /net.core.default_qdisc/d' /etc/sysctl.d/99-sysctl.conf /etc/sysctl.conf "$TCPX_ACCEL_DROPIN" 2>/dev/null + echo "net.core.default_qdisc=fq" >>"$sysctl_conf" + echo "net.ipv4.tcp_congestion_control=lotspeed" >>"$sysctl_conf" + sysctl --system >/dev/null 2>&1 + + # remove_bbr_lotserver 已先禁用旧的开机服务,此处按当前 LotSpeed 状态重新启用 + setup_lotspeed_boot_service + + echo -e "${INFO} LotSpeed 加速已成功切换并启用!" +} + +# ================================================= +# 杂项与附加功能模块 (补齐缺失的函数) +# ================================================= + +# 通用的远程脚本获取器: 下载到临时文件 -> 校验 -> 交给调用方处理 +# 统一走 safe_wget,国内机才能借助镜像拿到 raw.githubusercontent.com 上的内容 +fetch_remote_script() { + local url="$1" + local dest="$2" + + safe_wget "$url" "$dest" || return 1 + + # 校验:非空 + 是 shell 脚本 (防止把 404 页面/运营商劫持页当脚本执行) + if [[ ! -s "$dest" ]]; then + echo -e "${ERROR} 下载到的文件为空!" + return 1 + fi + if ! head -n 1 "$dest" | grep -qE '^#!.*(bash|sh)'; then + echo -e "${ERROR} 下载到的内容不是有效的 Shell 脚本 (可能是错误页或被劫持)!" + return 1 + fi + return 0 +} + +# 首次运行自安装:把脚本落地到 /usr/local/bin/tcpx,之后可直接输入 tcpx 运行。 +# 兼容两种运行方式: +# 1) 本地文件 (bash tcpx.sh / ./tcpx.sh) -> 直接复制自身 +# 2) 管道/进程替换 (bash <(curl -fsSL )) -> 本地无副本,需从 TCPX_SELF_URL 重新下载 +install_self() { + local target="$TCPX_INSTALL_PATH" + local src + src=$(readlink -f "${BASH_SOURCE[0]:-$0}" 2>/dev/null || echo "${BASH_SOURCE[0]:-$0}") + + # 正在运行的就是安装后的副本,无需再装 + [[ "$src" == "$target" ]] && return 0 + + # 目标目录不可写(极少数只读环境)则跳过,不阻断正常使用 + [[ -w "$(dirname "$target")" ]] || return 0 + + if [[ -f "$src" && -r "$src" ]]; then + # 本地文件方式:内容有变化才复制,避免每次运行都写盘 + if [[ ! -f "$target" ]] || ! cmp -s "$src" "$target"; then + if cp "$src" "$target" 2>/dev/null && chmod +x "$target" 2>/dev/null; then + echo -e "${INFO} 已安装/更新到 ${target},之后可直接输入 ${GREEN_FONT_PREFIX}tcpx${FONT_COLOR_SUFFIX} 运行。" + fi + fi + return 0 + fi + + # 管道/进程替换方式:本地没有文件副本,必须从权威地址重新下载 + if [[ -n "$TCPX_SELF_URL" ]]; then + local tmp + tmp=$(mktemp /tmp/tcpx_self.XXXXXX) || return 0 + if fetch_remote_script "$TCPX_SELF_URL" "$tmp" >/dev/null 2>&1; then + if mv -f "$tmp" "$target" 2>/dev/null && chmod +x "$target" 2>/dev/null; then + echo -e "${INFO} 已安装到 ${target},之后可直接输入 ${GREEN_FONT_PREFIX}tcpx${FONT_COLOR_SUFFIX} 运行。" + fi + else + rm -f "$tmp" + echo -e "${TIP} 自安装下载失败(地址不可达或非脚本内容),本次仍可正常使用菜单。" + fi + else + echo -e "${TIP} 检测到以管道方式运行,但未配置下载地址,无法自安装到 ${target}。" + echo -e "${TIP} 可用 ${GREEN_FONT_PREFIX}TCPX_URL=<地址> bash <(curl -fsSL <地址>)${FONT_COLOR_SUFFIX} 重新运行以启用自安装。" + fi + return 0 +} + +Update_Shell() { + echo -e "${INFO} 正在更新脚本..." + + # 关键: 绝不能直接 wget -O 覆盖正在运行的脚本文件。 + # bash 是边读边执行的,覆写自身会导致后续语句从错误的字节偏移继续解析。 + # 正确做法是先下到临时文件、校验通过后再 mv 覆盖,最后 exec 重启。 + local tmp_sh + tmp_sh=$(mktemp /tmp/tcpx_update.XXXXXX) || { + echo -e "${ERROR} 无法创建临时文件!" + return 1 + } + + if ! fetch_remote_script "${GITHUB_RAW_URL}/tcpx.sh" "$tmp_sh"; then + rm -f "$tmp_sh" + echo -e "${ERROR} 脚本更新失败,已保留当前版本 (未做任何更改)。" + return 1 + fi + + # 解析目标位置:优先覆盖当前脚本自身 + local self_path + self_path=$(readlink -f "$0" 2>/dev/null || echo "$0") + if [[ ! -w "$(dirname "$self_path")" ]]; then + echo -e "${TIP} 当前脚本所在目录不可写,将安装到 /root/tcpx.sh" + self_path="/root/tcpx.sh" + fi + + local new_ver=$(grep -m1 '^readonly SH_VER=' "$tmp_sh" | cut -d'"' -f2) + if [[ -n "$new_ver" ]]; then + if [[ "$new_ver" == "$SH_VER" ]]; then + echo -e "${INFO} 当前已是最新版本 [v${SH_VER}],无需更新。" + rm -f "$tmp_sh" + return 0 + fi + echo -e "${INFO} 发现新版本: ${GREEN_FONT_PREFIX}v${new_ver}${FONT_COLOR_SUFFIX} (当前 v${SH_VER})" + fi + + mv -f "$tmp_sh" "$self_path" || { + rm -f "$tmp_sh" + echo -e "${ERROR} 写入 ${self_path} 失败!" + return 1 + } + chmod +x "$self_path" + + # 同步安装/刷新到 /usr/local/bin/tcpx,保证升级后可直接输入 tcpx 运行, + # 不再依赖"旧版本升级后是否会重新执行并触发 install_self"这一不确定链路。 + local reload_path="$self_path" + if [[ "$self_path" != "$TCPX_INSTALL_PATH" && -w "$(dirname "$TCPX_INSTALL_PATH")" ]]; then + if cp -f "$self_path" "$TCPX_INSTALL_PATH" 2>/dev/null && chmod +x "$TCPX_INSTALL_PATH" 2>/dev/null; then + echo -e "${INFO} 已同步安装到 ${GREEN_FONT_PREFIX}${TCPX_INSTALL_PATH}${FONT_COLOR_SUFFIX},之后可直接输入 ${GREEN_FONT_PREFIX}tcpx${FONT_COLOR_SUFFIX} 运行。" + # 从安装后的规范路径重载,后续升级即针对 /usr/local/bin/tcpx 生效 + reload_path="$TCPX_INSTALL_PATH" + fi + fi + + echo -e "${INFO} 更新完成,正在重新载入脚本..." + exec bash "$reload_path" +} + +gotodd() { + echo -e "${INFO} 正在切换到一键 DD 系统脚本..." + local tmp_sh + tmp_sh=$(mktemp /tmp/reinstall.XXXXXX) || return 1 + + if ! fetch_remote_script "https://raw.githubusercontent.com/bin456789/reinstall/main/reinstall.sh" "$tmp_sh"; then + rm -f "$tmp_sh" + echo -e "${ERROR} reinstall.sh 下载失败!" + return 1 + fi + bash "$tmp_sh" + rm -f "$tmp_sh" +} + +closeipv6() { + echo -e "${INFO} 正在禁用 IPv6..." + sed -i '/net.ipv6.conf.all.disable_ipv6/d; /net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf /etc/sysctl.conf 2>/dev/null + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv6.conf.default.disable_ipv6 = 1" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system >/dev/null 2>&1 + echo -e "${INFO} IPv6 已成功禁用!" +} + +openipv6() { + echo -e "${INFO} 正在开启 IPv6..." + sed -i '/net.ipv6.conf.all.disable_ipv6/d; /net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf /etc/sysctl.conf 2>/dev/null + echo "net.ipv6.conf.all.disable_ipv6 = 0" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv6.conf.default.disable_ipv6 = 0" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system >/dev/null 2>&1 + echo -e "${INFO} IPv6 已成功开启!" +} + +optimizing_ddcc() { + echo -e "${INFO} 正在应用防 CC/DDOS 轻量优化..." + local sysctl_conf="/etc/sysctl.d/99-sysctl.conf" + sed -i '/net.ipv4.tcp_syncookies/d; /net.ipv4.tcp_max_syn_backlog/d; /net.ipv4.tcp_synack_retries/d' "$sysctl_conf" 2>/dev/null + cat >>"$sysctl_conf" </dev/null 2>&1 + echo -e "${INFO} 防 CC 基础参数已写入并生效!" +} + +# 调起 tcpfit 进行网络精调 (自适应 BDP/内存的独立工具) +# 按要求:菜单 [60] 直接从上游一键运行 tcpfit,不做任何检测或提示。 +# 注意: 此处走原生 curl 直连,未使用脚本内的镜像/落盘校验逻辑; +# 国内网络若无法直连 raw.githubusercontent.com 可能失败。 +run_tcpfit_tune() { + echo -e "${INFO} 正在调起 tcpfit 进行网络精调..." + bash <(curl -fsSL https://raw.githubusercontent.com/Kylin010/tcpfit/main/tcpfit.sh) +} + +# ================================================= +# UI 面板与主逻辑 +# ================================================= + +# 获取系统面板信息 +get_system_info() { + opsy="${OS_TYPE} ${OS_VERSION_ID}" + arch="${OS_ARCH}" + kern=$(uname -r) + + # 获取虚拟化类型 + if command -v virt-what >/dev/null 2>&1; then + virtual=$(virt-what | head -n 1) + elif command -v systemd-detect-virt >/dev/null 2>&1; then + virtual=$(systemd-detect-virt) + else + virtual="Unknown" + fi + [[ -z "$virtual" ]] && virtual="Dedicated" +} + +# 开始菜单 +# 绘制菜单与状态面板 (不含读取输入,便于循环复用) +show_menu_panel() { + clear + echo && echo -e " TCP加速 一键安装管理脚本 ${RED_FONT_PREFIX}[v${SH_VER}] 不卸内核${FONT_COLOR_SUFFIX} from blog.ylx.me 母鸡慎用 + ${GREEN_FONT_PREFIX}0.${FONT_COLOR_SUFFIX} 升级脚本 + ———————————————————————————— 内核安装 ————————————————————————————— + ${GREEN_FONT_PREFIX}1.${FONT_COLOR_SUFFIX} 安装 BBR自编编译内核 ${GREEN_FONT_PREFIX}7.${FONT_COLOR_SUFFIX} 安装 官方稳定内核 + ${GREEN_FONT_PREFIX}2.${FONT_COLOR_SUFFIX} 安装 BBRplus版内核 ${GREEN_FONT_PREFIX}8.${FONT_COLOR_SUFFIX} 安装 官方最新内核 + ${GREEN_FONT_PREFIX}3.${FONT_COLOR_SUFFIX} 安装 Lotserver(锐速)内核 ${GREEN_FONT_PREFIX}9.${FONT_COLOR_SUFFIX} 安装 XANMOD(main) + ${GREEN_FONT_PREFIX}4.${FONT_COLOR_SUFFIX} 安装 官方cloud内核 ${GREEN_FONT_PREFIX}10.${FONT_COLOR_SUFFIX} 安装 XANMOD(LTS) + ${GREEN_FONT_PREFIX}5.${FONT_COLOR_SUFFIX} 安装 BBRplus新版内核 ${GREEN_FONT_PREFIX}11.${FONT_COLOR_SUFFIX} 安装 XANMOD(EDGE) + ${GREEN_FONT_PREFIX}6.${FONT_COLOR_SUFFIX} 安装 Zen官方版内核 ${GREEN_FONT_PREFIX}12.${FONT_COLOR_SUFFIX} 安装 XANMOD(RT) + ———————————————————————————— 加速启用 ————————————————————————————— + ${GREEN_FONT_PREFIX}20.${FONT_COLOR_SUFFIX} 使用BBR+FQ加速 ${GREEN_FONT_PREFIX}21.${FONT_COLOR_SUFFIX} 使用BBR+FQ_PIE加速 + ${GREEN_FONT_PREFIX}22.${FONT_COLOR_SUFFIX} 使用BBR+CAKE加速 ${GREEN_FONT_PREFIX}23.${FONT_COLOR_SUFFIX} 使用BBRplus+FQ版加速 + ${GREEN_FONT_PREFIX}24.${FONT_COLOR_SUFFIX} 使用Lotserver(锐速)加速 ${GREEN_FONT_PREFIX}25.${FONT_COLOR_SUFFIX} 编译安装brutal模块 + ${GREEN_FONT_PREFIX}26.${FONT_COLOR_SUFFIX} 编译安装LotSpeed模块 ${GREEN_FONT_PREFIX}27.${FONT_COLOR_SUFFIX} 使用LotSpeed加速 + ———————————————————————————— 系统配置 ————————————————————————————— + ${GREEN_FONT_PREFIX}30.${FONT_COLOR_SUFFIX} 开启ECN ${GREEN_FONT_PREFIX}31.${FONT_COLOR_SUFFIX} 关闭ECN + ${GREEN_FONT_PREFIX}32.${FONT_COLOR_SUFFIX} 系统网络自适应优化 ${GREEN_FONT_PREFIX}33.${FONT_COLOR_SUFFIX} 防CC/DDOS轻量优化 + ${GREEN_FONT_PREFIX}35.${FONT_COLOR_SUFFIX} 禁用IPv6 ${GREEN_FONT_PREFIX}36.${FONT_COLOR_SUFFIX} 开启IPv6 + ${GREEN_FONT_PREFIX}37.${FONT_COLOR_SUFFIX} 手动提交合并内核参数 ${GREEN_FONT_PREFIX}38.${FONT_COLOR_SUFFIX} 手动编辑内核参数 + ———————————————————————————— 内核管理 ————————————————————————————— + ${GREEN_FONT_PREFIX}51.${FONT_COLOR_SUFFIX} 查看排序内核 ${GREEN_FONT_PREFIX}52.${FONT_COLOR_SUFFIX} 删除保留指定内核 + ${GREEN_FONT_PREFIX}55.${FONT_COLOR_SUFFIX} 卸载全部加速 ${GREEN_FONT_PREFIX}99.${FONT_COLOR_SUFFIX} 退出脚本 + ———————————————————————————— 其它工具 ————————————————————————————— + ${GREEN_FONT_PREFIX}60.${FONT_COLOR_SUFFIX} 网络精调(tcpfit联动) ${GREEN_FONT_PREFIX}92.${FONT_COLOR_SUFFIX} 一键DD重装系统 +————————————————————————————————————————————————————————————————" + check_status + get_system_info + echo -e " 信息: ${FONT_COLOR_SUFFIX}$opsy ${GREEN_FONT_PREFIX}$virtual${FONT_COLOR_SUFFIX} $arch ${GREEN_FONT_PREFIX}$kern${FONT_COLOR_SUFFIX} " + if [[ ${kernel_status} == "noinstall" ]]; then + echo -e " 状态: ${GREEN_FONT_PREFIX}未安装${FONT_COLOR_SUFFIX} 加速内核 ${RED_FONT_PREFIX}请先安装内核${FONT_COLOR_SUFFIX}" + else + echo -e " 状态: ${GREEN_FONT_PREFIX}已安装${FONT_COLOR_SUFFIX} ${RED_FONT_PREFIX}${kernel_status}${FONT_COLOR_SUFFIX} 加速内核 , ${GREEN_FONT_PREFIX}${run_status}${FONT_COLOR_SUFFIX} ${RED_FONT_PREFIX}${brutal}${FONT_COLOR_SUFFIX} ${RED_FONT_PREFIX}${lotspeed_status}${FONT_COLOR_SUFFIX}" + fi + echo -e " 拥塞控制算法: ${GREEN_FONT_PREFIX}${net_congestion_control}${FONT_COLOR_SUFFIX} 队列算法: ${GREEN_FONT_PREFIX}${net_qdisc}${FONT_COLOR_SUFFIX} Headers状态:${GREEN_FONT_PREFIX}${headers_status}${FONT_COLOR_SUFFIX}" + if tcpfit_present; then + echo -e " ${YELLOW_FONT_PREFIX}检测到 tcpfit 正在管理网络参数${FONT_COLOR_SUFFIX}: 菜单 [60] 可调起 tcpfit,[32] 会让路,切算法自动抢优先级" + fi +} + +# 开始菜单 (改为循环驱动) +# 原实现里各功能函数末尾靠递归调用 start_menu 回到菜单,存在两个问题: +# 1) 递归会不断累积调用栈,且大多数分支根本没有递归,执行完就直接退出脚本; +# 2) 安装函数里的 exit 会连带杀掉整个交互式脚本。 +# 现统一由本循环驱动,功能函数只需 return。 +start_menu() { + while true; do + show_menu_panel + + # 用 read 的返回值捕获 EOF (Ctrl-D),否则会陷入空输入死循环 + if ! read -rp " 请输入数字 :" num; then + echo "" + echo -e "${INFO} 已退出。" + exit 0 + fi + + case "$num" in + 0) Update_Shell ;; + 1) installbbr ;; + 2) installbbrplus ;; + 3) installlot ;; + 4) installcloud ;; + 5) installbbrplusnew ;; + 6) check_sys_official_zen ;; + 7) check_sys_official ;; + 8) check_sys_official_bbr ;; + 9) check_sys_official_xanmod_main ;; + 10) check_sys_official_xanmod_lts ;; + 11) check_sys_official_xanmod_edge ;; + 12) check_sys_official_xanmod_rt ;; + 20) enable_acceleration "fq" "bbr" ;; + 21) enable_acceleration "fq_pie" "bbr" ;; + 22) enable_acceleration "cake" "bbr" ;; + 23) enable_acceleration "fq" "bbrplus" ;; + 24) startlotserver ;; + 25) startbrutal ;; + 26) install_lotspeed ;; + 27) enable_lotspeed_standalone ;; + 30) set_ecn "1" ;; + 31) set_ecn "0" ;; + 32) optimizing_system ;; + 33) optimizing_ddcc ;; + 35) closeipv6 ;; + 36) openipv6 ;; + 37) update_sysctl_interactive ;; + 38) edit_sysctl_interactive ;; + 51) show_kernels ;; + 52) delete_kernel_custom ;; + 55) remove_all ;; + 60) run_tcpfit_tune ;; + 92) gotodd ;; + 99) + echo -e "${INFO} 已退出。" + exit 0 + ;; + *) + echo -e "${ERROR}: 请输入正确数字" + ;; + esac + + # 统一在此暂停,让用户看清上面命令的输出后再刷新菜单 + echo "" + read -rp "按回车键返回主菜单..." _ || exit 0 + done +} + +#----------------------------------------------------------------------- +# 函数: update_sysctl_interactive (V4 - 增加错误忽略参数) +# 功能: 以交互方式安全地更新 sysctl 配置文件并应用。 +# 命令执行失败时,将不会回滚文件更改。 +#----------------------------------------------------------------------- +update_sysctl_interactive() { + # 强制使用C语言环境,确保正则表达式的行为可预测且一致。 + # 注意: 必须 export,否则 sed/grep/sysctl 等子进程根本看不到这个变量, + # 仅 local 声明等于完全没生效。 + local LC_ALL=C + export LC_ALL + + # --- 配置与参数解析 --- + local CONF_FILE="/etc/sysctl.d/99-sysctl.conf" + local TMP_FILE + local BACKUP_FILE + local ignore_apply_error=true + + # --- 帮助函数 (仅本函数内部使用,退出前会 unset 以免污染全局命名空间) --- + log_info() { + echo "[INFO] $1" + } + + log_error() { + echo "[ERROR] $1" >&2 + } + + log_warn() { + echo "[WARN] $1" >&2 + } + + # 函数返回时清理这三个临时函数定义 + trap 'unset -f log_info log_error log_warn 2>/dev/null' RETURN + + # --- 主逻辑 --- + + # 1. 权限检查 + if [[ $EUID -ne 0 ]]; then + log_error "此函数必须以 root 权限运行,请使用 sudo。" + return 1 + fi + + # 2. 交互式获取用户输入 + log_info "请输入或粘贴您要设置的 sysctl 参数 (格式: key = value)。" + log_info "可参考TCP迷之调参,https://omnitt.com/" + log_info "注释行(以 # 或 ; 开头)和空行将被忽略。" + log_info "最后一行请以空行结束 可手动回车加一行空行" + log_info "输入完成后,请按 Ctrl+D 结束输入。" + + readarray -t user_input + + if [ ${#user_input[@]} -eq 0 ]; then + log_info "没有接收到任何输入,操作已取消。" + return 0 + fi + + # 确保配置文件存在 + touch "$CONF_FILE" + + # 3. 创建临时文件 + TMP_FILE=$(mktemp) || { + log_error "无法创建临时文件" + return 1 + } + # 覆盖上面的 RETURN trap,追加临时文件清理 (bash 每个信号只保留一个 trap) + trap 'rm -f "$TMP_FILE"; unset -f log_info log_error log_warn 2>/dev/null' RETURN + + cp "$CONF_FILE" "$TMP_FILE" + + local -A params_to_add + local all_params_valid=true + + # 4. 预处理所有输入,检查合法性 + log_info "正在校验所有输入参数..." + for line in "${user_input[@]}"; do + trimmed_line=$(echo "$line" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + if [[ -z "$trimmed_line" ]] || [[ "$trimmed_line" =~ ^[[:space:]]*[#\;] ]]; then + continue + fi + + if ! [[ "$trimmed_line" =~ ^[[:space:]]*([a-zA-Z0-9._-]+)[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$ ]]; then + log_error "格式无效: '$trimmed_line'. 期望格式为 'key = value'." + all_params_valid=false + continue + fi + + local key="${BASH_REMATCH[1]}" + local value="${BASH_REMATCH[2]}" + + if ! sysctl -N "$key" >/dev/null 2>&1; then + log_error "参数键名无效: '$key' 不是一个有效的内核参数。" + all_params_valid=false + continue + fi + + local formatted_param="$key = $value" + + if grep -q -E "^[[:space:]]*${key//./\\.}([[:space:]]*)=.*" "$TMP_FILE"; then + sed -i -E "s|^[[:space:]]*${key//./\\.}([[:space:]]*)=.*|$formatted_param|" "$TMP_FILE" + log_info "已更新参数: $formatted_param" + else + if [[ -z "${params_to_add[$key]}" ]]; then + params_to_add["$key"]="$formatted_param" + fi + fi + done + + if ! $all_params_valid; then + log_error "检测到无效参数,操作已中止。配置文件未做任何更改。" + return 1 + fi + + # 5. 将所有新参数追加到临时文件末尾 + if [ ${#params_to_add[@]} -gt 0 ]; then + log_info "正在添加新参数..." + echo "" >>"$TMP_FILE" + for key in "${!params_to_add[@]}"; do + echo "${params_to_add[$key]}" >>"$TMP_FILE" + log_info "已添加新参数: ${params_to_add[$key]}" + done + fi + + # 6. 原子替换与应用 + BACKUP_FILE="${CONF_FILE}.bak_$(date +%Y%m%d_%H%M%S)" + cp "$CONF_FILE" "$BACKUP_FILE" + log_info "原始文件已备份到 $BACKUP_FILE" + + mv "$TMP_FILE" "$CONF_FILE" + chown root:root "$CONF_FILE" + chmod 644 "$CONF_FILE" + # TMP_FILE 已被 mv 走,无需再清理,但仍需在返回时收回临时函数定义 + trap 'unset -f log_info log_error log_warn 2>/dev/null' RETURN + + # 7. 应用配置并进行错误处理 + log_info "正在应用新的 sysctl 设置..." + # 与脚本其余部分统一使用 sysctl --system,按 /etc/sysctl.d/ 的优先级顺序 + # 加载全部配置文件,得到与重启后一致的最终合并结果。 + if apply_output=$(sysctl --system 2>&1); then + log_info "Sysctl 设置已成功应用。" + echo "--- 应用输出 ---" + echo "$apply_output" + echo "------------------" + rm -f "$BACKUP_FILE" + else + # 应用失败时的逻辑 + if [[ "$ignore_apply_error" == "true" ]]; then + log_warn "应用 sysctl 设置失败,但根据指令已忽略错误。" + log_warn "配置文件 '${CONF_FILE}' 已被更新,但部分设置可能未生效。" + log_warn "--- 错误详情 ---" + echo "$apply_output" >&2 + echo "------------------" + rm -f "$BACKUP_FILE" # 忽略错误,所以也删除备份 + return 0 # 返回成功状态 + else + log_error "应用 sysctl 设置失败!正在回滚..." + log_error "--- 错误详情 ---" + echo "$apply_output" + echo "------------------" + + mv "$BACKUP_FILE" "$CONF_FILE" + log_info "正在恢复到之前的设置..." + sysctl --system >/dev/null 2>&1 + + log_error "回滚完成。配置文件已恢复,问题备份文件保留在 $BACKUP_FILE" + return 1 + fi + fi + + return 0 +} + +edit_sysctl_interactive() { + local target_file="/etc/sysctl.d/99-sysctl.conf" + local editor_cmd="" + + # --- 1. 检查文件是否存在 --- + if [[ ! -f "$target_file" ]]; then + echo -e "${TIP} 文件 $target_file 不存在。" + read -r -p "是否立即创建并编辑? (Y/n): " create_choice + case "$create_choice" in + [nN]) + echo -e "${INFO} 操作已取消。" + return 0 + ;; + *) ;; + esac + fi + + # --- 2. 按优先级选择编辑器: $VISUAL → $EDITOR → nano → vi --- + # 脚本要求 root 运行,此处无需 sudo。 + if [[ -n "${VISUAL:-}" ]] && command -v "$VISUAL" >/dev/null 2>&1; then + editor_cmd="$VISUAL" + elif [[ -n "${EDITOR:-}" ]] && command -v "$EDITOR" >/dev/null 2>&1; then + editor_cmd="$EDITOR" + elif command -v nano >/dev/null 2>&1; then + editor_cmd="nano" + elif command -v vi >/dev/null 2>&1; then + editor_cmd="vi" + echo -e "${TIP} 将使用 vi 编辑器。按 'i' 进入插入模式,'Esc' 退出插入模式,':wq' 保存,':q!' 放弃。" + else + echo -e "${ERROR} 未找到可用文本编辑器 (nano / vi),请先安装:" + if [[ "${OS_TYPE}" == "CentOS" ]]; then + echo -e " yum install -y nano" + else + echo -e " apt-get install -y nano" + fi + return 1 + fi + + # --- 3. 执行编辑 --- + echo -e "${INFO} 使用 [${editor_cmd}] 打开 ${target_file}..." + if ! "$editor_cmd" "$target_file"; then + echo -e "${ERROR} 编辑器启动失败或异常退出。" + return 1 + fi + + # --- 4. 应用新配置 --- + echo "" + echo -e "${INFO} 正在应用新配置..." + # sysctl --system 加载 /etc/sysctl.d/ 全部文件,比 sysctl -p 单文件更完整 + sysctl --system >/dev/null 2>&1 + echo -e "${INFO} 内核参数已重载。部分参数需重启后才能生效。" +} + +# ================================================= +# 官方源内核安装模块 (包含 CentOS 10 战未来支持) +# ================================================= + +#检查官方稳定内核并安装 +check_sys_official() { + pre_install_check || return 1 + if [[ "${OS_TYPE}" == "CentOS" ]]; then + [[ "${OS_ARCH}" != "x86_64" ]] && { + echo -e "${ERROR} 不支持x86_64以外的系统 !" + return 1 + } + if [[ "${OS_VERSION_ID}" == "7" ]]; then + yum install kernel kernel-headers -y --skip-broken + elif [[ "${OS_VERSION_ID}" == "8" || "${OS_VERSION_ID}" == "9" || "${OS_VERSION_ID}" == "10" ]]; then + # CentOS 8、9、10 都是同样的包结构 + yum install kernel kernel-core kernel-headers -y --skip-broken + else + echo -e "${ERROR} 不支持当前系统 CentOS ${OS_VERSION_ID} !" && return 1 + fi + elif [[ "${OS_TYPE}" == "Debian" ]]; then + apt update + if [[ "${OS_ID}" == "ubuntu" || "${OS_ID}" == "pop" || "${OS_ID_LIKE}" == *"ubuntu"* ]]; then + # Ubuntu 及衍生版 (如 Mint) 使用 generic 命名 + apt-get install linux-image-generic linux-headers-generic -y + else + # Debian 及衍生版 (如 Kali, Armbian) 使用 amd64/arm64 命名 + if [[ "${OS_ARCH}" == "x86_64" ]]; then + apt-get install linux-image-amd64 linux-headers-amd64 -y + elif [[ "${OS_ARCH}" == "aarch64" ]]; then + apt-get install linux-image-arm64 linux-headers-arm64 -y + fi + fi + fi + BBR_grub + echo -e "${TIP} 内核安装完毕。" +} + +#检查官方最新内核并安装 (ELRepo / Backports / HWE) +check_sys_official_bbr() { + pre_install_check || return 1 + if [[ "${OS_TYPE}" == "CentOS" ]]; then + [[ "${OS_ARCH}" != "x86_64" ]] && { + echo -e "${ERROR} 不支持x86_64以外的系统 !" + return 1 + } + rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org + if [[ "${OS_VERSION_ID}" == "7" ]]; then + yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm -y + yum --enablerepo=elrepo-kernel install kernel-ml kernel-ml-headers -y --skip-broken + elif [[ "${OS_VERSION_ID}" == "8" ]]; then + yum install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm -y + yum --enablerepo=elrepo-kernel install kernel-ml kernel-ml-headers -y --skip-broken + elif [[ "${OS_VERSION_ID}" == "9" ]]; then + yum install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm -y + yum --enablerepo=elrepo-kernel install kernel-ml kernel-ml-headers -y --skip-broken + elif [[ "${OS_VERSION_ID}" == "10" ]]; then + yum install https://www.elrepo.org/elrepo-release-10.el10.elrepo.noarch.rpm -y + yum --enablerepo=elrepo-kernel install kernel-ml kernel-ml-headers -y --skip-broken + else + echo -e "${ERROR} 不支持当前系统 CentOS ${OS_VERSION_ID} !" && return 1 + fi + elif [[ "${OS_TYPE}" == "Debian" ]]; then + apt update + if [[ "${OS_ID}" == "ubuntu" || "${OS_ID}" == "pop" || "${OS_ID_LIKE}" == *"ubuntu"* ]]; then + # Ubuntu 及衍生版安装官方最新内核 (HWE - 硬件使能内核) + echo -e "${INFO} 正在为 Ubuntu/衍生系 获取官方最新 HWE 内核..." + if apt-cache show linux-generic-hwe-${OS_VERSION_ID} >/dev/null 2>&1; then + apt-get install --install-recommends linux-generic-hwe-${OS_VERSION_ID} -y + else + echo -e "${TIP} 当前版本 (${OS_VERSION_ID}) 无专属 HWE 包,将自动为您更新至常规最新 generic 内核..." + apt-get install linux-image-generic linux-headers-generic -y + fi + else + # Debian 及衍生版 (如 Kali, Deepin) + local apt_args="" + # 仅为纯净 Debian 添加 Backports 源,避免搞坏 Kali 的滚动源 + if [[ "${OS_ID}" == "debian" ]]; then + # 原生读取 os-release,彻底摆脱 lsb_release 依赖 + local codename=$(awk -F= '/^VERSION_CODENAME/{print $2}' /etc/os-release | tr -d '"') + [[ -z "$codename" ]] && codename=$(awk -F= '/^VERSION=/{print $2}' /etc/os-release | grep -oP '(?<=\().*(?=\))') + + [[ -z "$codename" ]] && { + echo -e "${ERROR} 无法获取 Debian 代号" + return 1 + } + echo "deb http://deb.debian.org/debian ${codename}-backports main" >/etc/apt/sources.list.d/${codename}-backports.list + apt update + apt_args="-t ${codename}-backports" + else + echo -e "${TIP} 检测到 ${OS_ID} (非原生 Debian),跳过添加 Backports 源,直接从默认源安装最新内核..." + fi + + if [[ "${OS_ARCH}" == "x86_64" ]]; then + apt $apt_args install linux-image-amd64 linux-headers-amd64 -y + elif [[ "${OS_ARCH}" =~ ^(arm|aarch64)$ ]]; then + apt $apt_args install linux-image-arm64 linux-headers-arm64 -y + fi + fi + fi + BBR_grub + echo -e "${TIP} 内核安装完毕。" +} + +# 统一 Xanmod 安装引擎 +install_xanmod_generic() { + local edition="$1" # main, lts, edge, rt + pre_install_check || return 1 + [[ "${OS_ARCH}" != "x86_64" ]] && { + echo -e "${ERROR} Xanmod 仅支持 x86_64 !" + return 1 + } + [[ "${OS_TYPE}" != "Debian" ]] && { + echo -e "${ERROR} 当前一键 Xanmod 仅支持 Debian/Ubuntu !" + return 1 + } + + apt update + apt-get install gnupg gnupg2 gnupg1 wget -y + + # 清除可能存在的旧版或重复源 (兼容 PR 提到的 .sources 与冲突问题) + rm -f /etc/apt/sources.list.d/xanmod-kernel.list + rm -f /etc/apt/sources.list.d/xanmod-release.list + rm -f /etc/apt/sources.list.d/xanmod-kernel.sources + sed -i '/deb.xanmod.org/d' /etc/apt/sources.list 2>/dev/null + + # 使用现代化的 signed-by 格式写入 GPG 密钥与源,彻底消除 apt 警告 + wget -qO - https://dl.xanmod.org/gpg.key | gpg --dearmor --yes -o /usr/share/keyrings/xanmod-archive-keyring.gpg + echo 'deb [signed-by=/usr/share/keyrings/xanmod-archive-keyring.gpg] http://deb.xanmod.org releases main' | tee /etc/apt/sources.list.d/xanmod-kernel.list + + # 在临时目录下载并运行 CPU 支持等级检测脚本,避免污染调用方 CWD + local xanmod_work + xanmod_work=$(mktemp -d /tmp/xanmod_install.XXXXXX) || { + echo -e "${ERROR} 无法创建临时目录!" + return 1 + } + trap 'cd /tmp; rm -rf "$xanmod_work"; trap - RETURN' RETURN + cd "$xanmod_work" || { + echo -e "${ERROR} 无法进入临时目录!" + return 1 + } + + local cpu_level="1" + if wget -qO check_x86-64_psabi.sh https://dl.xanmod.org/check_x86-64_psabi.sh 2>/dev/null && + [[ -s check_x86-64_psabi.sh ]]; then + chmod +x check_x86-64_psabi.sh + # 输出示例 "Your CPU supports x86-64-v3";提取最后出现的 v1-v4 数字 + cpu_level=$(./check_x86-64_psabi.sh 2>/dev/null | + grep -oE '\bv[1-4]\b' | tail -n 1 | tr -d 'v') + [[ -z "$cpu_level" ]] && cpu_level="1" + else + echo -e "${TIP} CPU 等级检测脚本下载失败,将使用 v1 (最兼容) 版本。" + fi + echo -e "${INFO} CPU 支持等级: \033[32mv${cpu_level}\033[0m" + + apt update + local pkg_name="linux-xanmod" + [[ "$edition" != "main" ]] && pkg_name="linux-xanmod-${edition}" + + if [[ "$cpu_level" -ge 3 ]]; then + apt install "${pkg_name}-x64v3" -y + elif [[ "$cpu_level" -ge 2 ]]; then + apt install "${pkg_name}-x64v2" -y + else + apt install "${pkg_name}-x64v1" -y + fi + + BBR_grub + echo -e "${TIP} 内核安装完毕。" +} + +check_sys_official_xanmod_main() { install_xanmod_generic "main"; } +check_sys_official_xanmod_lts() { install_xanmod_generic "lts"; } +check_sys_official_xanmod_edge() { install_xanmod_generic "edge"; } +check_sys_official_xanmod_rt() { install_xanmod_generic "rt"; } + +#检查Zen官方内核并安装 +check_sys_official_zen() { + pre_install_check || return 1 + [[ "${OS_ARCH}" != "x86_64" ]] && { + echo -e "${ERROR} Zen内核仅支持x86_64 !" + return 1 + } + if [[ "${OS_ID}" == "debian" || "${OS_ID}" == "kali" || "${OS_ID_LIKE}" == *"debian"* ]] && [[ "${OS_ID_LIKE}" != *"ubuntu"* && "${OS_ID}" != "ubuntu" && "${OS_ID}" != "pop" ]]; then + # 落盘校验后再执行 Liquorix 加源脚本,避免 curl | bash + local tmp_lqx + tmp_lqx=$(mktemp /tmp/liquorix.XXXXXX) || return 1 + if ! fetch_remote_script "https://liquorix.net/add-liquorix-repo.sh" "$tmp_lqx"; then + rm -f "$tmp_lqx" + echo -e "${ERROR} Liquorix 源脚本下载失败!" + return 1 + fi + bash "$tmp_lqx" + rm -f "$tmp_lqx" + apt-get install linux-image-liquorix-amd64 linux-headers-liquorix-amd64 -y + elif [[ "${OS_ID}" == "ubuntu" || "${OS_ID}" == "pop" || "${OS_ID_LIKE}" == *"ubuntu"* ]]; then + apt-get install software-properties-common -y + add-apt-repository ppa:damentz/liquorix -y && apt-get update + apt-get install linux-image-liquorix-amd64 linux-headers-liquorix-amd64 -y + else + echo -e "${ERROR} Zen内核当前脚本仅支持 Debian/Ubuntu 及衍生版 !" && return 1 + fi + BBR_grub + echo -e "${TIP} 内核安装完毕。" +} + +#检查系统当前状态 +check_status() { + # 初始化变量,避免重复读取文件 + kernel_version=$(uname -r | awk -F "-" '{print $1}') + kernel_version_full=$(uname -r) + net_congestion_control=$(cat /proc/sys/net/ipv4/tcp_congestion_control 2>/dev/null || echo "unknown") + net_qdisc=$(cat /proc/sys/net/core/default_qdisc 2>/dev/null || echo "unknown") + + # 检测内核类型 + if [[ "$kernel_version_full" == *bbrplus* ]]; then + kernel_status="BBRplus" + elif [[ "$kernel_version_full" =~ (4\.9\.0-4|4\.15\.0-30|4\.8\.0-36|3\.16\.0-77|3\.16\.0-4|3\.2\.0-4|4\.11\.2-1|2\.6\.32-504|4\.4\.0-47|3\.13\.0-29) ]]; then + kernel_status="Lotserver" + elif read major minor <<<$(echo "$kernel_version" | awk -F'.' '{print $1, $2}') && + { [[ "$major" == "4" && "$minor" -ge 9 ]] || [[ "$major" -ge 5 ]]; }; then + kernel_status="BBR" + else + kernel_status="noinstall" + fi + + # 运行状态检测 + if [[ "$kernel_status" == "BBR" ]]; then + case "$net_congestion_control" in + "bbr") + run_status="BBR启动成功" + ;; + "bbr2") + run_status="BBR2启动成功" + ;; + "tsunami") + if lsmod | grep -q "^tcp_tsunami"; then + run_status="BBR魔改版启动成功" + else + run_status="BBR魔改版启动失败" + fi + ;; + "nanqinlang") + if lsmod | grep -q "^tcp_nanqinlang"; then + run_status="暴力BBR魔改版启动成功" + else + run_status="暴力BBR魔改版启动失败" + fi + ;; + *) + run_status="未安装加速模块" + ;; + esac + elif [[ "$kernel_status" == "Lotserver" ]]; then + if [[ -e /appex/bin/lotServer.sh ]]; then + run_status=$(bash /appex/bin/lotServer.sh status | grep "LotServer" | awk '{print $3}') + [[ "$run_status" == "running!" ]] && run_status="启动成功" || run_status="启动失败" + else + run_status="未安装加速模块" + fi + elif [[ "$kernel_status" == "BBRplus" ]]; then + case "$net_congestion_control" in + "bbrplus") + run_status="BBRplus启动成功" + ;; + "bbr") + run_status="BBR启动成功" + ;; + *) + run_status="未安装加速模块" + ;; + esac + else + run_status="未安装加速模块" + fi + + # 检查 Headers 状态 (利用全局 OS_TYPE) + if [[ "${OS_TYPE}" == "CentOS" ]]; then + installed_headers=$(rpm -qa | grep -E "kernel(-ml|-lt|-uek|-rt|-plus)?-(devel|headers)" | grep -v '^$' || echo "") + if [[ -z "$installed_headers" ]]; then + headers_status="未安装" + else + if echo "$installed_headers" | grep -q -E "kernel(-ml|-lt|-uek|-rt|-plus)?-(devel|headers)-${kernel_version_full}"; then + headers_status="已匹配" + else + headers_status="未匹配" + fi + fi + elif [[ "${OS_TYPE}" == "Debian" ]]; then + installed_headers=$(dpkg -l | grep -E "(linux|proxmox|pve|raspberrypi)-(headers|image)" | awk '{print $2}' | grep -v '^$' || echo "") + if [[ -z "$installed_headers" ]]; then + headers_status="未安装" + else + if echo "$installed_headers" | grep -q -E "(linux|proxmox|pve|raspberrypi)-headers-${kernel_version_full}"; then + headers_status="已匹配" + else + headers_status="未匹配" + fi + fi + else + headers_status="不支持的操作系统" + fi + + # Brutal 状态检测 + brutal="" + if lsmod | grep -q "brutal"; then + brutal="brutal已加载" + fi + + # 新增:LotSpeed 状态检测 + lotspeed_status="" + if lsmod | grep -q "lotspeed"; then + if [[ "$net_congestion_control" == "lotspeed" ]]; then + run_status="LotSpeed启动成功" # 直接覆盖掉上面错误的“未安装”提示 + else + lotspeed_status="LotSpeed已加载(未设为默认)" + fi + fi +} + +#############系统检测组件############# +# ================================================= +# 入口执行逻辑 +# ================================================= + +# 系统检测与镜像测速 (两种入口共用,只跑一次) +check_sys +check_cn_status + +# 首次运行时自安装到 /usr/local/bin/tcpx (支持 bash <(curl -fsSL ...) 管道方式) +install_self + +# 命令行静默调用参数解析 (免菜单执行) +if [ $# -gt 0 ]; then + case $1 in + op0 | op1 | op2) + # 兼容老指令,重定向到自适应新版优化 + optimizing_system + exit + ;; + op3) + update_sysctl_interactive + exit + ;; + op4) + edit_sysctl_interactive + exit + ;; + *) + echo -e "${ERROR} 未知选项: \"$1\"" + exit 1 + ;; + esac +fi + +# 常规交互式启动 +start_menu