Openwrt的一些小折腾(5)

/ 4 Comments / 1883 View

前言

距离上一次折腾 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。

ZoneID

添加 A 记录和 AAAA 记录并获取域名ID

获取域名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

点击域名进去,获取记录 ID,

记录 ID

获取 DNSPod TOKEN,然后将 下面的 LOGIN_TOKEN 替换成 222222,111111*0222222

DNSPod Token

#!/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') 网络检查完毕"

已有 4 条评论
  1. 这篇文章写得深入浅出,让我这个小白也看懂了!

    回复
  2. 目前虽然用不到,还是可以收藏以备不时之需

    回复
  3. 挺实用的

    回复
  4. 没折腾过op,嘿..
    感觉自己没这方面的需求,

    回复
发表新评论