Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ UI 修改必须横向检查,而不是只改一个应用。典型关联范围

### 网络与 TLS

- 网络栈当前主要服务 e1000;QEMU/VMware 的网卡、DHCP、DNS、TCP 和显示/
- 网络栈通过逐设备接口同时服务 e1000、e1000e 和 Realtek;QEMU/VMware 的网卡、DHCP、DNS、TCP 和显示/
驱动行为并不完全相同。报告时必须说明实际运行平台。
- TLS 故障必须区分 DNS、TCP 连接、握手、证书链、主机名、时间、发送与读取。
日志可输出主机名、阶段、返回码、网络状态、证书校验标志和时钟状态;绝不
Expand Down
44 changes: 26 additions & 18 deletions Documentation/admin-guide/network-controller.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@ TCP 连接建立不代表 TLS 校验或目标应用健康;DNS、端口与 Ping
配置与迁移
==========

持久化编辑明确限定当前内核支持的 eth0,其他可查询接口(若有)只读显示。
没有 IPv4 地址或处于禁用状态的网卡也按实际接口索引查询。支持自动
左侧选择实际枚举的以太网接口并分别编辑;切换网卡前会提示丢弃未保存的修改。
没有 IPv4 地址或处于禁用状态的网卡也会显示。支持自动
DHCP、静态 IPv4 和禁用;静态模式可以不设置默认网关,DNS 必须明确选择
自定义或 Cloudflare。自定义 DNS 最多三个 IPv4 地址,以空格分隔。

配置位于 ``/etc/reliefos/network.conf``,是版本化数据,不作为 shell 脚本执行。
静态地址、前缀、网关和 DNS 在 GUI、工作进程及启动链分别验证。文件目录和
配置必须由 root 所有,不能由组或其他用户写入,不能使用符号链接替代配置文件。
保存检查外部变动,并通过临时文件、同步与 rename 原子替换;旧内容保存在
``network.conf.bak``。“恢复上次配置”也检查当前快照,不能绕过冲突检查。
eth0 继续使用 ``/etc/reliefos/network.conf``;其他接口使用
``/etc/reliefos/network.d/<接口名>.conf``。未设置的接口默认使用 DHCP。
配置是版本化数据,不作为 shell 脚本执行。静态地址、前缀、网关和 DNS 在 GUI、
工作进程及启动链分别验证,接口名必须对应已枚举的 ``eth`` 加十进制编号。
文件目录和配置必须由 root 所有,不能由组或其他用户写入,不能使用符号链接
替代配置文件或目录。保存检查外部变动,并通过临时文件、同步与 rename 原子
替换;各接口各自保留 ``.bak`` 备份。“恢复上次配置”也检查当前快照,不能
绕过冲突检查。
原先没有配置文件的状态可以恢复为缺省状态。

旧的 DNS-only ``network.conf`` 保持可读;旧 udhcpc DNS 策略在迁移前继续生效。
Expand All @@ -42,24 +45,29 @@ DHCP、静态 IPv4 和禁用;静态模式可以不设置默认网关,DNS 必
编辑 profile 后重启网络服务调整策略;不会在解析器忽略旧文件时谎报设置成功。
未迁移的旧配置继续支持旧 API。

仍由 ``reliefos-dhcp`` OpenRC 服务拥有网络生命周期。DHCP 保留原有监督器,
静态/禁用执行一次性设置;停止服务依据启动时记录的实际模式,而不是刚保存的
新模式,避免漏停旧 DHCP 进程。客户端写入 root 所有的 PID 文件,停止时校验
该记录及实际命令行,通过 OpenRC ``start-stop-daemon`` 限时等待客户端退出,
然后清除租约与旧地址,避免残留客户端覆盖静态配置。
``network-profile`` 工具独立于可选 GUI,
因此关闭 GUI 组件不影响基础网络启动。解析器仍由唯一的 ``publish-resolver``
发布,DHCP 租约不会覆盖显式 DNS 策略。
``reliefos-dhcp`` OpenRC 服务启动时从 ``/proc/net/dev`` 枚举全部以太网卡,
逐卡设置静态/禁用状态或启动各自的 ``supervise-daemon`` 和 DHCP 客户端。
停止或应用某卡时先停其监督器,再校验客户端 PID 及接口参数并等待退出,
然后仅清理该卡地址、默认路由和租约;不会根据刚保存的新模式漏停旧客户端。
eth0 保留原有 ``/run/reliefos/udhcpc.pid``、``dhcp-lease`` 和 ``dhcp-dns``
路径,其他接口的运行状态带接口名后缀。``network-profile`` 独立于可选 GUI,
关闭 GUI 组件不影响基础网络启动。

DNS 解析器由 ``publish-resolver`` 全系统共享并串行发布。先按内核枚举顺序
收集未禁用且未停止接口的显式自定义/Cloudflare 服务器,再收集 eth0 的自动 RDNSS
(仅 eth0 为自动 DNS 时),最后按同一顺序收集自动 DHCP 租约;去重后发布最多
三个地址,多余地址保留在各自租约缓存中。某接口的释放、续租或停用不会删除
其他接口的 DNS 状态。单独 eth0 的旧 DNS-only 和固定 DNS 行为保持兼容。

“仅保存”更改持久化策略,供随后服务重启、DHCP 更新或下次启动读取;
“保存并应用”同时重启现有网络服务,可能中断连接。应用失败时已保存的文件
“保存并应用”只重新配置选中的接口,可能中断该接口的连接;如果整个服务
已停止,则由 OpenRC 启动服务并配置全部接口。应用失败时已保存的文件
仍保留,程序显示失败和输出,可修正或恢复备份;不能将保存成功等同于联网成功。

边界
====

当前 GUI 不提供 IPv6 持久化配置;系统的 IPv6/SLAAC、RDNSS 与命令行配置见
:doc:`ipv6`。此 GUI 也不提供 Wi-Fi、VPN、桥接、VLAN、任意静态路由管理或改变内核固定的
MTU。当前内核未导出 ARP 表和全网卡计数,不能将单条 TCP 连接的字节计数冒充
网卡总流量。不支持的能力不会显示成已生效。以太网链路能力取决于当前内核和驱动。
MTU。不能将单条 TCP 连接的字节计数冒充网卡总流量。不支持的能力不会显示成已生效。以太网链路能力取决于当前内核和驱动。
QEMU 运行验证与 VMware 实机验证分别记录;VMware 验证未完成。
2 changes: 1 addition & 1 deletion kernel/reliefnt
Submodule reliefnt updated 70 files
+4 −4 Documentation/core-api/drivers.rst
+4 −5 Documentation/core-api/e1000e.rst
+1 −0 Documentation/core-api/index.rst
+83 −0 Documentation/core-api/network-devices.rst
+4 −4 Documentation/core-api/realtek.rst
+264 −284 drivers/net/e1000/e1000_main.c
+3 −6 drivers/net/e1000e/hardware.c
+3 −2 drivers/net/e1000e/internal.h
+106 −68 drivers/net/e1000e/main.c
+3 −11 drivers/net/r8169/hardware.c
+3 −2 drivers/net/r8169/internal.h
+109 −97 drivers/net/r8169/main.c
+17 −7 include/reliefnt/e1000.h
+31 −3 include/reliefnt/net.h
+73 −0 include/reliefnt/net_device.h
+76 −0 include/reliefnt/net_interface.h
+20 −4 include/reliefnt/net_ipv6.h
+9 −0 include/reliefnt/net_packet.h
+11 −0 include/reliefnt/net_udp.h
+2 −0 include/uapi/linux/if.h
+1 −0 include/uapi/linux/rtnetlink.h
+13 −3 kernel/device/manager_internal.h
+6 −5 kernel/device/registry.c
+262 −136 kernel/device/services.c
+9 −0 kernel/syscall/ipc/socket.c
+59 −8 net/core.c
+19 −12 net/input.c
+1 −1 net/interface/Kbuild
+11 −175 net/interface/config.c
+258 −0 net/interface/ioctl.c
+186 −0 net/interface/routes.c
+54 −0 net/interface/socket.c
+159 −0 net/interface/state.c
+72 −2 net/internal.h
+32 −9 net/ipv4/core.c
+57 −14 net/ipv4/udp.c
+290 −203 net/ipv6/core.c
+112 −190 net/link/arp.c
+17 −4 net/link/ethernet.c
+26 −24 net/proc.c
+275 −53 net/socket/netlink.c
+64 −20 net/socket/packet.c
+62 −38 net/socket/udp.c
+25 −13 net/tcp/input.c
+52 −11 net/tcp/output.c
+67 −15 net/tcp/socket.c
+19 −0 tools/test_net_device.py
+16 −0 tools/test_net_multi_interface.py
+26 −0 tools/test_net_multinic_data.py
+4 −0 tools/tests/driver_modules_test.c
+74 −23 tools/tests/e1000_init_test.c
+78 −35 tools/tests/e1000e_test.c
+3 −0 tools/tests/host-suites.list
+17 −2 tools/tests/ipv6_control_test.c
+309 −4 tools/tests/ipv6_protocol_test.c
+23 −2 tools/tests/ipv6_tcp_test.c
+11 −0 tools/tests/ipv6_transport_test.c
+2 −0 tools/tests/module_legacy_audio_owner_test.c
+8 −0 tools/tests/modules/network.h
+137 −0 tools/tests/multinic_guest.c
+89 −0 tools/tests/net_device_test.c
+19 −4 tools/tests/net_interface_test.c
+197 −0 tools/tests/net_multi_interface_test.c
+97 −0 tools/tests/net_multinic_data_test.c
+45 −0 tools/tests/net_packet_test.c
+53 −15 tools/tests/r8169_test.c
+18 −0 tools/tests/task8_host_stubs/net-ioctl-user-candidate-3-net-ioctl-user-candidate-3.stub.c
+3 −1 tools/tests/task8_net_ioctl_user_test.c
+161 −2 tools/tests/tcp_state_test.c
+51 −0 tools/tests/udp_socket_test.c
58 changes: 21 additions & 37 deletions system/rootfs/etc/init.d/reliefos-dhcp
Original file line number Diff line number Diff line change
@@ -1,45 +1,29 @@
#!/sbin/openrc-run
description="Ethernet configuration and BusyBox DHCP client"
supervisor=supervise-daemon
retry=TERM/1/KILL/1
input_file=/dev/null
command=/sbin/udhcpc
command_args="-f -R -i eth0 -p /run/reliefos/udhcpc.pid -s /usr/share/udhcpc/default.script -t 3 -T 1 -A 30"
respawn_delay=5
respawn_max=3
respawn_period=60
output_log=/var/log/udhcpc.log
error_log=/var/log/udhcpc.log
description="Ethernet configuration and BusyBox DHCP clients"
depend() { need reliefos-runtime reliefos-modules; provide net; }

start_pre() {
/usr/lib/reliefos/network-profile prepare
}

start() {
local mode
mode=$(/usr/lib/reliefos/network-profile mode) || return 1
if [ "$mode" = dhcp ]; then
default_start || return $?
else
ebegin "Applying $mode Ethernet configuration"
eend 0
fi
service_set_value network_profile_mode "$mode"
local iface started interfaces
started=""
interfaces=$(/usr/lib/reliefos/network-profile list) || return 1
for iface in $interfaces; do
if ! /usr/lib/reliefos/network-profile start "$iface"; then
/usr/lib/reliefos/network-profile stop "$iface" || :
for iface in $started; do
/usr/lib/reliefos/network-profile stop "$iface" || :
done
return 1
fi
started="$started $iface"
done
}

stop() {
local mode
mode=$(service_get_value network_profile_mode)
# A service started before profile support used the DHCP supervisor.
if [ -z "$mode" ] || [ "$mode" = dhcp ]; then
default_stop || return $?
# Finish the DHCP client and its deconfiguration hook before a
# static address is installed, even if the supervisor exits early.
/usr/lib/reliefos/network-profile stop-dhcp || return $?
fi
}

stop_post() {
/usr/lib/reliefos/network-profile release
local iface result interfaces
result=0
interfaces=$(/usr/lib/reliefos/network-profile list) || return 1
for iface in $interfaces; do
/usr/lib/reliefos/network-profile stop "$iface" || result=1
done
return "$result"
}
106 changes: 92 additions & 14 deletions system/rootfs/usr/lib/reliefos/network-profile
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,44 @@ ip_number() (
printf '%s\n' "$((($1 << 24) | ($2 << 16) | ($3 << 8) | $4))"
)
print_ip() { printf '%s.%s.%s.%s\n' "$((($1 >> 24) & 255))" "$((($1 >> 16) & 255))" "$((($1 >> 8) & 255))" "$(($1 & 255))"; }
# Kernel Ethernet devices are named eth followed by their stable index.
valid_interface() {
case "$1" in eth*) number=${1#eth} ;; *) return 1 ;; esac
case "$number" in ''|*[!0-9]*|0[0-9]*) return 1 ;; esac
test "${#1}" -le 15
}
interfaces() {
test -r /proc/net/dev || fail 'cannot enumerate network interfaces'
while IFS= read -r line; do
case "$line" in *:*) name=${line%%:*} ;; *) continue ;; esac
while :; do case "$name" in ' '*) name=${name# } ;; *) break ;; esac; done
if valid_interface "$name"; then printf '%s\n' "$name"; fi
done < /proc/net/dev
}
if test "${1:-}" = list; then interfaces; exit; fi
iface=${2:-eth0}
valid_interface "$iface" || fail 'invalid Ethernet interface'
case "${1:-}" in
start|stop|apply)
mkdir -p /run/reliefos
exec 8>"/run/reliefos/interface.$iface.lock"
flock -x 8
;;
esac
found=no
for name in $(interfaces); do test "$name" != "$iface" || found=yes; done
test "$found" = yes || fail 'Ethernet interface is not present'
test "${1:-}" != interface || exit 0
case "$iface" in
eth0) client=/run/reliefos/udhcpc.pid; lease=/run/reliefos/dhcp-lease; dns_file=/run/reliefos/dhcp-dns ;;
*) client=/run/reliefos/udhcpc.$iface.pid; lease=/run/reliefos/dhcp-lease.$iface; dns_file=/run/reliefos/dhcp-dns.$iface ;;
esac
clear_lease() {
/sbin/route del default dev eth0 2>/dev/null || :
/sbin/ifconfig eth0 0.0.0.0
rm -f /run/reliefos/dhcp-lease /run/reliefos/dhcp-dns
/sbin/route del default dev "$iface" 2>/dev/null || :
/sbin/ifconfig "$iface" 0.0.0.0
rm -f "$lease" "$dns_file"
}
stop_dhcp() {
client=/run/reliefos/udhcpc.pid
if test ! -e "$client" && test ! -L "$client"; then return 0; fi
test -f "$client" && test ! -L "$client" || fail 'unsafe DHCP PID file'
test "$(stat -c %u "$client")" = 0 || fail 'DHCP PID file must belong to root'
Expand All @@ -33,26 +64,48 @@ stop_dhcp() {
case "$pid" in ''|*[!0-9]*) fail 'invalid DHCP PID' ;; esac
test -z "$extra" && test "${#pid}" -le 10 && test "$pid" -gt 1 && test "$pid" -le 2147483647 || fail 'invalid DHCP PID'
if test ! -d "/proc/$pid"; then rm -f "$client"; return 0; fi
# Match the recorded service client, including BusyBox multicall argv.
# Refuse an unrelated process if the recorded PID has been reused.
# Require both the DHCP executable and its precise -i argument: a PID
# reused by another interface must never terminate that interface.
command=$(tr '\000' '\n' < "/proc/$pid/cmdline")
newline='
'
case "$command" in
/sbin/udhcpc"$newline"*|udhcpc"$newline"*|/bin/busybox"$newline"udhcpc"$newline"*) ;;
*) fail 'PID no longer identifies the DHCP client' ;;
esac
case "$newline$command$newline" in *"$newline-i$newline$iface$newline"*) ;; *) fail 'DHCP PID belongs to another interface' ;; esac
/sbin/start-stop-daemon --stop --retry TERM/10/KILL/2 --pidfile "$client" || return $?
rm -f "$client"
}
stop_interface() {
# Stop the supervisor before its child so it cannot respawn during apply.
if test "$iface" = eth0; then
RC_SVCNAME=reliefos-dhcp /sbin/supervise-daemon reliefos-dhcp --stop 8>&- || return $?
fi
RC_SVCNAME=reliefos-dhcp.$iface /sbin/supervise-daemon "reliefos-dhcp.$iface" --stop 8>&- || return $?
stop_dhcp
clear_lease
: > "/run/reliefos/network-stopped.$iface"
/usr/lib/reliefos/publish-resolver
}
case "${1:-}" in
stop-dhcp) stop_dhcp; exit ;;
release) clear_lease; /usr/lib/reliefos/publish-resolver; exit ;;
mode|dns|prepare|check) ;;
*) fail 'expected mode, dns, prepare, check, release or stop-dhcp' ;;
stop) stop_interface; exit ;;
mode|dns|prepare|check|start|apply) ;;
*) fail 'expected list, mode, dns, prepare, check, start, stop, apply, release or stop-dhcp' ;;
esac
mode=dhcp address=0.0.0.0 prefix=24 gateway=0.0.0.0 dns_mode=automatic dns_servers= dns_custom=
profile=/etc/reliefos/network.conf
case "$iface" in eth0) profile=/etc/reliefos/network.conf ;; *) profile=/etc/reliefos/network.d/$iface.conf ;; esac
# Reject a replaced directory before touching any per-interface profile.
for dir in /etc/reliefos /etc/reliefos/network.d; do
if test -e "$dir" || test -L "$dir"; then
test -d "$dir" && test ! -L "$dir" || fail 'unsafe profile directory'
test "$(stat -c %u "$dir")" = 0 || fail 'profile directory must belong to root'
permissions=$(stat -c %a "$dir")
test "$((0$permissions & 022))" -eq 0 || fail 'profile directory is writable by another user'
fi
done
if test -e "$profile" || test -L "$profile"; then
test -f "$profile" && test ! -L "$profile" || fail 'profile is not a regular file'
test "$(stat -c %u "$profile")" = 0 || fail 'profile must belong to root'
Expand Down Expand Up @@ -114,6 +167,14 @@ if test -e "$profile" || test -L "$profile"; then
fi
;;
esac
elif test "$iface" = eth0 && test -r /etc/udhcpc/leonos-dns; then
read -r previous_mode previous_servers < /etc/udhcpc/leonos-dns
case "$previous_mode" in
0) dns_mode=cloudflare ;;
1) dns_mode=automatic ;;
2) dns_mode=custom; dns_servers=$previous_servers ;;
*) fail 'invalid published DNS mode' ;;
esac
fi
case "$mode" in dhcp|static|disabled) ;; *) fail 'invalid mode' ;; esac
case "$dns_mode" in automatic|custom|cloudflare) ;; *) fail 'invalid DNS mode' ;; esac
Expand Down Expand Up @@ -156,17 +217,34 @@ case "$1" in
dns)
case "$dns_mode" in automatic) printf '1 0.0.0.0\n' ;; cloudflare) printf '0 0.0.0.0\n' ;; custom) printf '2 %s\n' "$dns_servers" ;; esac
;;
prepare)
prepare|start|apply)
if test "$1" = apply; then
# Preserve OpenRC service ownership if the service was stopped.
# Release the per-interface lock before OpenRC starts every NIC.
if ! /sbin/rc-service reliefos-dhcp status >/dev/null 2>&1; then
exec 8>&-
exec /sbin/rc-service reliefos-dhcp start
fi
stop_interface
fi
mkdir -p /run/reliefos
clear_lease
rm -f "/run/reliefos/network-stopped.$iface"
case "$mode" in
dhcp) /sbin/ifconfig eth0 up ;;
dhcp) /sbin/ifconfig "$iface" up ;;
static)
/sbin/ifconfig eth0 "$address" netmask "$(print_ip "$mask")" up
test "$gw" -eq 0 || /sbin/route add default gw "$gateway" dev eth0
/sbin/ifconfig "$iface" "$address" netmask "$(print_ip "$mask")" up
test "$gw" -eq 0 || /sbin/route add default gw "$gateway" dev "$iface"
;;
disabled) /sbin/ifconfig eth0 down ;;
disabled) /sbin/ifconfig "$iface" down ;;
esac
/usr/lib/reliefos/publish-resolver
if test "$1" != prepare && test "$mode" = dhcp; then
RC_SVCNAME=reliefos-dhcp.$iface /sbin/supervise-daemon "reliefos-dhcp.$iface" --start \
--retry TERM/1/KILL/1 --stdin /dev/null --stdout /var/log/udhcpc.log \
--stderr /var/log/udhcpc.log --respawn-delay 5 --respawn-max 3 --respawn-period 60 \
/sbin/udhcpc -- -f -R -i "$iface" -p "$client" \
-s /usr/share/udhcpc/default.script -t 3 -T 1 -A 30 8>&-
fi
;;
esac
66 changes: 47 additions & 19 deletions system/rootfs/usr/lib/reliefos/publish-resolver
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
# Sole resolver writer. Inputs are numeric data, never sourced shell text.
set -eu
valid_ipv4() (
# A subshell keeps field splitting local to validation. Reject empty
# octets before splitting, including a trailing dot discarded by the shell.
case "$1" in *[!0-9.]*|''|.*|*.|*..*) return 1 ;; esac
IFS=.
set -- $1
Expand All @@ -12,23 +10,54 @@ valid_ipv4() (
test "${#octet}" -le 3 && test "$octet" -le 255 || return 1
done
)
mode=1
custom=0.0.0.0
if test -e /etc/reliefos/network.conf || test -L /etc/reliefos/network.conf; then
settings=$(/usr/lib/reliefos/network-profile dns)
read -r mode custom <<EOF
# Every writer publishes after updating its own cache. Serializing the read
# and rename prevents an older callback from replacing a newer combined view.
mkdir -p /run/reliefos
exec 9>/run/reliefos/resolver.lock
flock -x 9
explicit= automatic= ra=no
if test -x /usr/lib/reliefos/network-profile && test -r /proc/net/dev; then
interfaces=$(/usr/lib/reliefos/network-profile list)
for iface in $interfaces; do
if test -e "/run/reliefos/network-stopped.$iface"; then continue; fi
state=$(/usr/lib/reliefos/network-profile mode "$iface")
test "$state" != disabled || continue
settings=$(/usr/lib/reliefos/network-profile dns "$iface")
read -r mode custom <<SETTINGS
$settings
EOF
elif test -r /etc/udhcpc/leonos-dns; then
read -r mode custom < /etc/udhcpc/leonos-dns
SETTINGS
case "$mode" in
0) explicit="$explicit 1.1.1.1" ;;
2) explicit="$explicit $custom" ;;
1)
test "$iface" != eth0 || ra=yes
case "$iface" in eth0) cache=/run/reliefos/dhcp-dns ;; *) cache=/run/reliefos/dhcp-dns.$iface ;; esac
automatic="$automatic $(cat "$cache" 2>/dev/null || :)"
;;
*) exit 1 ;;
esac
done
else
# Compatibility with the old DNS-only configuration and standalone hook.
mode=1 custom=0.0.0.0
if test -e /etc/reliefos/network.conf || test -L /etc/reliefos/network.conf; then
settings=$(/usr/lib/reliefos/network-profile dns)
read -r mode custom <<SETTINGS
$settings
SETTINGS
elif test -r /etc/udhcpc/leonos-dns; then
read -r mode custom < /etc/udhcpc/leonos-dns
fi
case "$mode" in
0) explicit=1.1.1.1 ;;
1) ra=yes; automatic=$(cat /run/reliefos/dhcp-dns 2>/dev/null || :) ;;
2) explicit=$custom ;;
*) exit 1 ;;
esac
fi
case "$mode" in
1) servers="$(cat /run/reliefos/ra-dns 2>/dev/null || :)
$(cat /run/reliefos/dhcp-dns 2>/dev/null || :)" ;;
0) servers=1.1.1.1 ;;
2) servers=$custom ;;
*) exit 1 ;;
esac
servers=$explicit
if test "$ra" = yes; then servers="$servers $(cat /run/reliefos/ra-dns 2>/dev/null || :)"; fi
servers="$servers $automatic"
umask 022
tmp=$(mktemp /etc/.resolv.conf.XXXXXX)
trap 'rm -f "$tmp"' EXIT HUP INT TERM
Expand All @@ -42,8 +71,7 @@ for server in $servers; do
esac
case "$seen" in *" $server "*) continue ;; esac
seen="$seen$server "
# musl consumes at most three nameservers. Keep additional advertised
# entries in their source cache so expiry can expose the next server.
# musl consumes at most three nameservers. Keep remaining source caches.
test "$count" -lt 3 || continue
printf 'nameserver %s\n' "$server" >> "$tmp"
count=$((count + 1))
Expand Down
Loading
Loading