linux ppp-off命令

命令简介

ppp-off 是一个用于断开PPP(点对点协议)拨号连接的Shell脚本,通常与ppp-on配对使用。它通过发送终止信号给pppd进程来关闭拨号连接,释放网络接口,并清理相关资源。虽然现代Linux系统通常使用网络管理工具或poff命令,但在一些遗留系统或脚本中仍可能遇到。

注意:ppp-off 是较老的PPP连接管理脚本,现代Linux系统中更多使用poffifdown或NetworkManager等工具。但在一些嵌入式系统或自定义拨号脚本中仍在使用。

基本语法

# 基本格式
ppp-off [选项] [连接名称]

# 常用格式
ppp-off                     # 断开默认连接
ppp-off dsl-provider       # 断开指定连接
ppp-off -a                 # 断开所有连接
sudo ppp-off               # 需要root权限

注意:在某些系统中,ppp-off可能是一个脚本文件,位于/usr/sbin//etc/ppp//etc/ppp/scripts/目录下。

ppp-off 与 poff 对比

特性 ppp-off poff
实现方式 Shell脚本 pppd工具的一部分
功能完整性 基本功能,断开连接 完整功能,管理连接
配置依赖 需要单独的脚本文件 使用pppd配置文件
连接名称支持 有限支持 完整支持(通过peers)
错误处理 简单 详细错误信息
日志记录 有限 完整日志支持
系统集成 独立脚本 与pppd深度集成
现代使用 遗留系统,简单脚本 推荐用于pppd连接
灵活性 可自定义修改 标准化,功能固定
发行版支持 部分发行版预装 所有支持ppp的发行版

常用选项

大多数ppp-off实现支持以下选项:

选项 描述
-a, --all 断开所有PPP连接
-h, --help 显示帮助信息
-v, --verbose 详细模式,显示更多信息
-q, --quiet 安静模式,不显示输出
-f, --force 强制断开,不等待清理
-d, --debug 调试模式,显示执行过程
-c 连接名 指定要断开的连接名称
-i 接口 指定要断开的网络接口(如ppp0)
-p PID文件 指定PID文件位置
-t 超时 设置断开超时时间(秒)

安装和配置

在一些系统中,ppp-off可能需要手动安装或创建:

# 1. 检查是否已安装
which ppp-off
# 或
ls -la /usr/sbin/ppp-off /etc/ppp/ppp-off

# 2. 安装ppp包(包含ppp-off脚本)
# Debian/Ubuntu
sudo apt-get update
sudo apt-get install ppp

# RHEL/CentOS
sudo yum install ppp
# 或
sudo dnf install ppp

# 安装后,ppp-off通常位于:
# /usr/sbin/ppp-off
# 或
# /etc/ppp/ppp-off

# 3. 手动创建ppp-off脚本
# 如果系统没有提供,可以手动创建
sudo nano /usr/local/bin/ppp-off
# 内容如下:
#!/bin/bash
# ppp-off脚本
# 断开PPP连接

# 默认设置
CONFIG_FILE="/etc/ppp/ppp-off.conf"
LOG_FILE="/var/log/ppp-off.log"
PID_FILE="/var/run/ppp0.pid"
INTERFACE="ppp0"
VERBOSE=0
FORCE=0

# 加载配置文件
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"

# 解析参数
while [ $# -gt 0 ]; do
    case $1 in
        -a|--all)
            DISCONNECT_ALL=1
            shift
            ;;
        -h|--help)
            echo "用法: ppp-off [选项]"
            echo "选项:"
            echo "  -a, --all     断开所有PPP连接"
            echo "  -h, --help    显示此帮助"
            echo "  -v, --verbose 详细模式"
            echo "  -q, --quiet   安静模式"
            echo "  -f, --force   强制断开"
            echo "  -c CONN       指定连接名称"
            echo "  -i IFACE      指定接口(如ppp0)"
            echo "  -p PIDFILE    指定PID文件"
            exit 0
            ;;
        -v|--verbose)
            VERBOSE=1
            shift
            ;;
        -q|--quiet)
            VERBOSE=0
            shift
            ;;
        -f|--force)
            FORCE=1
            shift
            ;;
        -c)
            shift
            CONNECTION=$1
            shift
            ;;
        -i)
            shift
            INTERFACE=$1
            shift
            ;;
        -p)
            shift
            PID_FILE=$1
            shift
            ;;
        *)
            # 非选项参数作为连接名
            CONNECTION=$1
            shift
            ;;
    esac
done

# 日志函数
log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
    [ $VERBOSE -eq 1 ] && echo "$1"
}

# 检查是否以root运行
if [ "$(id -u)" != "0" ]; then
    echo "错误: 需要root权限运行ppp-off"
    exit 1
fi

# 断开连接函数
disconnect_ppp() {
    local iface=$1
    local pid_file=$2

    log_message "断开PPP连接: 接口 $iface"

    # 检查接口是否存在
    if ! ip link show "$iface" >/dev/null 2>&1; then
        log_message "接口 $iface 不存在,可能已断开"
        return 0
    fi

    # 获取PID
    local pid=""
    if [ -f "$pid_file" ]; then
        pid=$(cat "$pid_file")
    else
        # 尝试从进程列表获取
        pid=$(ps aux | grep "pppd.*$iface" | grep -v grep | awk '{print $2}' | head -1)
    fi

    # 发送终止信号
    if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
        log_message "发送TERM信号到pppd进程 $pid"
        kill -TERM "$pid"

        # 等待进程结束
        local timeout=10
        local count=0
        while kill -0 "$pid" 2>/dev/null && [ $count -lt $timeout ]; do
            sleep 1
            count=$((count + 1))
        done

        # 如果进程仍在运行,强制杀死
        if kill -0 "$pid" 2>/dev/null; then
            if [ $FORCE -eq 1 ]; then
                log_message "强制杀死pppd进程 $pid"
                kill -KILL "$pid"
            else
                log_message "警告: pppd进程 $pid 仍在运行"
            fi
        else
            log_message "pppd进程 $pid 已终止"
        fi
    else
        log_message "未找到运行的pppd进程"
    fi

    # 清理PID文件
    [ -f "$pid_file" ] && rm -f "$pid_file"

    # 删除路由
    log_message "清理路由表..."
    ip route del default dev "$iface" 2>/dev/null

    # 关闭接口
    log_message "关闭接口 $iface"
    ip link set "$iface" down

    # 删除接口
    ip link delete "$iface" 2>/dev/null

    log_message "PPP连接 $iface 已断开"
    return 0
}

# 主程序
main() {
    log_message "=== 开始断开PPP连接 ==="

    if [ "$DISCONNECT_ALL" = "1" ]; then
        # 断开所有PPP接口
        for iface in $(ip link show | grep "ppp" | awk -F: '{print $2}' | tr -d ' '); do
            disconnect_ppp "$iface" "/var/run/${iface}.pid"
        done
    elif [ -n "$CONNECTION" ]; then
        # 断开指定连接
        disconnect_ppp "$INTERFACE" "/var/run/$CONNECTION.pid"
    else
        # 断开默认连接
        disconnect_ppp "$INTERFACE" "$PID_FILE"
    fi

    log_message "=== 断开完成 ==="
}

# 执行主程序
main "$@"
# 设置权限
sudo chmod +x /usr/local/bin/ppp-off

# 4. 创建配置文件(可选)
sudo nano /etc/ppp/ppp-off.conf
# 内容:
# INTERFACE="ppp0"
# PID_FILE="/var/run/ppp0.pid"
# LOG_FILE="/var/log/ppp-off.log"
# VERBOSE=1

# 5. 测试ppp-off
sudo ppp-off --help
# 应该显示帮助信息

# 6. 创建ppp-on配对脚本
# 通常ppp-off与ppp-on配对使用
sudo cp /usr/local/bin/ppp-off /usr/local/bin/ppp-on
# 然后修改ppp-on为连接脚本

实际示例

示例1:基本断开连接

断开PPP连接的基本操作:

# 1. 首先检查当前PPP连接状态
ifconfig ppp0
# 或
ip addr show ppp0
# 输出示例:
# ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1492
#        inet 192.168.1.100  netmask 255.255.255.255  destination 192.168.1.1
#        ppp  txqueuelen 3  (Point-to-Point Protocol)

# 查看路由表
route -n
# 或
ip route show
# 应该看到默认路由通过ppp0

# 2. 使用ppp-off断开连接
sudo ppp-off
# 输出示例:
# 断开PPP连接: 接口 ppp0
# 发送TERM信号到pppd进程 1234
# pppd进程 1234 已终止
# 清理路由表...
# 关闭接口 ppp0
# PPP连接 ppp0 已断开

# 3. 验证连接已断开
ifconfig ppp0
# 应该显示: ppp0: error fetching interface information: Device not found
# 或没有输出

ip link show ppp0
# 接口应该不存在

# 4. 检查路由表
route -n
# 默认路由应该不再通过ppp0

# 5. 查看ppp-off日志
tail -f /var/log/ppp-off.log
# 输出断开连接的详细日志

# 6. 检查pppd进程
ps aux | grep pppd
# 应该没有pppd进程运行

# 7. 查看系统日志
tail -f /var/log/syslog | grep ppp
# 或
journalctl -f | grep ppp

# 8. 清理残留文件
# 检查PID文件
ls -la /var/run/*.pid
# 如果仍有ppp相关PID文件,可以删除
sudo rm -f /var/run/ppp*.pid

示例2:高级断开操作

使用不同选项断开PPP连接:

# 1. 断开所有PPP连接
sudo ppp-off -a
# 或
sudo ppp-off --all
# 这会断开ppp0, ppp1, ppp2等所有PPP接口

# 2. 指定接口断开
sudo ppp-off -i ppp1
# 只断开ppp1接口

# 3. 指定连接名称断开
sudo ppp-off -c dsl-provider
# 断开名为"dsl-provider"的连接
# 会使用/var/run/dsl-provider.pid作为PID文件

# 4. 强制断开
sudo ppp-off -f
# 或
sudo ppp-off --force
# 不等待正常终止,直接发送KILL信号

# 5. 详细模式
sudo ppp-off -v
# 或
sudo ppp-off --verbose
# 显示详细执行过程

# 6. 安静模式
sudo ppp-off -q
# 或
sudo ppp-off --quiet
# 不显示任何输出,只记录到日志

# 7. 调试模式
sudo ppp-off -d
# 显示调试信息,包括执行的每个步骤

# 8. 指定PID文件
sudo ppp-off -p /tmp/myppp.pid
# 使用指定的PID文件

# 9. 设置超时
# 修改ppp-off脚本,添加超时选项
# 或使用timeout命令
timeout 10 sudo ppp-off
# 10秒后强制终止

# 10. 组合选项
sudo ppp-off -v -f -i ppp0
# 详细模式 + 强制断开 + 指定接口

# 11. 在脚本中使用
#!/bin/bash
# 自动断开重连脚本
MAX_RETRIES=3
RETRY_DELAY=5

for i in $(seq 1 $MAX_RETRIES); do
    echo "尝试断开连接 (尝试 $i/$MAX_RETRIES)"

    if sudo ppp-off -q; then
        echo "连接已成功断开"
        break
    else
        echo "断开失败,等待 ${RETRY_DELAY}秒后重试..."
        sleep $RETRY_DELAY
    fi
done

if [ $i -eq $MAX_RETRIES ]; then
    echo "错误: 无法断开PPP连接"
    exit 1
fi

# 12. 通过信号直接断开
# 找到pppd进程ID
PPPD_PID=$(ps aux | grep pppd | grep -v grep | awk '{print $2}')
# 发送断开信号
sudo kill -TERM $PPPD_PID
# 等待断开
sleep 2
# 如果仍在运行,强制杀死
sudo kill -KILL $PPPD_PID 2>/dev/null

示例3:完整的ppp-on/ppp-off脚本对

创建完整的ppp-on和ppp-off脚本对:

#!/bin/bash
# 文件名: ppp-on
# 完整的PPP连接脚本

CONFIG_FILE="/etc/ppp/ppp-on.conf"
LOG_FILE="/var/log/ppp-on.log"
PID_FILE="/var/run/ppp0.pid"
VERBOSE=1

# 加载配置
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
    [ $VERBOSE -eq 1 ] && echo "$1"
}

# 检查root权限
check_root() {
    if [ "$(id -u)" != "0" ]; then
        echo "错误: 需要root权限"
        exit 1
    fi
}

# 检查调制解调器
check_modem() {
    if [ ! -c "$DEVICE" ]; then
        log "错误: 调制解调器设备 $DEVICE 不存在"
        return 1
    fi

    # 检查设备权限
    if [ ! -r "$DEVICE" ] || [ ! -w "$DEVICE" ]; then
        log "错误: 对 $DEVICE 没有读写权限"
        return 1
    fi

    return 0
}

# 建立连接
establish_connection() {
    log "开始建立PPP连接"

    # 清理可能存在的旧连接
    if [ -f "$PID_FILE" ]; then
        OLD_PID=$(cat "$PID_FILE")
        if kill -0 "$OLD_PID" 2>/dev/null; then
            log "发现旧的pppd进程 $OLD_PID,正在终止..."
            kill -TERM "$OLD_PID"
            sleep 2
        fi
        rm -f "$PID_FILE"
    fi

    # 启动pppd
    log "启动pppd: 设备=$DEVICE, 速率=$SPEED"

    pppd "$DEVICE" "$SPEED" \
        noauth \
        defaultroute \
        usepeerdns \
        persist \
        holdoff 10 \
        maxfail 0 \
        debug \
        nodetach \
        "$@" \
        > /tmp/pppd.log 2>&1 &

    PPPD_PID=$!
    echo $PPPD_PID > "$PID_FILE"

    log "pppd已启动,PID: $PPPD_PID"

    # 等待连接建立
    log "等待连接建立..."
    local timeout=30
    local connected=0

    for i in $(seq 1 $timeout); do
        if grep -q "local  IP address" /tmp/pppd.log 2>/dev/null; then
            connected=1
            break
        fi
        sleep 1
    done

    if [ $connected -eq 1 ]; then
        # 获取分配的IP
        LOCAL_IP=$(grep "local  IP address" /tmp/pppd.log | awk '{print $NF}')
        REMOTE_IP=$(grep "remote IP address" /tmp/pppd.log | awk '{print $NF}')

        log "连接建立成功"
        log "本地IP: $LOCAL_IP"
        log "远程IP: $REMOTE_IP"
        log "接口: ppp0"

        # 显示连接信息
        ifconfig ppp0
        route -n | grep ppp0

        return 0
    else
        log "错误: 连接超时"
        kill -TERM $PPPD_PID 2>/dev/null
        rm -f "$PID_FILE"
        return 1
    fi
}

# 主程序
main() {
    check_root

    log "=== 开始PPP连接 ==="

    # 检查必要参数
    if [ -z "$DEVICE" ]; then
        DEVICE="/dev/ttyS0"
    fi

    if [ -z "$SPEED" ]; then
        SPEED="115200"
    fi

    if ! check_modem; then
        exit 1
    fi

    # 建立连接
    if establish_connection "$@"; then
        log "=== 连接成功 ==="
        exit 0
    else
        log "=== 连接失败 ==="
        exit 1
    fi
}

# 执行
main "$@"
#!/bin/bash
# 文件名: ppp-off
# 完整的PPP断开脚本

CONFIG_FILE="/etc/ppp/ppp-off.conf"
LOG_FILE="/var/log/ppp-off.log"
PID_FILE="/var/run/ppp0.pid"
INTERFACE="ppp0"
VERBOSE=1
FORCE=0

# 加载配置
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
    [ $VERBOSE -eq 1 ] && echo "$1"
}

# 检查root权限
check_root() {
    if [ "$(id -u)" != "0" ]; then
        echo "错误: 需要root权限"
        exit 1
    fi
}

# 断开连接
disconnect() {
    local iface=$1
    local pid_file=$2

    log "断开PPP连接: 接口 $iface"

    # 检查接口
    if ! ip link show "$iface" >/dev/null 2>&1; then
        log "接口 $iface 不存在"
        return 0
    fi

    # 获取IP信息(记录用)
    local ip_info=$(ip addr show "$iface" 2>/dev/null | grep "inet " || echo "无IP地址")
    log "接口信息: $ip_info"

    # 获取PID
    local pid=""
    if [ -f "$pid_file" ] && [ -s "$pid_file" ]; then
        pid=$(cat "$pid_file")
        if ! kill -0 "$pid" 2>/dev/null; then
            log "PID文件存在但进程 $pid 未运行"
            pid=""
        fi
    fi

    if [ -z "$pid" ]; then
        # 从进程列表查找
        pid=$(ps aux | grep "pppd.*$iface" | grep -v grep | awk '{print $2}' | head -1)
    fi

    # 终止进程
    if [ -n "$pid" ]; then
        log "找到pppd进程: $pid"

        if [ $FORCE -eq 1 ]; then
            log "强制终止进程 $pid"
            kill -KILL "$pid" 2>/dev/null
        else
            log "发送终止信号到进程 $pid"
            kill -TERM "$pid" 2>/dev/null

            # 等待终止
            local timeout=10
            for i in $(seq 1 $timeout); do
                if ! kill -0 "$pid" 2>/dev/null; then
                    log "进程 $pid 已终止"
                    break
                fi
                sleep 1
            done

            # 检查是否仍在运行
            if kill -0 "$pid" 2>/dev/null; then
                log "进程仍在运行,强制终止"
                kill -KILL "$pid" 2>/dev/null
            fi
        fi
    else
        log "未找到pppd进程"
    fi

    # 清理PID文件
    [ -f "$pid_file" ] && rm -f "$pid_file"

    # 清理路由
    log "清理路由表..."
    ip route del default dev "$iface" 2>/dev/null

    # 关闭并删除接口
    log "关闭接口 $iface"
    ip link set "$iface" down 2>/dev/null
    ip link delete "$iface" 2>/dev/null

    # 清理DNS
    log "恢复DNS设置..."
    # 这里可以添加恢复原DNS服务器的代码

    log "PPP连接 $iface 已断开"
    return 0
}

# 断开所有连接
disconnect_all() {
    log "断开所有PPP连接"

    # 查找所有PPP接口
    local interfaces=$(ip link show | grep -o "ppp[0-9]\+" | sort -u)

    if [ -z "$interfaces" ]; then
        log "没有找到PPP接口"
        return 0
    fi

    for iface in $interfaces; do
        disconnect "$iface" "/var/run/${iface}.pid"
    done

    # 清理残留进程
    local pppd_pids=$(ps aux | grep pppd | grep -v grep | awk '{print $2}')
    if [ -n "$pppd_pids" ]; then
        log "清理残留pppd进程"
        kill -TERM $pppd_pids 2>/dev/null
        sleep 1
        kill -KILL $pppd_pids 2>/dev/null
    fi

    # 清理所有PID文件
    rm -f /var/run/ppp*.pid

    log "所有PPP连接已断开"
}

# 主程序
main() {
    check_root

    log "=== 开始断开PPP连接 ==="

    # 解析参数
    while [ $# -gt 0 ]; do
        case $1 in
            -a|--all)
                disconnect_all
                exit 0
                ;;
            -f|--force)
                FORCE=1
                shift
                ;;
            -q|--quiet)
                VERBOSE=0
                shift
                ;;
            -v|--verbose)
                VERBOSE=1
                shift
                ;;
            -i)
                shift
                INTERFACE=$1
                shift
                ;;
            -p)
                shift
                PID_FILE=$1
                shift
                ;;
            *)
                # 连接名
                INTERFACE="ppp0"
                PID_FILE="/var/run/$1.pid"
                shift
                ;;
        esac
    done

    # 执行断开
    disconnect "$INTERFACE" "$PID_FILE"

    log "=== 断开完成 ==="
}

# 执行
main "$@"
# 创建配置文件
sudo nano /etc/ppp/ppp-on.conf
# 内容:
DEVICE="/dev/ttyS0"
SPEED="115200"
TELEPHONE="1234567"
USERNAME="myuser"
PASSWORD="mypass"
# 可选:调制解调器初始化字符串
MODEM_INIT="ATZ"
# 可选:调试级别
DEBUG=1

# 设置权限
sudo chmod +x /usr/local/bin/ppp-on
sudo chmod +x /usr/local/bin/ppp-off

# 创建日志目录
sudo mkdir -p /var/log/ppp
sudo touch /var/log/ppp-on.log /var/log/ppp-off.log
sudo chmod 644 /var/log/ppp/*.log

# 测试连接
sudo ppp-on
# 等待连接建立...
# 测试断开
sudo ppp-off

# 创建systemd服务(可选)
sudo nano /etc/systemd/system/ppp-connection.service
# 内容:
[Unit]
Description=PPP Dial-up Connection
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/ppp-on
ExecStop=/usr/local/bin/ppp-off
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable ppp-connection
sudo systemctl start ppp-connection
sudo systemctl status ppp-connection

示例4:故障排查和恢复

ppp-off故障排查和连接恢复:

# 1. ppp-off无法断开连接
# 检查是否有多个pppd进程
ps aux | grep pppd
# 如果有多个,全部终止
sudo killall pppd
# 或
sudo pkill -9 pppd

# 2. 接口仍存在
# 检查接口
ip link show ppp0
# 如果存在,手动删除
sudo ip link delete ppp0
# 如果提示"Operation not permitted",可能是接口忙
# 先关闭
sudo ip link set ppp0 down
sudo ip link delete ppp0

# 3. 路由未清理
# 检查路由
route -n
# 或
ip route show
# 删除残留路由
sudo ip route del default dev ppp0
sudo ip route del 192.168.1.0/24 dev ppp0  # 示例

# 4. PID文件残留
# 检查PID文件
ls -la /var/run/*.pid
# 删除所有ppp相关PID文件
sudo rm -f /var/run/ppp*.pid
sudo rm -f /var/run/pppd*.pid

# 5. 调制解调器未重置
# 重置串行端口
sudo stty -F /dev/ttyS0 sane
# 发送调制解调器复位命令
echo "ATZ" | sudo tee /dev/ttyS0
# 等待响应
sleep 2

# 6. 系统日志检查
# 查看相关日志
sudo tail -f /var/log/syslog | grep -E "ppp|pppd|ppp-off"
sudo dmesg | grep ppp
sudo journalctl -f | grep ppp

# 7. 网络命名空间问题
# 检查是否在特定网络命名空间中
ip netns list
# 如果在命名空间中,需要在其中执行
sudo ip netns exec NAMESPACE ppp-off

# 8. 权限问题
# 检查sudo配置
sudo -l
# 检查脚本权限
ls -la /usr/local/bin/ppp-off
# 应为root可执行
sudo chown root:root /usr/local/bin/ppp-off
sudo chmod 4755 /usr/local/bin/ppp-off

# 9. 脚本语法错误
# 检查脚本语法
bash -n /usr/local/bin/ppp-off
# 调试执行
bash -x /usr/local/bin/ppp-off

# 10. 配置文件问题
# 检查配置文件语法
source /etc/ppp/ppp-off.conf
# 或
. /etc/ppp/ppp-off.conf
# 不应有错误

# 11. 资源冲突
# 检查端口占用
sudo lsof /dev/ttyS0
# 如果有其他进程使用,终止
sudo kill -9 $(sudo lsof -t /dev/ttyS0)

# 12. 恢复网络设置
# 断开后恢复原有网络配置
# 如果有网络管理器,重新启动
sudo systemctl restart NetworkManager
# 或
sudo systemctl restart network

# 13. 自动恢复脚本
#!/bin/bash
# 自动恢复网络连接
MAX_ATTEMPTS=3
LOG_FILE="/var/log/network-recovery.log"

log() {
    echo "$(date) - $1" >> $LOG_FILE
}

recover_network() {
    log "开始恢复网络连接"

    # 1. 断开所有PPP连接
    if which ppp-off >/dev/null 2>&1; then
        sudo ppp-off -a
    else
        sudo poff -a
    fi

    # 2. 清理残留
    sudo pkill -9 pppd
    sudo ip link delete ppp0 2>/dev/null
    sudo ip link delete ppp1 2>/dev/null

    # 3. 重启网络服务
    if systemctl is-active NetworkManager >/dev/null 2>&1; then
        sudo systemctl restart NetworkManager
    elif systemctl is-active network >/dev/null 2>&1; then
        sudo systemctl restart network
    fi

    # 4. 等待恢复
    sleep 5

    # 5. 测试连接
    if ping -c 2 -W 2 8.8.8.8 >/dev/null 2>&1; then
        log "网络恢复成功"
        return 0
    else
        log "网络恢复失败"
        return 1
    fi
}

# 主程序
for i in $(seq 1 $MAX_ATTEMPTS); do
    log "恢复尝试 $i/$MAX_ATTEMPTS"
    if recover_network; then
        log "恢复成功"
        exit 0
    fi
    sleep 10
done

log "无法恢复网络连接"
exit 1

示例5:现代替代方案

使用现代工具断开PPP连接:

# 1. 使用poff (pppd工具的一部分)
# 断开默认连接
sudo poff
# 断开指定连接
sudo poff dsl-provider
# 断开所有连接
sudo poff -a
# 强制断开
sudo poff -c
# 显示帮助
poff --help

# 2. 使用ifdown
# 断开PPP接口
sudo ifdown ppp0
# 通过接口配置
sudo ifdown ppp0
# 如果配置了/etc/network/interfaces
# auto ppp0
# iface ppp0 inet ppp
#     provider dsl-provider

# 3. 使用NetworkManager
# 命令行
nmcli connection down ppp0
# 或
nmcli connection down id "My PPP Connection"
# 查看连接
nmcli connection show
# 断开所有
nmcli connection down --all

# 4. 使用systemd
# 停止ppp服务
sudo systemctl stop pppd@dsl-provider
# 禁用自动启动
sudo systemctl disable pppd@dsl-provider
# 查看状态
sudo systemctl status pppd@dsl-provider

# 5. 使用wvdial
# 如果使用wvdial拨号
sudo killall wvdial
# 或
sudo pkill wvdial
# 断开连接
wvdial --disconnect

# 6. 使用chat/pppd直接控制
# 找到pppd PID
PPP_PID=$(ps aux | grep pppd | grep -v grep | awk '{print $2}')
# 发送断开信号
sudo kill -TERM $PPP_PID
# 或通过控制文件
echo "quit" > /var/run/ppp0.pid

# 7. 使用ppp工具集
# 查看活动连接
plog
# 查看连接统计
pppstats
# 断开连接
# 先找到连接信息,然后终止对应进程

# 8. 使用Python脚本
#!/usr/bin/env python3
# 现代Python PPP断开脚本
import subprocess
import sys
import os
import signal
import time

def disconnect_ppp(interface="ppp0", force=False):
    """断开PPP连接"""

    # 检查权限
    if os.geteuid() != 0:
        print("需要root权限", file=sys.stderr)
        return False

    print(f"断开PPP连接: {interface}")

    # 检查接口是否存在
    result = subprocess.run(
        ["ip", "link", "show", interface],
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        print(f"接口 {interface} 不存在")
        return True

    # 查找pppd进程
    result = subprocess.run(
        ["pgrep", "-f", f"pppd.*{interface}"],
        capture_output=True,
        text=True
    )

    pids = result.stdout.strip().split()

    if pids:
        for pid in pids:
            if pid:
                print(f"终止进程: {pid}")
                if force:
                    os.kill(int(pid), signal.SIGKILL)
                else:
                    os.kill(int(pid), signal.SIGTERM)

                # 等待进程结束
                for _ in range(10):
                    try:
                        os.kill(int(pid), 0)
                        time.sleep(1)
                    except OSError:
                        break
    else:
        print("未找到pppd进程")

    # 删除接口
    subprocess.run(["ip", "link", "delete", interface], capture_output=True)

    # 清理路由
    subprocess.run(["ip", "route", "del", "default", "dev", interface], capture_output=True)

    print("连接已断开")
    return True

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="断开PPP连接")
    parser.add_argument("-i", "--interface", default="ppp0", help="PPP接口名")
    parser.add_argument("-f", "--force", action="store_true", help="强制断开")
    parser.add_argument("-a", "--all", action="store_true", help="断开所有PPP连接")

    args = parser.parse_args()

    if args.all:
        # 查找所有PPP接口
        result = subprocess.run(
            ["ip", "-o", "link", "show"],
            capture_output=True,
            text=True
        )

        for line in result.stdout.splitlines():
            if "ppp" in line:
                iface = line.split(":")[1].strip()
                disconnect_ppp(iface, args.force)
    else:
        disconnect_ppp(args.interface, args.force)

常见问题

排查步骤:

  1. 检查pppd进程:
    ps aux | grep pppd
    # 如果存在,手动终止
    sudo killall pppd
    # 或
    sudo pkill -9 pppd
  2. 检查网络接口:
    ip link show
    # 查找ppp接口
    # 手动删除
    sudo ip link delete ppp0
    # 如果忙,先关闭
    sudo ip link set ppp0 down
    sudo ip link delete ppp0
  3. 检查路由表:
    route -n
    # 或
    ip route show
    # 删除ppp相关路由
    sudo ip route del default dev ppp0
    # 删除特定路由
    sudo ip route del 192.168.1.0/24 dev ppp0
  4. 检查PID文件:
    ls -la /var/run/*.pid
    # 删除残留文件
    sudo rm -f /var/run/ppp*.pid
    sudo rm -f /var/run/pppd*.pid
  5. 检查调制解调器状态:
    # 重置串行端口
    sudo stty -F /dev/ttyS0 sane
    # 发送调制解调器命令
    echo "ATH0" | sudo tee /dev/ttyS0
    # 挂断调制解调器
  6. 检查系统日志:
    sudo tail -f /var/log/syslog | grep ppp
    sudo dmesg | grep ppp
    sudo journalctl -f | grep ppp
  7. 使用force选项:
    sudo ppp-off -f
    # 或
    sudo ppp-off --force

迁移步骤:

  1. 安装ppp工具:确保已安装完整ppp包
    # Debian/Ubuntu
    sudo apt-get install ppp
    # RHEL/CentOS
    sudo yum install ppp
  2. 配置pppd:创建/etc/ppp/peers/配置文件
    # 创建连接配置
    sudo nano /etc/ppp/peers/dsl-provider
    # 内容:
    /dev/ttyS0
    115200
    noauth
    defaultroute
    usepeerdns
    persist
    maxfail 0
    holdoff 10
    debug
    # 认证信息(在单独文件中)
  3. 创建认证文件:
    # PAP认证
    sudo nano /etc/ppp/pap-secrets
    # 内容:
    # 用户名   服务    密码    IP地址
    myuser    *       mypass  *
    
    # CHAP认证
    sudo nano /etc/ppp/chap-secrets
    # 类似格式
  4. 迁移连接脚本:将ppp-on改为pon
    # 原:ppp-on
    # 新:pon dsl-provider
    # 创建别名
    alias ppp-on='pon dsl-provider'
  5. 迁移断开脚本:将ppp-off改为poff
    # 原:ppp-off
    # 新:poff
    # 或指定连接
    # poff dsl-provider
    # 创建别名
    alias ppp-off='poff'
  6. 测试新命令:
    # 建立连接
    pon dsl-provider
    # 查看状态
    plog
    # 断开连接
    poff
    # 或
    poff dsl-provider
  7. 更新脚本:修改现有脚本
    # 查找使用ppp-off的脚本
    grep -r "ppp-off" /etc/ /usr/local/bin/ 2>/dev/null
    # 替换为poff
    sudo sed -i 's/ppp-off/poff/g' /path/to/script.sh
  8. 监控日志:使用plog查看状态
    # 查看连接日志
    plog
    # 或
    tail -f /var/log/ppp.log

安全断开步骤:

  1. 通知用户:断开前通知相关用户
    # 发送通知
    echo "PPP连接将在5分钟后断开进行维护" | wall
    # 或
    notify-send "网络维护" "PPP连接将断开"
  2. 检查依赖:确认没有关键服务依赖连接
    # 检查使用ppp0的连接
    sudo lsof -i @ppp0
    # 或
    sudo netstat -tunap | grep ppp0
    # 检查NFS、数据库等连接
  3. 备份路由:备份当前路由配置
    # 备份路由表
    ip route show > /tmp/routes-backup-$(date +%s).txt
    # 备份接口配置
    ip addr show ppp0 > /tmp/ppp0-backup.txt
  4. 优雅断开:先尝试正常断开
    # 正常断开
    sudo ppp-off
    # 或
    sudo poff
    # 等待完成
    sleep 5
  5. 检查状态:确认连接已断开
    # 检查接口
    ip link show ppp0
    # 应该不存在
    # 检查进程
    ps aux | grep pppd
    # 应该没有
  6. 清理资源:清理残留资源
    # 清理残留接口
    ip link delete ppp0 2>/dev/null
    # 清理路由
    ip route del default dev ppp0 2>/dev/null
    # 清理PID文件
    rm -f /var/run/ppp*.pid
  7. 记录日志:记录断开操作
    # 记录到系统日志
    logger "PPP连接已安全断开"
    # 记录到专用日志
    echo "$(date) - PPP连接断开 by $(whoami)" >> /var/log/ppp-maintenance.log
  8. 验证恢复:确保可以重新连接
    # 测试重新连接
    pon dsl-provider
    # 检查连接
    ping -c 2 8.8.8.8
    # 断开测试连接
    poff
  9. 自动化脚本:使用安全断开脚本
    #!/bin/bash
    # 安全断开PPP连接脚本
    SAFE_DISCONNECT_TIMEOUT=30
    NOTIFICATION_MESSAGE="PPP连接将在${SAFE_DISCONNECT_TIMEOUT}秒后断开"
    
    # 发送通知
    echo "$NOTIFICATION_MESSAGE" | wall
    
    # 等待通知时间
    sleep $SAFE_DISCONNECT_TIMEOUT
    
    # 执行断开
    if sudo ppp-off; then
        echo "PPP连接已安全断开" | logger -t ppp-off
        exit 0
    else
        echo "PPP连接断开失败" | logger -t ppp-off -p err
        exit 1
    fi

容器中使用ppp-off的注意事项:

  1. 权限要求:容器需要适当权限
    # Docker运行参数
    docker run --privileged --net=host ...
    # 或
    docker run --cap-add=NET_ADMIN --device=/dev/ttyS0 ...
    
    # Kubernetes Pod配置
    # securityContext:
    #   privileged: true
    # 或
    # securityContext:
    #   capabilities:
    #     add: ["NET_ADMIN"]
  2. 设备映射:映射串行设备到容器
    # Docker设备映射
    docker run --device=/dev/ttyS0:/dev/ttyS0 ...
    
    # 多个设备
    docker run \
      --device=/dev/ttyS0:/dev/ttyS0 \
      --device=/dev/ppp:/dev/ppp \
      ...
  3. 精简ppp-off脚本:创建容器专用版本
    #!/bin/bash
    # 容器专用ppp-off
    PPP_INTERFACE="${PPP_INTERFACE:-ppp0}"
    
    # 检查接口
    if ip link show "$PPP_INTERFACE" >/dev/null 2>&1; then
        echo "断开PPP连接: $PPP_INTERFACE"
    
        # 查找pppd进程
        PPPD_PID=$(pgrep -f "pppd.*$PPP_INTERFACE")
    
        if [ -n "$PPPD_PID" ]; then
            kill -TERM "$PPPD_PID"
            sleep 2
            kill -KILL "$PPPD_PID" 2>/dev/null
        fi
    
        # 清理接口
        ip link delete "$PPP_INTERFACE" 2>/dev/null
    
        echo "连接已断开"
    else
        echo "接口 $PPP_INTERFACE 不存在"
    fi
  4. 容器网络配置:正确处理网络命名空间
    # 在主机命名空间中运行
    nsenter --net=/proc/1/ns/net ppp-off
    # 或使用主机网络
    docker run --net=host ...
    
    # 在容器内操作主机网络
    ip netns exec host ppp-off
  5. 健康检查:添加容器健康检查
    # Docker健康检查
    HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
      CMD ! ip link show ppp0 || exit 1
    
    # Kubernetes存活探针
    # livenessProbe:
    #   exec:
    #     command:
    #     - sh
    #     - -c
    #     - "! ip link show ppp0"
  6. 日志处理:容器日志配置
    # 日志输出到stdout/stderr
    exec ppp-off 2>&1 | logger -t ppp-off
    
    # Docker日志驱动
    docker run --log-driver=syslog ...
    
    # 日志卷
    docker run -v /var/log/ppp:/var/log/ppp ...
  7. 容器镜像构建:Dockerfile配置
    FROM alpine:latest
    
    # 安装必要工具
    RUN apk add --no-cache ppp iproute2
    
    # 复制ppp-off脚本
    COPY ppp-off /usr/local/bin/
    RUN chmod +x /usr/local/bin/ppp-off
    
    # 设置入口点
    ENTRYPOINT ["ppp-off"]
    CMD []
  8. 编排配置:Kubernetes配置示例
    # Kubernetes Job用于断开连接
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: ppp-disconnect
    spec:
      template:
        spec:
          containers:
          - name: ppp-off
            image: my-ppp-off:latest
            securityContext:
              capabilities:
                add: ["NET_ADMIN"]
            command: ["ppp-off"]
            args: ["-a"]
          restartPolicy: Never