Files
baim0-learn/linux/04 ifcfg备份文件被执行机制总结.md

9.8 KiB
Raw Permalink Blame History

ifcfg 备份文件会被 network 服务当配置执行(机制分析 + 源码)

  • 记录日期:2026-09-24
  • 结论:在 CentOS/RHEL 7 系(老式 initscripts)环境下,/etc/sysconfig/network-scripts/ 里面的 ifcfg 备份文件会被 network 服务枚举并真实执行,只要满足两个条件:
    1. 文件名以 ifcfg- 开头、且不以 ~ / .bak / .old / .orig / .rpmnew / .rpmorig / .rpmsave 结尾(即 ifcfg-eth0.bak0923、ifcfg-eth0.bakup0921 这类"bak+日期"命名会逃过过滤);
    2. 文件里写了 DEVICE=eth0(有它,配置就作用到真实 eth0;没有它才会被跳过)。
  • 影响:地址按"累加"方式生效、默认路由"后者覆盖前者",最终状态取决于文件执行顺序,行为不可预期。

1. 完整执行链条(4 步)

步骤 位置 行为
1. 枚举 + 过滤 network 服务脚本 ls ifcfg-*,用正则剔除"忽略文件"——只匹配结尾的 .bak/.old/...,bak+日期 命名逃过过滤
2. 取 DEVICE、查 ONBOOT network 服务脚本 从每个文件里 grep DEVICE=;DEVICE 为空才回退用文件名;ONBOOT=yes 则拉起
3. 执行 ifup network 服务脚本 ./ifup eth0.bak0923 boot
4. 作用对象由文件内 DEVICE 决定 ifup / network-functions / ifup-eth 载入备份文件全部变量后,REALDEVICE=${DEVICE%%:*} = eth0,后续加地址/装路由全部落在 eth0 上

2. 源码证据(逐段)

源码取自 fedora-sysv/initscripts(main 分支,与 CentOS/RHEL 7 系 initscripts 同源,行号可能有小差异)。 本目录 initscripts-source/ 下有 5 个文件的完整副本,可离线核对。

2.1 枚举 ifcfg-*(etc/rc.d/init.d/network,第 42-49 行)

# find all the interfaces besides loopback.
# ignore aliases, alternative configurations, and editor backup files
interfaces=$(ls ifcfg-* | \
        LC_ALL=C sed -e "$__sed_discard_ignored_files" \
               -e '/\(ifcfg-lo$\|:\|ifcfg-.*-range\)/d' \
               -e '{ s/^ifcfg-//g;s/[0-9]/ &/}' | \
        LC_ALL=C sort -k 1,1 -k 2n | \
        LC_ALL=C sed 's/ //')

注释里写明"ignore … editor backup files"——意图是忽略备份文件,但实际过滤规则有漏洞(见 2.2)。

2.2 过滤规则:只匹配"结尾"(etc/rc.d/init.d/functions,第 609-614、678-679 行)

# Copy of the function is present in usr/sbin/service
is_ignored_file() {
    case "$1" in
    *~ | *.bak | *.old | *.orig | *.rpmnew | *.rpmorig | *.rpmsave)
        return 0
        ;;
    esac
# A sed expression to filter out the files that is_ignored_file recognizes
__sed_discard_ignored_files='/\(~\|\.bak\|\.old\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d'

正则以 $ 结尾锚定:

  • ifcfg-eth0.bak → 结尾是 .bak → 被忽略(正确行为)
  • ifcfg-eth0.bak0923 → 结尾是 0923 → 不被忽略,进入后续流程
  • ifcfg-eth0.bakup0921 → 结尾是 0921 → 不被忽略
  • ifcfg-ens18.bakup0921 → 同样不被忽略(但 ens18 不存在,后续会被跳过,见 2.5)

2.3 DEVICE 取自文件内容 + ONBOOT 检查 + 拉起(etc/rc.d/init.d/network,第 91-134 行,节选)

    # bring up all other interfaces configured to come up at boot time
    for i in $interfaces; do
        unset DEVICE TYPE SLAVE NM_CONTROLLED
        eval $(LANG=C grep -F "DEVICE=" ifcfg-$i)
        eval $(LANG=C grep -F "TYPE=" ifcfg-$i)
        eval $(LANG=C grep -F "SLAVE=" ifcfg-$i)
        eval $(LANG=C grep -F "NM_CONTROLLED=" ifcfg-$i)

        if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi
        ...
        if ( . ./ifcfg-"$i" ; is_false "$ONBOOT" ) ; then
            # this loads the module, to preserve ordering
            is_available $i
            continue
        fi
        action $"Bringing up interface $i: " ./ifup $i boot
        [ $? -ne 0 ] && rc=1
    done

备份文件里 DEVICE=eth0、ONBOOT="yes" → 走到 ./ifup eth0.bak0923 boot。

2.4 作用对象 = 文件里的 DEVICE(network-scripts/ifup 第 27、53、168 行;network-scripts/network-functions 第 115-120、173-175 行)

# ifup
CONFIG=${1}
...
source_config
# network-functions
source_config ()
{
    CONFIG=${CONFIG##*/}
    DEVNAME=${CONFIG##ifcfg-}
    . /etc/sysconfig/network-scripts/$CONFIG
    [ -r "keys-$DEVNAME" ] && . /etc/sysconfig/network-scripts/keys-$DEVNAME
    ...
    [ -z "$REALDEVICE" -a -n "$PARENTDEVICE" ] && REALDEVICE=$PARENTDEVICE
    [ -z "$REALDEVICE" ] && REALDEVICE=${DEVICE%%:*}

ifup eth0.bak0923 载入的是 ifcfg-eth0.bak0923 的全部变量(DEVICE=eth0、IPADDR=…、GATEWAY=… 全部生效),然后真正的操作对象 REALDEVICE = eth0。

# ifup 末尾把控制权交给 ifup-eth
exec ${OTHERSCRIPT} ${CONFIG} $2

2.5 "真实执行"还是"被跳过"的分界(network-scripts/ifup-eth)

有 DEVICE=eth0 → 真实执行,操作落在 REALDEVICE 上(第 329 行起):

            # set lifetime of address to forever
            ip addr change ${ipaddr[$idx]}/${prefix[$idx]} dev ${REALDEVICE} valid_lft forever preferred_lft forever

没有 DEVICE= → DEVICE 回退为文件名派生的 eth0.bak0923 → 设备不存在,报错并跳过(第 132-139 行):

# now check the real state
is_available_wait ${REALDEVICE} ${DEVTIMEOUT} || {
    if [ -n "$alias" ]; then
        net_log $"$alias device ${DEVICE} does not seem to be present, delaying initialization."
    else
        net_log $"Device ${DEVICE} does not seem to be present, delaying initialization."
    fi
    exit 1
}

判读提醒:日志里看到 Bringing up interface eth0.bak0923: 不代表它没生效,要看整行有没有以 does not seem to be present, delaying initialization 收尾——有这句才是"被跳过"。

2.6 为什么"执行顺序不可预期"(network-scripts/ifup-eth 第 332-340 行)

        # Set a default route.
        if ! is_false "${DEFROUTE}" && [ -z "${GATEWAYDEV}" -o "${GATEWAYDEV}" = "${REALDEVICE}" ]; then
            # set up default gateway. replace if one already exists
            if [ -n "${GATEWAY}" ] && [ "$(ipcalc --network ${GATEWAY} ${netmask[0]} 2>/dev/null)" = "NETWORK=${NETWORK}" ]; then
                ip route replace default ... via ${GATEWAY} ... ||
                    net_log $"Error adding default gateway ${GATEWAY} for ${DEVICE}."
  • 地址:ip addr change/add → 累加(多份文件的不同地址会都加上去);
  • 默认路由:ip route replace → 后者覆盖前者;
  • 多个文件按 ls 字母序依次执行 → 最终状态取决于内容与顺序,不可预期。

3. 在机器上实锤的方法

# 1) 本次启动/重启实际拉起了哪些"接口名"
grep -i "Bringing up interface" /var/log/messages | tail -30
journalctl -b -u network --no-pager | tail -80
# 判读:出现 eth0.bak0923 等名字且整行无 "does not seem to be present" → 真实执行了

# 2) 文件差异对比(谁和谁不一样、哪个"赢了")
cd /etc/sysconfig/network-scripts
md5sum ifcfg-eth0 ifcfg-eth0.bak0923 ifcfg-eth0.bakup0921
grep -H -E "DEVICE|ONBOOT|IPADDR|PREFIX|GATEWAY" ifcfg-eth0*

4. 规避建议

  • 备份文件不要放在 /etc/sysconfig/network-scripts/ 里;非要放,至少不要以 ifcfg- 开头(例如改成 eth0_backup_0921)。
  • 如果用 ifcfg- 开头命名,只能靠"结尾"后缀才能被忽略:ifcfg-eth0.bak 可以,ifcfg-eth0.bak0923 不行。
  • 已存在的备份建议统一移走:
mkdir -p /root/backup-network-scripts
mv /etc/sysconfig/network-scripts/ifcfg-*bak* /root/backup-network-scripts/
ls /etc/sysconfig/network-scripts/ifcfg-*   # 预期只剩本体配置和 ifcfg-lo

文件命名规则速查表:

文件名 是否会被当设备配置执行
ifcfg-<设备名>(如 ifcfg-eth0) 是(设备本体配置)
ifcfg-<设备名>:<n>、ifcfg-<设备名>-range<n>、ifcfg-<设备名>.<vlan数字> 是(别名/批量别名/VLAN,有专门逻辑)
以 ~、.bak、.old、.orig、.rpmnew、.rpmorig、.rpmsave 结尾 否(被过滤规则忽略)
*.bak0923、*.bakup0921、*.bak_日期 等"bak+后缀" 是,会被枚举执行(本次踩坑点)
不以 ifcfg- 开头的任意命名 否

5. 参考资料