背景
上文讲到Ubuntu中热点遇到的一些问题,涉及到了Ubuntu环境中网络管理的各个组件,总是很模糊。本文主要通过梳理DNS管理来整理Ubuntu中网络管理组件分工,以及DNS管理细节,本文不会涉及DNS协议细节。
环境
system:Ubuntu24.04
libc: glibc2.39
Ubuntu24.04网络管理
Linux环境中有多个网络管理组件,比如Network Manager
, systemd-networkd
等,不同组件有不同的上下文概念和配置方式,这对于网络管理和配置来说很有挑战性。后面推出了netplan,作为网络管理抽象层,使用YAML文件配置,使用上面列到的组建作为渲染器,一份配置文件适配多种渲染器,目前支持两种渲染器:Network Manager
和systemd-networkd
。
Ubuntu上面默认使用前者作为网络管理工具,可以查看man 5 NetworkManager.conf
查看相关配置,配置文件位于/etc/NetworkManager/NetworkManager.conf
。
网络有很多部分,比如无线,有线,DNS, 蓝牙等,NetworkManager使用不同的软件管理这些组件。无线部分使用wpa_supplicant
(见wifi.backend
), dhcp client端也有多种选择,默认使用internal。
其中DNS配置很有意思,根据manpage
文档,DNS管理使用systemd-resolved
(/etc/resolv.conf
文件是软连接至/run/systemd/resolve/stub-resolv.conf
), 也可以设置使用dnsmasq
。
Ubuntu24.04 DNS管理
DNS基础知识 Cloudflare
DNS基础知识 ruanyifeng
resolvectl
作为systemd-resolved
的命令行管理工具管理包括DNS等。
systemd-resolved
会在本地53端口起个DNS服务,并且将nameserver 127.0.0.53
写入/etc/resolv.conf
文件,那其他软件DNS查找这个文件,都是访问本地53端口服务(这个成为stub resolver
),但是真正的DNS服务server会藏在systemd-resolved
的相关配置文件中,可以使用如下命令查看:
resolvectl status
它会列出所有网络设备的resolve信息,比如无线网卡的信息
Link 3 (wlp4s0)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.0.1
DNS Servers: 192.168.0.1
DNS Domain: lan
上述的192.168.0.1
作为DNS server一般是DHCP
分配的,局域网内部的DNS server, 会去请求ISP的DNS server(DNS的迭代模式(Iterate
)), 最后会进入递归模式,不断从ROOT(.)
, TLD nameserver(Top Level Domain)(.com)
, Authoritative nameserver(baidu.com)
获取DNS信息,返回信息有各种所谓的Record
(上述是全流程,当然可能存在缓存就不用走这些流程了,不然每次请求都消耗很多时间,另外服务器负载也太大了), 下面是常见的Record:
A - IPV4
AAAA - IPV6
NS - Authoritative Name Server
CNAME - 别名,比如www.baidu.com -> www.shifen.com
MX - Mail
TXT - Human readable document
PTR - reverse DNS lookup, 由ip查域名
查看systemd-resolved的DNS缓存
使用sudo resolvectl statistic
可以查看缓存情况,比如缓存个数,命中个数等。如果想查看缓存内容,使用sudo pkill -USR1 systemd-resolve
, 然后在syslog
中查看 sudo journalctl -u systemd-resolved > ~/resolved.txt
。注意上面打印的数据是unique的数据,记录中可能有多个相同域名指向多个IP。
清空systemd-resolved的DNS缓存
清空所有cache
sudo pkill -USR2 systemd-resolve
# OR
sudo resolvectl flush-caches
sudo resolvectl statistic
glibc的DNS服务nsswitch和ncsd
为什么提到了glibc的DNS服务呢?因为在程序中使用比如函数getaddrinfo
进行域名解析时候就使用了glibc提供的域名解析服务。下面梳理glibc的DNS相关流程。
glibc提供了各类名称解析服务,比如用户名->USER ID
, 组名->GROUP ID
, 域名->IP
等,其中使用的模块是NSS(Name Service Switch)
, 这个不要跟Firefox的NSS(Network Security Services)模块弄混淆了。
NSSwitch
可以使用getent database key
从数据库中获取NSS支持类型的存储数据, 比如
getent ahosts # get A record host
getent group # get group resolve record
man 5 nss
man 5 nsswitch.conf
getent ahost
先使用缓存没有就使用getaddrinfo开始网络请求,详情见man getent
。
nsswitch
的配置文件见/etc/nsswitch.conf
,我们关心DNS相关的是hosts
hosts: files mdns4_minimal [NOTFOUND=return] dns
其中hosts
值规定使用DNS
查询的顺序,files
代表/etc/hosts
; mdns4\_minimal
使用系统提供的multicast DNS
解析服务,如果有这个服务,调用成功但是没有我们要的结果,就返回,如果没有这个服务,就使用系统提供的DNS服务查询(/etc/resolv.conf
)。
使用nsswitch
查询DNS
整个过程,可以通过strace
查看到上述描述细节
sudo strace -e trace=open,openat,connect -f ping -c1 www.baidu.com
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libidn2.so.0", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libunistring.so.5", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY|O_CLOEXEC) = 5
# nscd缓存
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (没有那个文件或目录)
connect(5, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (没有那个文件或目录)
# 使用了getaddrinfo, 所以会调用nsswitch查询dns
openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 5
# hosts中配置files第一顺序
openat(AT_FDCWD, "/etc/host.conf", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 5
# 使用multicast DNS本地局域网所有example.local机器咨询DNS, 没有这个机器服务,下一个dns系统查询
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libnss_mdns4_minimal.so.2", O_RDONLY|O_CLOEXEC) = 5
# hosts配置中的dns, 使用系统提供的dns服务, 即systemd-resolved
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0
openat(AT_FDCWD, "/etc/gai.conf", O_RDONLY|O_CLOEXEC) = 5
...
connect(5, {sa_family=AF_INET, sin_port=htons(1025), sin_addr=inet_addr("183.2.172.42")}, 16) = 0
PING www.a.shifen.com (183.2.172.42) 56(84) bytes of data.
openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.53")}, 16) = 0
64 bytes from 183.2.172.42: icmp_seq=1 ttl=51 time=25.3 ms
--- www.a.shifen.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 25.305/25.305/25.305/0.000 ms
+++ exited with 0 +++
其中可以修改/etc/nsswitch.conf
中的hosts: file dns
就不会有multicast DNS
请求了, 修改是自动刷新的。
NOTICE:上述strace
监听openat系统调用,open可能已经没有用了
ncsd
其中nscd(Name Service Cache Daemon)
是全局各种解析服务的缓存,数据库中存储了各种解析类型映射的缓存。nscd manpage
网上看到这篇文章总结的不错。
注意,/etc/nscd.conf文件注释必须是首个字符为#,不然会服务可能启动失败。
主要提下调试,有两种方式,一种是使用日志文件,一种是在终端打印日志查看
- 使用日志文件
修改
/etc/nscd.conf
中日志相关项,logfile(/var/log/nscd.log)
和debug-file(0-5, 可以选择5)
, 然后重启nscd, 就可以查看(tail -f /var/log/nscd.log
), 当ping
或者getent hosts
就会更新日志。 - 终端查看
先暂停
sudo systemctl stop nscd
, 然后sudo nscd -g
, 就会在终端打印日志了,但是也要设置日志等级。
总结
使用getent
, ping
这些命令都会走glibc提供的DNS服务,底层使用getaddrinfo
函数; 其他命令如host(man 1 host)
, dig
, nslookup
走的是全局DNS解析服务,但是也可以自己配置DNS服务器直接网络请求。查看manpage, 会发现都是BIND 9
提供的命令,在找BIND 9
你就会发现他们为什么不走glibc的了 :)
编程语言DNS服务
目前有很多库提供DNS服务,比如libevent, c-ares。
很多语言如果底层使用使用glibc的函数也是走NSS那套,比如python,java等,但是也有的语言提供独立的DNS服务,比如自举后的golang。
NodeJS底层使用c-ares
提供DNS服务。
libcurl可以使用自己提供DNS server,但是需要编译时候添加c-ares库; 默认走glibc那套。
Nodejs
Nodejs文档真心不错,最后说明了两种方式的特点和注意点。
默认情况(使用dns.lookup()),使用getaddrinfo走系统那套查询方式; 也可以使用 dns.resolve()
, dns.resolve*()
, and dns.reverse()
,底层使用c-ares,直接网络请求DNS服务器获取解析结果。
小结
又整理了一篇无用知识,发现strace
真是个好工具,解决问题都要想到她。
Top comments (0)