Skip to content

Instantly share code, notes, and snippets.

@koolvn
Last active January 24, 2026 12:44
Show Gist options
  • Select an option

  • Save koolvn/993d9c7eb2666dbf15bdccc7d3c4fe33 to your computer and use it in GitHub Desktop.

Select an option

Save koolvn/993d9c7eb2666dbf15bdccc7d3c4fe33 to your computer and use it in GitHub Desktop.
# UDP Trash Hack for WireGuard on AsusWRT Merlin

UDP Trash Hack for WireGuard on AsusWRT Merlin

Подготовка

Проверяем, что включен пункт меню Enable JFFS custom scripts and configs

image

Установка

  • Кладём файл wgclient-start в /jffs/scripts/
  • Делаем скрипт запускаемым
chmod +x /jffs/scripts/wgclient-start
  • Подключаем WG клиента в настройках роутера. Если клиент уже подключен, то выполняем команду /jffs/scripts/wgclient-start <номер впн клиента> (Например /jffs/scripts/wgclient-start 1 если подключен клиент под номером 1)
  • Done ✅

Спасибо @alexbozhenko за полезные ссылки:

Проверен на роутерах:

  • ASUS RT-AX88U PRO, Merlin 3004.388.8_2
  • ASUS RT-AX68U, Merlin 3004.388.8_2
  • ASUS RT-AX86U
  • ASUS RT-AX56U, Merlin 3004.388.8_2
#!/bin/sh
# Set the number of times a random message would be sent
NUM_MESSAGES_SENT=3
# Random message generation params
block_size=512
block_count=5
# Enable exit on error
set -e
# Function to generate a random number between 49152 and 65535
generate_random_port() {
awk -v min=49152 -v max=65535 'BEGIN { srand(); print int(min + rand() * (max - min + 1)) }'
}
# Function to generate a random message and send it
send_random_message() {
message=$(dd if=/dev/urandom bs=$block_size count=$block_count 2>/dev/null | tr -dc 'A-Za-z0-9')
echo "$message" | socat - UDP-SENDTO:"$wg_server:$wg_port",sourceport="$l_port"
logger -t WireGuardClient -p user.notice "Message sent to WG server $wg_server:$wg_port from client's port $l_port"
sleep 1
}
interface="wgc$1"
sleep 3
# Log the start of the script
logger -t WireGuardClient -p user.notice "Starting junk-udp-hack script"
logger -t WireGuardClient -p user.notice "Processing WG interface - $interface"
# Retrieve the server and port
wg_endpoint=$(wg show "$interface" endpoints 2>/dev/null || echo "")
if [ -z "$wg_endpoint" ]; then
logger -s -t WireGuardClient -p user.err "Unable to retrieve endpoint for interface $interface" >&2
exit 1
fi
wg_server=$(echo "$wg_endpoint" | awk '{print $2}' | cut -d':' -f1 2>/dev/null || echo "")
wg_port=$(echo "$wg_endpoint" | awk '{print $2}' | cut -d':' -f2 2>/dev/null || echo "")
if [ -z "$wg_server" ] || [ -z "$wg_port" ]; then
logger -s -t WireGuardClient -p user.err "Unable to extract server or port for interface $interface" >&2
exit 1
fi
l_port=$(generate_random_port)
while netstat -an | grep -qE '(^|[^0-9])'"$l_port"'([^0-9]|$)'; do
logger -t WireGuardClient -p user.warn "Port $l_port is already in use. Generating new one"
sleep 1
l_port=$(generate_random_port)
done
# Send random messages specified number of times
i=1
while [ "$i" -le "$NUM_MESSAGES_SENT" ]; do
send_random_message
i=$((i + 1))
done
# Update the WireGuard interface with the new listen port
wg set "$interface" listen-port "$l_port"
logger -t WireGuardClient -p user.notice "Done"

Бонус

Случайно обнаружил, что многие сервисы, блокирующие трафик из РФ используют для этого Cloudflare. Так же РКН, видимо, частично блокирует адреса Cloudflare.

Оказалось, что если прописать IPшники Cloudflare в VPN Director и гнать трафик через ВПН, то волшебным образом начинают работать многие сервисы (например chatgpt.com и многие другие)

Написал скрипт для этого чуда и спешу поделиться 😅

Установка

  1. Прописываем нужного клиента в переменную VPN_CLIENT_ID (У меня это WGC2 у вас это может быть другой - WGC1 например)
  2. (Опционально) Прописываем в переменную LOCAL_SUBNET локальную подсеть или IP. В таком случае правила будут применяться только для них. Оставьте пустым, чтоб трафик к Cloudflare от всех клиентов роутера был пущен через ВПН
  3. Записываем файл в /jffs/scripts/update_cloudflare_rules.sh
  4. Делаем его запускаемым chmod +x /jffs/scripts/update_cloudflare_rules.sh
  5. Запускаем /jffs/scripts/update_cloudflare_rules.sh
  6. Если отработал без ошибок идём на вкладку VPN Director в UI роутера http://<ip:port>/Advanced_VPNDirector.asp Должно появиться что-то похожее: image
  7. Ставим автозапуск скрипта на 3 часа утра каждый день (IPшники Cloudflare могут периодически изменяться)
# Выполняем комманду, чтоб поставить автозапуск скрипта на крон
cru a UpdCfVpnDirRules "0 3 * * * /jffs/scripts/update_cloudflare_rules.sh"
# Так как после перезагрузки расписание крона сотрётся, то прописываем его в скрипт services-start.
# Теперь после перезагрузки расписание усановится автоматически
echo 'cru a UpdCfVpnDirRules "0 3 * * * /jffs/scripts/update_cloudflare_rules.sh"' >> /jffs/scripts/services-start

Всё готово!

#!/bin/sh
# ==============================================================================
# VPN Director - Cloudflare CIDR Updater
#
# This script automatically fetches the latest Cloudflare IPv4 CIDRs,
# compares them against the currently configured rules in VPN Director,
# and updates the rule list if any changes are detected.
#
# It correctly parses and generates the concatenated rule format.
# Designed for POSIX-compliant shells (e.g., ash on BusyBox).
# ==============================================================================
# --- Configuration ---
readonly RULE_PREFIX="Cloudflare v4_"
readonly VPN_CLIENT_ID="WGC1"
readonly RULES_FILE="/jffs/openvpn/vpndirector_rulelist"
readonly LOCAL_SUBNET="" # (Optional) Specify a subnet (e.g., 192.168.1.0/24) or a single IP address to restrict the Cloudflare rules to a specific local network segment or client.
# Leave empty ("") to apply the rules to ALL clients on the router.
# Example: LOCAL_SUBNET="192.168.1.0/24" will only route traffic from this subnet through the VPN for Cloudflare IPs.
# Use for testing
#readonly RULES_FILE="./vpndirector_rulelist"
# --- Script Logic ---
set -e
set -u
set -o pipefail
readonly TEMP_DIR="/tmp/cf_updater_$$"
mkdir "$TEMP_DIR"
trap 'rm -rf -- "$TEMP_DIR"' EXIT
readonly NEW_CIDRS_FILE="$TEMP_DIR/cloudflare_cidrs_new.txt"
readonly OLD_CIDRS_FILE="$TEMP_DIR/cloudflare_cidrs_old.txt"
readonly NEW_CF_RULES_FILE="$TEMP_DIR/cloudflare_rules_new.txt"
readonly OLD_CF_RULES_FILE="$TEMP_DIR/cloudflare_rules_old.txt"
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Starting Cloudflare CIDR update process."
# Step 1: Fetch the latest Cloudflare IPv4 CIDRs.
intrfce=$(echo "$VPN_CLIENT_ID" | awk '{print tolower($0)}')
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Fetching latest Cloudflare IPv4 CIDRs..."
if ! curl --interface "$intrfce" -s "https://api.cloudflare.com/client/v4/ips" | jq -r '.result.ipv4_cidrs[]' > "$NEW_CIDRS_FILE"; then
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.error "ERROR: Failed to fetch or parse Cloudflare IPs. Exiting."
exit 1
fi
if [ ! -s "$NEW_CIDRS_FILE" ]; then
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.error "ERROR: Fetched CIDR list is empty. Exiting."
exit 1
fi
# Step 2: Extract currently configured Cloudflare rules block.
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Extracting existing Cloudflare rules from $RULES_FILE..."
if [ -f "$RULES_FILE" ] && [ -s "$RULES_FILE" ]; then
# Extract the full Cloudflare rules block (not just CIDRs)
awk -F'>' -v prefix="^${RULE_PREFIX}" 'BEGIN{RS="<"; ORS=""} NF>0 && $2 ~ prefix {print "<"$0}' "$RULES_FILE" > "$OLD_CF_RULES_FILE"
else
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Rules file does not exist or is empty. Will create it."
: > "$OLD_CF_RULES_FILE"
fi
# Step 3: Generate the new block of Cloudflare rules.
NEW_CLOUDFLARE_RULES_BLOCK=""
counter=0
while IFS= read -r cidr; do
label="${RULE_PREFIX}${counter}"
NEW_CLOUDFLARE_RULES_BLOCK="${NEW_CLOUDFLARE_RULES_BLOCK}<1>${label}>${LOCAL_SUBNET}>${cidr}>${VPN_CLIENT_ID}"
counter=$((counter + 1))
done < "$NEW_CIDRS_FILE"
echo -n "$NEW_CLOUDFLARE_RULES_BLOCK" > "$NEW_CF_RULES_FILE"
# Step 4: Compare the new and existing Cloudflare rules blocks.
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Comparing new and existing Cloudflare rules block..."
if cmp -s "$OLD_CF_RULES_FILE" "$NEW_CF_RULES_FILE"; then
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: No changes detected in Cloudflare rules. No update necessary. Exiting."
exit 0
fi
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Changes detected. Proceeding with rule update."
# Step 5: Extract all non-Cloudflare rules.
CUSTOM_RULES_BLOCK=""
if [ -f "$RULES_FILE" ] && [ -s "$RULES_FILE" ]; then
CUSTOM_RULES_BLOCK=$(awk -F'>' -v prefix="^${RULE_PREFIX}" 'BEGIN{RS="<"; ORS=""} NF>0 && $2 !~ prefix {print "<"$0}' "$RULES_FILE")
fi
# Step 6: Combine custom rules with the new Cloudflare rules by simple concatenation.
FINAL_RULE_LIST="${CUSTOM_RULES_BLOCK}${NEW_CLOUDFLARE_RULES_BLOCK}"
# Step 7: Atomically update the rule list and apply changes.
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "INFO: Writing new rules to $RULES_FILE and applying changes."
echo -n "$FINAL_RULE_LIST" > "$RULES_FILE.tmp"
mv "$RULES_FILE.tmp" "$RULES_FILE"
service restart_vpndirector
logger -sc -t "Cloudflare CIDR VPN Director Updater" -p user.notice "SUCCESS: VPN Director rules updated successfully."
exit 0
@GitHubbie0836
Copy link

"02-wgclient-start" - отличный скипт (большое вам спасибо за него!), но к сожалению он у меня перестаёт срабатывать со всё большим количеством хостингов. :(

@Scratch-net
Copy link

Спасиб тебе добрый человек

@JugglerLKR
Copy link

JugglerLKR commented Jan 15, 2026

Последние дни перестало работать. Обновленная Amnezia v1.5 на компе справляется пока, возможно ли как-то подсмотреть тут на гитхабе что они делают и попытаться это имплементировать в скрипт?
В частности у них появился новый параметр в конфиге I1 = <b 0xc20000000...>
https://docs.amnezia.org/ru/documentation/amnezia-wg/#:~:text=%D0%93%D0%BB%D0%B0%D0%B2%D0%BD%D1%8B%D0%B9%20%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%20%E2%80%94%20I1%20%E2%80%94,%D0%BC%D0%B5%D1%82%D0%BA%D0%B8%20%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%B8%20%D0%B8%20%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D1%8B%D1%85%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85.

@JugglerLKR
Copy link

JugglerLKR commented Jan 15, 2026

Мне помогло:


send_random_message() {
#   message=$(dd if=/dev/urandom bs=$block_size count=$block_count 2>/dev/null | tr -dc 'A-Za-z0-9')
#    echo "$message" | socat - UDP-SENDTO:"$wg_server:$wg_port",sourceport="$l_port"
    echo -ne "$(echo 'c2000000011419fa4bb3599f336777de79f81ca9a8d80d91eeec000044c635cef024a885dcb66d1420a91a8c427e87d6cf8e08b563932f449412cddf77d3e2594ea1c7a183c238a89e9adb7ffa57c133e55c59bec101634db90afb83f75b19fe703179e26a31902324c73f82d9354e1ed8da39af610afcb27e6590a44341a0828e5a3d2f0e0f7b0945d7bf3402feea0ee6332e19bdf48ffc387a97227aa97b205a485d282cd66d1c384bafd63dc42f822c4df2109db5b5646c458236ddcc01ae1c493482128bc0830c9e1233f0027a0d262f92b49d9d8abd9a9e0341f6e1214761043c021d7aa8c464b9d865f5fbe234e49626e00712031703a3e23ef82975f014ee1e1dc428521dc23ce7c6c13663b19906240b3efe403cf30559d798871557e4e60e86c29ea4504ed4d9bb8b549d0e8acd6c334c39bb8fb42ede68fb2aadf00cfc8bcc12df03602bbd4fe701d64a39f7ced112951a83b1dbbe6cd696dd3f15985c1b9fef72fa8d0319708b633cc4681910843ce753fac596ed9945d8b839aeff8d3bf0449197bd0bb22ab8efd5d63eb4a95db8d3ffc796ed5bcf2f4a136a8a36c7a0c65270d511aebac733e61d414050088a1c3d868fb52bc7e57d3d9fd132d78b740a6ecdc6c24936e92c28672dbe00928d89b891865f885aeb4c4996d50c2bbbb7a99ab5de02ac89b3308e57bcecf13f2da0333d1420e18b66b4c23d625d836b538fc0c221d6bd7f566a31fa292b85be96041d8e0bfe655d5dc1afed23eb8f2b3446561bbee7644325cc98d31cea38b865bdcc507e48c6ebdc7553be7bd6ab963d5a14615c4b81da7081c127c791224853e2d19bafdc0d9f3f3a6de898d14abb0e2bc849917e0a599ed4a541268ad0e60ea4d147dc33d17fa82f22aa505ccb53803a31d10a7ca2fea0b290a52ee92c7bf4aab7cea4e3c07b1989364eed87a3c6ba65188cd349d37ce4eefde9ec43bab4b4dc79e03469c2ad6b902e28e0bbbbf696781ad4edf424ffb35ce0236d373629008f142d04b5e08a124237e03e3149f4cdde92d7fae581a1ac332e26b2c9c1a6bdec5b3a9c7a2a870f7a0c25fc6ce245e029b686e346c6d862ad8df6d9b62474fbc31dbb914711f78074d4441f4e6e9edca3c52315a5c0653856e23f681558d669f4a4e6915bcf42b56ce36cb7dd3983b0b1d6fdf0f8efddb68e7ca0ae9dd4570fe6978fbb524109f6ec957ca61f1767ef74eb803b0f16abd0087cf2d01bc1db1c01d97ac81b3196c934586963fe7cf2d310e0739621e8bd00dc23fded18576d8c8f285d7bb5f43b547af3c76235de8b6f757f817683b2151600b11721219212bf27558edd439e73fce951f61d582320e5f4d6c315c71129b719277fc144bbe8ded25ab6d29b6e189c9bd9b16538faf60cc2aab3c3bb81fc2213657f2dd0ceb9b3b871e1423d8d3e8cc008721ef03b28e0ee7bb66b8f2a2ac01ef88df1f21ed49bf1ce435df31ac34485936172567488812429c269b49ee9e3d99652b51a7a614b7c460bf0d2d64d8349ded7345bedab1ea0a766a8470b1242f38d09f7855a32db39516c2bd4bcc538c52fa3a90c8714d4b006a15d9c7a7d04919a1cab48da7cce0d5de1f9e5f8936cffe469132991c6eb84c5191d1bcf69f70c58d9a7b66846440a9f0eef25ee6ab62715b50ca7bef0bc3013d4b62e1639b5028bdf757454356e9326a4c76dabfb497d451a3a1d2dbd46ec283d255799f72dfe878ae25892e25a2542d3ca9018394d8ca35b53ccd94947a8' | sed -e 's/../\\x&/g')" | socat - UDP-SENDTO:"$wg_server:$wg_port",sourceport="$l_port"
    logger -t WireGuardClient -p user.notice "Message sent to WG server $wg_server:$wg_port from client's port $l_port"
    sleep 1
}


строка для echo взята отсюда
https://github.com/ImMALWARE/bash-warp-generator

@Nomoneyno
Copy link

cru a UpdCfVpnDirRules "0 3 * * * /jffs/scripts/update_cloudflare_rules.sh"
Разве cron не чистится после перезагрузки на Мерлине? Надо наверное в services-start добавлять эту запись

@koolvn
Copy link
Author

koolvn commented Jan 24, 2026

@Nomoneyno Спасибо, что обнаружили недочёт в инструкции! Действительно, эту команду надо прописать в /jffs/scripts/services-start
Исправлю

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment