Openwrt的一些小折腾(5)
前言
距离上一次折腾 OpenWrt 已经 3 年了,这段时间里偶尔也会对家里网络环境做一些小改动,现在主路由只负责拨号、DDNS和 DHCP 服务,其他事情交给 PVE 上的 OpenWrt 来负责。所以,为了主路由的稳定,一些任务尽可能的避免安装插件,用脚本来代替。
更新 DnsPod 和 CloudFlare 并发送通知
根据 serverchan 这个插件,搞了个脚本来代替,主要就是路由重启或者网络重拨后发送一些系统状态和网络信息到 Telegram,然后再更新 ddns。
1、首先在 /etc/hotplug.d/iface 创建 30-ddns-go 给予 755 权限,会在 WAN 口 发生变化的时候实时运行 serverchan 脚本。
#!/bin/sh
# wan ifup
[ "$ACTION" = "ifup" -a "$INTERFACE" = "wan" ]
sh /usr/share/watchdog/serverchan
2、在/usr/share/watchdog/ 创建 serverchan 给予 755 权限
设置了一个参数,在直接运行 /usr/share/watchdog/serverchan send,就会发送一次标题为路由运行状况通知,方便定时任务调用。
脚本运行顺序:
当 WAN 口发生变化,就运行 serverchan ,WAN 口运行时间是否小于 60,如果大于 60 就结束脚本;
否则就进行下一个判断,WAN 口距离上一次发生变化是否大于 60,如果小于 60 就结束脚本;
否则就更新 DDNS,并做一次判断系统运行时间是否小于 60,小于60 就发送标题为 路由器重新启动;
否则就发送标题为 IP 地址变动通知;
通知都包含系统状态和网络信息。
另外需要注意,脚本在获取 IPV6 时,只是截取了前缀,所以 $wan_ipv6::1 的 ::1 是代表 路由上的 IP,可以更改成其他设备固定后缀。
脚本需要提前获取 Telegram 的 token 和 chat_id。
#!/bin/bash
# 发送 Telegram 消息
send_telegram_message() {
local message=$1
curl -s -X POST https://api.telegram.org/bot<token>/sendMessage -d "chat_id=<chat_id>" --data-binary "chat_id=$CHAT_ID&text=$message"
}
# 获取系统状态信息
get_system_status_message() {
load_average=$(cut -d ' ' -f 1-3 /proc/loadavg)
cpu_usage=$(get_cpu_usage)
memory_usage=$(free | awk '/Mem:/ {printf "%.2f%", $3/($3+$4)*100}')
uptime=$(cat /proc/uptime | awk '{print int($1/3600/24) "天" int($1/3600%24) "时" int($1%3600/60) "分" int($1%60) "秒"}')
wan_info=$(ifstatus wan | jq -r '.uptime' | awk '{print int($1/3600/24) "天" int($1/3600%24) "时" int($1%3600/60) "分" int($1%60) "秒"}')
wan_ipv4=$(ip -4 addr show dev pppoe-wan | grep inet | awk '{print $2}' | cut -d '/' -f 1)
wan_ipv6=$(ip addr show br-lan | grep inet6 | sed 's/\/.*//g' | awk '{print $2}' | grep 240e | head -n 1 | awk -F '::' '{print $1}')
system_status_message="系统状态:
--------
平均负载:$load_average
CPU 占用:$cpu_usage
内存占用:$memory_usage
运行时间:$uptime
--------
网络信息:
外网 IPv4: $wan_ipv4
外网 IPv6: $wan_ipv6::1
在线时间:$wan_info"
}
# 获取 CPU 占用
get_cpu_usage() {
local AT=$(cat /proc/stat | grep "^cpu " | awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}')
sleep 3
local BT=$(cat /proc/stat | grep "^cpu " | awk '{print $2+$3+$4+$5+$6+$7+$8 " " $2+$3+$4+$7+$8}')
printf "%.01f%%" $(echo ${AT} ${BT} | awk '{print (($4-$2)/($3-$1))*100}')
}
# 发送通知的函数
send_notification() {
local notification_message=$1
get_system_status_message
send_telegram_message "$notification_message
--------
$system_status_message"
}
# 获取系统运行时间,单位为秒
uptime_seconds=$(cut -d '.' -f 1 /proc/uptime)
# 获取 WAN 口运行时间,单位为秒
wan_uptime_seconds=$(ifstatus wan | jq -r '.uptime')
# 从临时文件中获取上一次 WAN 口变动的时间
last_wan_change_time_file="/tmp/last_wan_change_time.txt"
# 如果临时文件存在,则从文件中读取上一次 WAN 口变动的时间,否则设置为0
last_wan_change_time=$(test -f "$last_wan_change_time_file" && cat "$last_wan_change_time_file" || echo 0)
# 跟踪 WAN 口变动时间,并更新临时文件
track_wan_change_time() {
date +%s > "$last_wan_change_time_file"
}
# 检查 WAN 口通知条件
check_wan_notification() {
current_time=$(date +%s)
time_since_last_wan_change=$((current_time - last_wan_change_time))
if [ "$time_since_last_wan_change" -gt 60 ]; then
if [ "$uptime_seconds" -lt 60 ]; then
send_notification "路由器重新启动"
else
send_notification "IP 地址变动通知"
fi
#更新 DnsPod DDNS
sh /usr/share/watchdog/dnspod-go
#更新 CloudFlare DDNS
sh /usr/share/watchdog/cloudflare-go
#更新时间戳
track_wan_change_time
fi
}
# 判断 WAN 是否启动,并发送通知
if [ "$wan_uptime_seconds" -lt 60 ]; then
check_wan_notification
elif [ "$1" = "send" ]; then
send_notification "路由运行状况通知"
fi
3、在/usr/share/watchdog/ 创建 cloudflare-go 给予 755 权限
进入 CloudFlare 的主页 -> 左侧网站-> 点击需要设置的域名。在右侧下方看到 区域 ID (ZoneID),另外需要点击 API 令牌去获取 Token。
添加 A 记录和 AAAA 记录并获取域名ID
#!/bin/bash
# 更新 DNS 记录
cf_update_dns() {
local record_id="$1"
local record_type="$2"
local record_name="$3"
local record_content="$4"
wan_ipv4=$(ip -4 addr show dev pppoe-wan | grep inet | awk '{print $2}' | cut -d '/' -f 1)
wan_ipv6=$(ip addr show br-lan | grep inet6 | sed 's/\/.*//g' | awk '{print $2}' | grep 240e | head -n 1 | awk -F '::' '{print $1}')
curl -X PUT "https://api.cloudflare.com/client/v4/zones/区域 ID/dns_records/$record_id" \
-H "X-Auth-Email: Cloudflare的邮箱" \
-H "X-Auth-Key: API 令牌" \
-H "Content-Type: application/json" \
--data "{\"type\":\"$record_type\",\"name\":\"$record_name\",\"content\":\"$record_content\",\"proxied\":false}"
}
# 更新 IPv4 地址的 DNS 记录
cf_update_dns "域名ID" "A" "test1.xxxnas.top" "$wan_ipv4"
# 更新 IPv6 地址的 DNS 记录
cf_update_dns "域名ID" "AAAA" "test2.xxxnas.top" "$wan_ipv6::1"
4、在/usr/share/watchdog/ 创建 dnspod-go 给予 755 权限
进入 dnspod 控制台 -> 我的域名
点击域名进去,获取记录 ID,
获取 DNSPod TOKEN,然后将 下面的 LOGIN_TOKEN 替换成 222222,111111*0222222
#!/bin/bash
# 更新 DNS 记录函数
dp_update_dns() {
local record_type=$1
local sub_domain=$2
local record_id=$3
local value=$4
wan_ipv4=$(ip -4 addr show dev pppoe-wan | grep inet | awk '{print $2}' | cut -d '/' -f 1)
wan_ipv6=$(ip addr show br-lan | grep inet6 | sed 's/\/.*//g' | awk '{print $2}' | grep 240e | head -n 1 | awk -F '::' '{print $1}')
local record_url="https://dnsapi.cn/Record.Modify"
local record_params="login_token=LOGIN_TOKEN&format=json&domain_id=domain_id&record_id=$record_id&value=$value&record_type=$record_type&sub_domain=$sub_domain&record_line_id=0"
curl -k -X POST "$record_url" -d "$record_params"
}
# 更新 IPv4 DNS 记录
dp_update_dns A "test1" "记录ID" $wan_ipv4
# 更新 IPv6 DNS 记录
dp_update_dns AAAA "test2" "记录ID" "$wan_ipv6::1"
网络故障重启网络和重启路由
另外还对上次的监控网络状态脚本做了一点小调整,连续重启 wan 口和路由两次还网络故障的话,更改检测间隔时间为 600 秒。不过根据最近的观察,觉得应该不是网络问题,而是弱电箱的电源功率不够导致的路由死机,所以也得考虑是否更换个电源来测试下。
#!/bin/sh
# 日志默认目录
LOG_DIR="/usr/share/watchdog"
# 连续计数
NETWORK_CHECK_COUNTER_FILE="${LOG_DIR}/network-counter.log"
# 执行日志
NETWORK_CHECK_LOG_FILE="${LOG_DIR}/network-check.log"
# 默认计数为0
COUNTER=0
# 连续失败计数大于该数值,则进行 RESTART_INTERVAL 秒等待,再执行重新检测
COUNTER_THRESHOLD=4
# 持续失败,后默认等待时间(秒),然后再重启
RESTART_INTERVAL=600
# 检查目录是否存在,不存在则创建
if [ ! -d "$LOG_DIR" ]; then
echo "Creating directory $LOG_DIR"
mkdir -p "$LOG_DIR"
fi
# 检查文件是否存在,如果不存在则创建文件
touch "$NETWORK_CHECK_LOG_FILE"
if [ ! -e "$NETWORK_CHECK_COUNTER_FILE" ]; then
touch "$NETWORK_CHECK_COUNTER_FILE"
echo "0" > "$NETWORK_CHECK_COUNTER_FILE"
fi
COUNTER=$(cat "$NETWORK_CHECK_COUNTER_FILE")
# 检测网络是否畅通
ping_domain() {
# ping的域名或者DNS
local domain="223.5.5.5"
# ping的次数
local tries=6
# 请求成功次数
local packets_responded=0
for i in $(seq 1 "$tries"); do
if ping -c 1 "$domain" >/dev/null; then
packets_responded=$((packets_responded + 1))
sleep 1
fi
done
# 如果请求成功总次数大于等于2,则表示成功
if [ "$packets_responded" -ge 2 ]; then
echo "true"
else
echo "false"
fi
}
# 检测网络连接函数
check_network() {
# 如果ping 6次至少有2次包未响应,则执行以下代码
if [ "$(ping_domain)" = "false" ]; then
# 如果无法连接网络,则重启网络
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络连接失败"
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络连接失败" >> "$NETWORK_CHECK_LOG_FILE"
/etc/init.d/network restart
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络服务已重启"
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络服务已重启" >> "$NETWORK_CHECK_LOG_FILE"
echo "$(( $(cat "$NETWORK_CHECK_COUNTER_FILE") + 1 ))" > "$NETWORK_CHECK_COUNTER_FILE"
sleep 60
if [ "$(ping_domain)" = "false" ]; then
# 如果仍无法连接网络,则重启路由服务
echo "$(date '+%Y-%m-%d %H:%M:%S') 重启网络后,联网失败,准备重启路由"
echo "$(date '+%Y-%m-%d %H:%M:%S') 重启网络后,联网失败,准备重启路由" >> "$NETWORK_CHECK_LOG_FILE"
echo "$(( $(cat "$NETWORK_CHECK_COUNTER_FILE") + 1 ))" > "$NETWORK_CHECK_COUNTER_FILE"
/sbin/reboot
else
echo "$(date '+%Y-%m-%d %H:%M:%S') 重启路由后,连接已恢复"
echo "$(date '+%Y-%m-%d %H:%M:%S') 重启路由后,连接已恢复" >> "$NETWORK_CHECK_LOG_FILE"
fi
else
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络连接正常"
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络连接正常" >> "$NETWORK_CHECK_LOG_FILE"
echo "0" > "$NETWORK_CHECK_COUNTER_FILE"
fi
}
# 计数器检查函数
check_counter() {
COUNTER=$(cat "$NETWORK_CHECK_COUNTER_FILE")
if [ "$COUNTER" -ge "$COUNTER_THRESHOLD" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') 计数器值大于等于 $COUNTER_THRESHOLD ,等待 $RESTART_INTERVAL 秒后重新检测网络连接"
echo "$(date '+%Y-%m-%d %H:%M:%S') 计数器值大于等于 $COUNTER_THRESHOLD ,等待 $RESTART_INTERVAL 秒后重新检测网络连接" >> "$NETWORK_CHECK_LOG_FILE"
sleep "$RESTART_INTERVAL" # 等待
echo "$(date '+%Y-%m-%d %H:%M:%S') 等待 $RESTART_INTERVAL 秒后,开始重新检测网络" >> "$NETWORK_CHECK_LOG_FILE"
check_network
else
check_network
fi
}
check_counter
echo "$(date '+%Y-%m-%d %H:%M:%S') 网络检查完毕"