因为一台NUC怒学两天网络排查问题
起源
鄙人曾经认为自己不会乱花钱,第一个月工资发下来就上海鲜市场整了一台NUC,真香~
我给它装了个archlinux,由于太菜,直接用了开源的GUI Installer,我每天把它带到公司,带回家,贼喜欢。
但是我发现,在公司的时候,无论如何也没法让我的macbook ssh到arch上,它们连接了同一个wifi网络,在同一个子网下,没有ap隔离,其它的设备都能正常互相通信,只有macbook没法连上arch!但在家里一切都是好的!
而且更魔幻的是,一般在arch开机几十分钟后,macbook就可以正常连接arch!
问题到这已经有点玄了,实际上从效率角度考虑直接重装一个ubuntu就完事了,但是想到arch社区里的那些geek们怼天怼地的样子,这样就退出arch阵营岂不是显得自己很垃圾!所以我就开始找一些网络书看,试图找出原因。就是以下两本:
- 计算机网络:自顶向下方法
- TCP/IP卷一:协议
今天终于发现点眉目,记录一下
工作了是真没精力更新博客,哎哎哎~
局域网互联过程简述
在我早先的视角里,一个局域网之间的两台设备,通过一个ip就能直接互相访问,这对我来说非常自然,我也从来不知道这里面的细节,甚至之前读计算机网络自顶向下方法的时候也就读到网络层就完事了,再下面我是完全没了解过。
下面我会尝试先阐述一些网络知识,我的措辞不一定准确毕竟我是个网络菜鸡,哦不,全方面菜鸡。
广播式网络的链路层通信
TCP/IP的链路层使用硬件地址通信,我们熟知的MAC地址就是硬件地址的一种形式。后面我们直接用MAC地址来阐述。
我所在的WIFI网络就是一种广播式网络,之前的那种用一条线把很多设备连在一起的也叫广播式网络,这种网络的特性是你发送一条数据,网络中的其它设备不可避免地也会接收到,尽管你不是发给它的,而发给谁就用链路层头中的目标地址标识,硬件或驱动程序发现该数据包不是发给自己的就会主动丢弃该数据包,如果发现是发给自己的就会拆解,向上递交给IP层。
ARP
我们假设archlinux的IP地址是192.168.1.103,并且下文中我们会一直使用该IP,我们的目的是用一台处在同一局域网(192.168.1.0/24)的其它设备ssh连接它
考虑我们常见的场景,archlinux刚刚接入到网络中,我们知道它的ip地址是192.168.1.103,然后我打算通过我的另一台电脑ssh到它:
~# ssh root@192.168.1.103
传输层会把端口号22封装到传输层头,IP层会把192.168.1.103封装到IP层头中,但这俩东西对于链路层来说狗屁不是,按照分层原则,链路层甚至不应该知道它们的存在(但这只是理想情况下)。链路层通信需要的是MAC地址,而我们根本不知道archlinux的链路层地址!链路层根本没办法指定archlinux接收这个消息啊!
但是刚刚说过,对于广播式局域网,你发一个数据包,无论目的MAC是啥,网络中的所有设备都能接到,所以我们好像是可以实现发一个数据包问问网络中哪一个设备拥有192.168.1.103这个IP地址,如果它有,就返回自己的MAC地址,这样我们就知道192.168.1.103对应的MAC地址是啥了。这东西就叫ARP(地址解析协议)。
通过WireShark抓包,在SSH时确实抓到了几个ARP协议的数据包,第一个数据包在问,谁有192.168.1.103这个IP地址,如果有的话,告诉我。下面两个数据包是两个回复,返回了两个MAC地址
这里先不考虑为啥返回两个的问题
可以看到,ARP协议发送的目的MAC地址是ff:ff:ff:ff:ff:ff
,这是一个链路层的广播地址,广播式局域网中,设备不会过滤掉该消息,虽然这个MAC地址不等于自己的MAC地址
解决问题
为什么有两个MAC地址宣称它们has对应的IP
从上面抓到的数据包中可以看到有两个MAC地址都说自己拥有192.168.1.103这个IP,我们知道一个计算机可能有多个网络接口,每一个网络接口有一个MAC地址并连接到一个网络,而且至少一般来说,每一个网络接口都拥有一个独立的IP地址,所以按理说不应该存在像上面这种有两个MAC地址说它拥有192.168.1.103的情况。
根据ifconfig
的返回,我知道了这个9c什么的MAC地址来自于wlan0这张网卡,它的IP也不是103啊,1c开头的这个有线连接的MAC地址才是对应的103:
eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.103 netmask 255.255.255.0 broadcast 192.168.1.255
ether 1c:69:7a:a2:cd:b6 txqueuelen 1000 (Ethernet)
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.85 netmask 255.255.255.0 broadcast 192.168.1.255
ether 96:b1:6a:23:0e:99 txqueuelen 1000 (Ethernet)
而且操蛋的是,在我使用的电脑(要连接到arch的那台电脑)上,9c这个wlan0网卡的mac地址已经被绑定到了192.168.1.103这个有线连接的IP上:
C:\Users\15941>arp -a
Interface: 192.168.1.125 --- 0x5
Internet Address Physical Address Type
192.168.1.1 18-7c-aa-f1-d1-01 dynamic
192.168.1.103 96-b1-6a-23-0e-99 dynamic
卧槽!!!这不是瞎几把弄吗?
就在我刚被这个问题攻击尚处于眩晕状态的时候,我又发现了更操蛋的事,我在家里也连不上192.168.1.103这个IP了!症状和在公司一样,我一直以为这个病只会在公司出现呢。
不过操蛋归操蛋,我还是很兴奋的,因为我感觉我快接近正确答案了!
wlan0的MAC地址居然会变!?
分析一下,无法给192.168.1.103发消息,问题不可能在IP层,只可能出现在arp缓存表里的mac地址有问题,我到arch里看了一眼,果然,吐血了!
4: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 12:db:7f:b1:c3:19 brd ff:ff:ff:ff:ff:ff permaddr 2c:db:07:28:58:8e
...
网卡wlan0的MAC地址已经变成了12:db:7f:b1:c3:19
,而我的另一台电脑的arp缓存表里还用的96
开头的老MAC地址发送数据,自然,网络中所有设备都接到该数据,但没人会接,因为没人的MAC地址是96那一串。
这已经打破我的认知了,MAC地址竟然会变化!直觉告诉我wlan0这张网卡是虚拟的,所以它才可能MAC地址变化。
所以archlinux无法访问的问题原因到这里应该就找到了,不是只有在公司才会发生,而是纯纯的偶然,上面这种情况在公司每次都发生了。
- 当我发出ARP,寻找192.168.1.103的MAC地址时,wlan0欠儿欠儿的告诉我,它拥有这个地址
- 我误打误撞的在wlan0和eno1的返回中,选择了wlan0的MAC地址
- wlan0的MAC地址动态变更,被我记录在arp缓存表里的MAC地址无效了
- 几十分钟后,我的arp缓存表失效,重新发送arp,这次有可能误打误撞我选择了eno1的MAC,或者wlan0尚未动态更换MAC
iwd快滚!!
我不断地尝试在Google上构造关键词,interface、wlan0、MAC address、dynamically change...终于找到了几篇文章:
- https://iwd.wiki.kernel.org/addressrandomization
- https://insanity.industries/post/random-mac-addresses-for-privacy/
- https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1007097
原来是iwd在默认情况下(一个命令行控制wifi连接的程序)给我虚拟了一张wlan网卡,并定时的动态更换我的MAC地址。而且我发现我arch里本来有一个nmcli
就可以控制wifi连接,根本没必要用iwd。
我大概知道了,我用的第三方archlinux图形界面安装程序给我装了些自动的网络管理工具,但我是个newbie,我根本不懂,自己又装了一些,后面还有网络管理工具之间的冲突造成的问题。
所以我卸载了iwd并重启,wlan0网卡已经变回了我原先的无线网卡wlp0s20f3
,MAC地址也正常了,不会随机改变了。
2: wlp0s20f3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 2c:db:07:28:58:8e brd ff:ff:ff:ff:ff:ff
现在再看ARP,虽然2c这个MAC地址也会回复它拥有192.168.1.103,但是MAC地址失效的问题不会出现了
当我想要用主机名连接的时候,又有骚问题...
我肯定不想每次都用IP地址连接啊,而且IP地址是DHCP的,下次就变动了,我肯定想用我arch的主机名——yudoge-arch
来连接!
在没有DNS服务器的局域网中,mDNS协议可以利用组播来交换主机名
我用主机名顺利的连接到arch上,但我感觉这个延迟...不像有线连接,而是像WIFI连接,即使我确定使用的MAC地址是有线连接的。
抓包看下mDNS协议相关的数据包:
192.168.1.103和192.168.1.104都说它们是yudoge-arch
,可是我的印象里根本没有104这个IP啊...
而且ping
的结果显示,103的延迟基本只有1到2ms,是正常的有线连接延迟,而104的延迟飙到了100ms左右:
C:\Users\15941>ping 192.168.1.103
Pinging 192.168.1.103 with 32 bytes of data:
Reply from 192.168.1.103: bytes=32 time=1ms TTL=64
Reply from 192.168.1.103: bytes=32 time=2ms TTL=64
C:\Users\15941>ping 192.168.1.104
Pinging 192.168.1.104 with 32 bytes of data:
Reply from 192.168.1.104: bytes=32 time=94ms TTL=64
Reply from 192.168.1.104: bytes=32 time=106ms TTL=64
我进到arch里,用ip addr
看了一眼,我靠,确实,每一个网卡都有两个IP:
有了之前的经验,我大概猜到了是我自己装了什么网络管理服务和自带的冲突了,我目前用的是NetworkManager
,好在Arch的wiki中有提到可以用systemctl --type=service
查看一下现在运行的服务
果然除了NetworkManager
还有另外一个systemd-networkd
服务,我把它disable了
➜ ~ sudo systemctl disable systemd-networkd
Removed "/etc/systemd/system/sockets.target.wants/systemd-networkd.socket".
Removed "/etc/systemd/system/dbus-org.freedesktop.network1.service".
Removed "/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service".
Removed "/etc/systemd/system/sysinit.target.wants/systemd-network-generator.service".
Removed "/etc/systemd/system/multi-user.target.wants/systemd-networkd.service".
重启!!
现在只有103会回复自己是yudoge-arch
,并且它会在数据包里携带自己的MAC地址,这就避免了我arp之后两张网卡都回复,然后绑错网卡的情况:
ip addr
显示只有一个IP地址了:
总结
本篇文章解决了两个奇怪的网络问题:
- 绑定好的MAC地址无效了导致局域网内无法通信
- 一个网络接口有多个IP地址,导致mDNS时把主机名解析成了预期之外的IP
这两个问题的成因都是因为对archlinux甚至对整个linux体系的不了解,瞎装了其它的网络管理工具造成的,所幸这两个问题大概是把我引入了使用archlinux的门槛,我已经懂得利用archlinux最成熟最牛逼的wiki体系了。
网络是一个很复杂的东西,概念很多,而且偏向实践和工程,我仍有很多细节不了解,所以本文中的很多用词可能不准确,欢迎批评指正。