简介:网络剪刀手(NetCut)是一款面向局域网管理场景的网络控制工具,适合网络管理员、小型办公用户及教育场景中的教师了解和使用。它围绕设备识别、连接断开、保护模式与网络诊断等功能展开,可帮助读者理解如何借助ARP机制管理共享网络中的带宽占用与异常接入问题。压缩包共2个文件,包含1个txt与1个html,整体约3KB,前者侧重软件介绍与功能说明,后者提供网页形式的资料页面,便于快速浏览核心信息。已有211人学习或下载。资料对NetCut的工作原理、主要功能、典型应用场景及安全与伦理边界均有涉及,读者可据此建立对局域网控制工具的基本认识,理解ARP伪造与流量重定向的风险,并在合法合规前提下评估其实际用途,可作为了解局域网控制工具边界与ARP相关知识的入门参考。
1. 网络剪刀手到底在做什么:一条 ARP 缓存记录就能让同网段的人掉线
网络剪刀手这类工具在局域网里只做一件事:往同网段发伪造的 ARP 应答,把「网关 IP 对应哪个 MAC」这条缓存改掉。改完,受害主机的流量要么被引到别处,要么找不到出口,表现就是网页转圈、游戏掉线、内网互 ping 不通。它不靠什么 0day,利用的是 ARP 无认证、无状态的设计缺口。手里管着机房、宿舍网、小型办公网的人最该看:先弄清自己网段有没有被一刀剪断的风险,再谈别的。把它当「神器」之前,先看清边界。
2. ARP 与默认网关:一条缓存记录凭什么决定你的网络生死
很多人第一次听说这类工具的反应是「不可能吧,改个 MAC 就能断网?」。真上手抓一次包就会发现,整个过程安静得可怕:没有扫描告警,没有端口异常,只有几条 ARP 报文飘过去,目标机器该断就断了。要理解这件事,得先把 ARP 在协议栈里的位置、以及默认网关在整个转发路径上的角色讲清楚,否则后面所有排查都是盲人摸象。
2.1 无状态、无认证:ARP 的三条硬规则
主机要发包,先查路由表。目的地址在本网段,就直接问「这个 IP 的 MAC 是多少」;不在本网段,就把包丢给默认网关,同样要先问「网关的 MAC 是多少」。这个「问」的过程就是 ARP:发一个广播请求,谁认领这个 IP 谁回一个单播应答,请求方把结果写进本地缓存表(Linux 下用ip neigh看,Windows 下用arp -a看)。
这套流程有三条写死在协议里的规则,也是所有问题的根源:
- 请求广播、应答单播。请求方发的是广播帧,全广播域都能收到;应答方回的是单播,只有请求方能收到。也就是说,广播域里任何一台机器都能看到「谁在问谁的 MAC」。
- 收到应答不校验「我是不是问过」。主机不会去核对这个应答是不是自己那条请求的回复,也不关心回话的人是不是真的持有那个 IP。这是无状态设计带来的直接后果。
- 收到就更新缓存。绝大多数实现里,一条新的 ARP 应答到达时会直接覆盖旧条目,不做二次确认。Windows 会做一定程度的免费 ARP 检测,Linux 则受
arp_accept、arp_ignore、gc_stale_time这几个内核参数影响,行为并不完全一致。
这三条规则是上世纪八十年代定下的,那时候局域网里都是互相信任的机器。今天的办公网、宿舍网、机房是「陌生人共享一个广播域」,协议还是那个协议,信任前提却早就不成立了。工具本身没什么高深技术,它只是把规则二和规则三用到了极致。
各家系统的差异值得单独记一笔。Linux 默认arp_accept=0,对未经请求的免费 ARP 不会主动建表,但对已有的条目仍然会被动更新;把arp_ignore设成 1、arp_accept设成 0,能挡掉一部分情况,但挡不住针对已有条目的一致性覆盖。Windows 的邻居不可达检测(NUD)会在条目老化时主动探测,能缩短被污染窗口,但不能消除。指望靠调内核参数彻底免疫,属于玄学范畴,后面第 6 章会讲更靠谱的做法。
2.2 默认网关的位置:跨网段流量的「第一跳」
一台主机访问外网,路径是这样的:应用发出数据 → 查路由表发现目的地址不在本网段 → 取出默认网关 IP → 查 ARP 缓存拿到网关 MAC → 封装成链路层帧发给网关 → 网关再往下转。整条链路里,网关的 ARP 条目是所有对外流量的单点。
这个单点一旦出问题,症状分两类。第一类是「黑洞」:缓存里的网关 MAC 被改成一个广播域里根本不存在的地址,主机发的帧没人接,表现就是彻底不通、连网关都 ping 不到。第二类是「绕行」:MAC 被改成另一台真实主机的地址,流量能出去也能回来,但中间多绕了一跳,表现是能通、但延迟抖动、大流量时卡顿。第二类更隐蔽,很多人以为是「网络不好」,查半天查不到原因,这就是典型的黑匣子场景。
同网段之间的通信不走网关,这一点经常被忽略。A 主机和 B 主机在同一个广播域里,互访时直接查对方的 MAC,跟网关没关系。所以「剪」同网段的某台机器,需要对目标主机本身动手,而不是对网关动手。这也解释了一个常见困惑:为什么有些环境下这类工具完全不起作用——你和目标之间隔了三层设备、做了 VLAN 隔离,或者干脆不在同一个广播域,工具连报文都送不到。
提示:判断自己在不在同一个广播域,最快的办法是看子网掩码和网关地址。掩码是 /24、网关是 x.x.x.1,那基本就是同一个二层域;掩码是 /25 或更细,就要小心是不是被拆过。
2.3 用抓包把「谁在改缓存」钉死
现象是最不可靠的证据。「网断了」这句话背后可能是 DNS 挂了、可能是 DHCP 租约到期、也可能是真的被剪了。要定性,只能看报文。
抓包不需要什么复杂配置,一条命令就够:
# -i 指定网卡(用 ip -brief addr 查实际名字,常见是 eth0 / ens33 / enp0s3) # -n 不做 IP 反解和端口名解析,避免抓包时反向 DNS 拖慢速度 # -e 显示链路层(MAC)头,这是本次排查的关键 # -c 50 抓满 50 个包就退出,防止刷屏 sudo tcpdump -i eth0 -n -e arp -c 50这条命令的逻辑很直接:只过滤 ARP 协议,把链路层地址一起打出来,抓够 50 个就停。参数里真正要盯的是-e——没有它,你看不到is-at后面的 MAC,等于白抓。-n也别省,局域网里几千台设备时,反向解析会把抓包进程拖到卡死。
抓到之后,重点看应答行:
14:22:31.108742 aa:bb:cc:00:11:22 > ff:ff:ff:ff:ff:ff, ethertype ARP, length 42 Request who-has 192.168.56.1 tell 192.168.56.101, length 28 14:22:31.109013 0a:00:27:00:00:01 > aa:bb:cc:00:11:22, ethertype ARP, length 42 Reply 192.168.56.1 is-at 0a:00:27:00:00:01, length 28第一行是正常的请求:101 号主机广播问「谁是 192.168.56.1」。第二行是正常的应答:网关回话说「我是,我的 MAC 是 0a:00:27:00:00:01」。一切正常。
异常长这样:同一秒里出现两条Reply 192.168.56.1 is-at,但后面的 MAC 不一样;或者出现一条Reply,前面的源 MAC 和网关真实 MAC 对不上;再或者出现大量Reply ... is-at却找不到对应的Request(这就是所谓的免费 ARP,正常情况下只应该在设备上线、IP 变更时出现一两次)。
# 对照本机缓存,看记录和抓包结果是否一致 ip neigh show # 只挑出网关那一条,方便反复核对 ip neigh show 192.168.56.1ip neigh show输出的每一行包含四段:IP、链路层地址、状态(REACHABLE / STALE / DELAY / PERMANENT)、网卡名。状态是 REACHABLE 说明最近确认过,STALE 说明条目陈旧但还没验证,PERMANENT 说明是静态绑定(第 6 章会用到)。盯着网关那条记录多看几次,如果它一会儿 REACHABLE 一会儿 STALE,还伴随 MAC 变化,那基本就能确定有人在动你的缓存了。
注意:抓包要抓在自己机器上,不要在别人的机器上装抓包工具搞「反制」,一来权限不够,二来性质就变了。所有观察都应该在自己有管理权的设备上做。
3. 搭一个隔离实验环境:拓扑、虚拟网络与三台机器的分工
原理讲清楚之后,下一步是验证。但验证这件事有前提:必须在完全隔离的环境里做。任何在办公网、宿舍网、公共 Wi-Fi 上「试一下」的行为,轻则自己断网,重则影响整层楼,这不是危言耸听,是真的会翻车。一台虚拟机、一个仅主机网络,成本不到半小时,收益是你可以随便折腾、快照一按就回滚。
3.1 虚拟网络选型:为什么必须用仅主机或内部网络
虚拟机软件提供的网络模式就那么几种,用途完全不同:
- 桥接模式:虚拟机和物理机在同一个二层网络里,相当于你直接把实验机接进了真实局域网。做实验绝对不能用这个,等于把实验室的门拆了。
- NAT 模式:虚拟机通过宿主机做地址转换访问外网,虚拟机和宿主机之间能通,但虚拟机和同一宿主机上的其他虚拟机之间不一定能直接二层通信。做 ARP 实验不够用。
- 仅主机模式(Host-Only):虚拟机之间、虚拟机和宿主机之间组成一个独立的虚拟二层网络,对外完全隔离。这是首选。
- 内部网络模式:虚拟机之间互通,宿主机都进不来。适合需要严格隔离的场景,缺点是排查时要从虚拟机内部看,稍微麻烦一点。
选型的判断标准只有一条:这个网络里除了我自己,还有没有别人。有,就换。仅主机模式默认是 192.168.56.0/24 网段,这个网段是虚拟化软件自己分配的,跟真实网络一般不冲突,正好拿来当实验田。
还要提醒一句:虚拟化软件装完之后通常会自动创建一张虚拟网卡给宿主机用,这张网卡上的地址就是宿主机在实验网里的地址。实验期间建议把它禁用掉,避免宿主机自己的网络流量跑进实验网,也避免你误以为「实验网能上网」。
3.2 三台机器的角色与地址规划
一台机器做实验容易自说自话,至少三台才能看出「谁影响了谁」。角色分工建议这么排:
| 角色 | 系统建议 | IP 规划 | MAC 规划 | 用途 |
|---|---|---|---|---|
| 网关 | 轻量 Linux | 192.168.56.1 | 0a:00:27:00:00:01 | 承担三层转发,开 IPv4 转发 |
| 观测机 | Linux(装 scapy) | 192.168.56.101 | 0a:00:27:00:00:65 | 跑监控脚本,记录全部 ARP 变化 |
| 目标机 | Windows 或 Linux | 192.168.56.102 | 0a:00:27:00:00:66 | 被观察对象,看它的缓存怎么变 |
| 干扰机(可选) | 任意 Linux | 192.168.56.103 | 0a:00:27:00:00:67 | 模拟异常 ARP 报文,用于验证监控脚本 |
四台配置:前三台是必须的,第四台可选,但如果想验证监控脚本到底灵不灵,最好加上。观测机的地址故意不用 .1 也不用连续段,是为了贴近真实网络里的混乱分配,免得写出来的脚本只对整齐的网段有效。
网关机如果只是做实验,不需要真的配 iptables,把net.ipv4.ip_forward=1打开、再把默认路由指向宿主机那张虚拟网卡即可。这个设置可以让目标机「以为自己能上网」,但实际出不去,正好用来观察黑洞和绕行的区别。
每台机器建好之后立刻打一个快照,命名成「干净环境」。实验前后的状态对比全靠这个快照,比记笔记靠谱。
3.3 用命令划清实验边界
环境搭完,第一件事不是急着发报文,而是确认「我到底在哪个网里」。三条命令足够:
# 1) 看本机所有网卡的地址和状态,确认自己在 192.168.56.0/24 里 ip -brief addr # 2) 看默认路由走向,确认网关 IP 和出口网卡 ip route show default # 3) 看当前 ARP 缓存,作为实验前的基准快照 ip neigh show第一条ip -brief addr输出一行一张网卡,格式是「网卡名 状态 地址」。重点是确认192.168.56.x挂在哪张网卡上——很常见的情况是机器同时插着真实网卡和虚拟网卡,实验时抓包抓错了网卡,抓了半天全是外网流量,白折腾。把网卡名记下来,后面 scapy 的iface参数、tcpdump 的-i参数都要用它。
第二条ip route show default输出类似default via 192.168.56.1 dev ens33。via后面是网关 IP,dev后面是出口网卡。如果这一行指向的是 192.168.1.1 之类的真实网关,说明你的虚拟机网络还没切到仅主机模式,赶紧停下来改配置。
第三条ip neigh show是基准。实验前先跑一次,把输出存成文件:
# 把实验前的邻居表存成基准,后面拿来 diff,比人眼对比靠谱得多 ip neigh show > /tmp/neigh_before.txt # 实验做完再存一份,直接比差异 ip neigh show > /tmp/neigh_after.txt diff /tmp/neigh_before.txt /tmp/neigh_after.txt这三条命令的逻辑是「先定位、再定路、后定状态」。diff那一步是整个实验里性价比最高的操作——ARP 缓存的变化是零散的,人眼在几十条记录里根本看不出哪条被改过,但用 diff 一比,增删改一目了然。参数上没什么可调的,唯一要注意的是/tmp目录在部分系统上重启会清空,实验周期长的话换个持久化目录。
提示:在目标机上把
ip neigh show挂成一个循环,每两秒打一次,输出重定向到文件。这样即使你错过了异常发生的那几秒,事后翻日志也能看到缓存是怎么被改的。
4. 自检脚本:用 scapy 被动监听 ARP 应答
有了隔离环境,接下来的问题变成「我怎么知道有没有人在动我的缓存」。主动去扫描整个网段虽然能看到设备列表,但会产生大量广播报文,在有监控的网络里反而容易把自己暴露出去。更稳妥的做法是被动监听:什么都不发,只收,谁在广播域里喊话、喊了些什么,全部记下来。这种做法对网络零影响,跑一整天也没人会发现异常——除非那个异常本身就是它在找的东西。
4.1 装 scapy 与选网卡
scapy 是 Python 里处理原始报文的常用库,装起来没什么门槛:
# 建议在虚拟环境里装,别污染系统 Python python3 -m venv /opt/arpwatch-venv source /opt/arpwatch-venv/bin/activate # scapy 依赖 libpcap 做抓包,先确认系统里有 sudo apt-get install -y libpcap-dev python3-dev # 装 scapy 本体 pip install scapy三段命令各有用处。第一段建虚拟环境,是为了把这款工具和你系统里其他 Python 项目隔开——scapy 会改系统的 socket 行为,装在全局偶尔会和别的网络库打架。第二段的libpcap-dev是 scapy 抓包的后端依赖,缺了它会退回纯 Python 抓包,性能差一大截,而且在高流量网段容易丢包。第三段是本体。
装完先验证一下能不能找到网卡:
# 列出所有可用的抓包接口,名字要和 ip -brief addr 的输出对得上 from scapy.all import get_if_list, get_if_hwaddr for name in get_if_list(): try: print(f"{name:16s} {get_if_hwaddr(name)}") except Exception as e: print(f"{name:16s} 读取失败:{e}")这段的作用是把 scapy 眼里的网卡名和系统里的网卡名对一遍。绝大多数情况下它们一致,但在容器或者多网卡机器上偶尔会出现eth0对ens33这种错位。get_if_hwaddr会返回该网卡的 MAC,如果这个值和ip -brief addr里显示的不一样,说明选错网卡了。try/except是必要的——有些虚拟网卡(比如没插网线的)读 MAC 会抛异常,不捕获的话整个循环就断了。
4.2 被动监听脚本:只收不发,发现 MAC 变更就报警
脚本的核心逻辑只有一句:收到 ARP 应答,就把「IP → MAC」记下来;如果同一个 IP 出现了第二个不同的 MAC,立刻报出来。就这么简单,但能覆盖绝大多数情况。
#!/usr/bin/env python3 # arp_watch.py —— 被动监听 ARP 应答,发现「同一 IP 换了 MAC」就告警 # 只收包,不发包,对网络零影响 import time from scapy.all import sniff, ARP IFACE = "ens33" # 改成 ip -brief addr 里实际的网卡名 WATCH_IPS = {"192.168.56.1"} # 重点盯的 IP,网关必填,可加多个 GRACE_SECONDS = 300 # 同一 MAC 再次出现时的静默期(秒) seen = {} # {ip: {"mac": 最近一次 MAC, "first": 首次时间, "last": 上次时间}} def now(): return time.strftime("%Y-%m-%d %H:%M:%S") def on_packet(pkt): # 只处理带 ARP 层的报文,其他一律放过 if not pkt.haslayer(ARP): return arp = pkt[ARP] # op == 2 是 ARP Reply(is-at),op == 1 是 Request(who-has) # 缓存污染绝大多数以 Reply 形式出现,这里只盯 Reply,降低噪音 if arp.op != 2: return ip, mac = arp.psrc, arp.hwsrc # 不在观察名单里的 IP 直接跳过,避免整张表被撑爆 if WATCH_IPS and ip not in WATCH_IPS: return rec = seen.get(ip) if rec is None: seen[ip] = {"mac": mac, "first": now(), "last": now()} print(f"[+] {now()} 首见 {ip} -> {mac}") return if rec["mac"] != mac: # 关键告警:同一个 IP 出现第二个 MAC print(f"[!] {now()} MAC 变更 {ip}: {rec['mac']} -> {mac}") print(f" 首次记录于 {rec['first']},上次出现 {rec['last']}") rec["mac"] = mac rec["last"] = now() return # MAC 没变,但距离上次出现超过静默期,才打一条心跳,防止刷屏 last_ts = time.mktime(time.strptime(rec["last"], "%Y-%m-%d %H:%M:%S")) if time.time() - last_ts > GRACE_SECONDS: print(f"[.] {now()} 心跳 {ip} 仍为 {mac}") rec["last"] = now() if __name__ == "__main__": print(f"[*] 监听 {IFACE},观察对象:{sorted(WATCH_IPS)}") # store=0 表示不把报文留在内存里,长时间运行不会被撑爆 # filter="arp" 交给底层 libpcap 过滤,比在 Python 里判类型省 CPU sniff(iface=IFACE, filter="arp", prn=on_packet, store=0)逻辑上分成四段。第一段做过滤,先把非 ARP 报文和非 Reply 报文剔掉,这两步能把处理量压到极低——一个几百台设备的网段,ARP Reply 一秒也就几条。第二段做名单过滤,WATCH_IPS为空表示全收,非空则只关心名单里的。第三段是核心判断:如果这个 IP 从没见过,记下来并打一条[+];如果见过但 MAC 变了,打[!]告警并更新记录。第四段是心跳,用了GRACE_SECONDS做静默期,避免一台设备正常刷新缓存时把日志刷满。
参数上有三个地方值得调。IFACE必须换成实际网卡名,写错了 scapy 会在启动时报Interface not found,不会有静默失败。WATCH_IPS建议至少包含网关,如果关心同网段横向问题,把关键服务器的 IP 也加进去。GRACE_SECONDS默认 300 秒,网段小可以调到 600,设备多、ARP 刷新频繁的话调到 1800 更合适——这个值的本质是「多久没消息算一次有效观察」,跟业务节奏有关,没有标准答案。
启动时需要注意权限:scapy 抓包要 root 或者CAP_NET_RAW能力,普通用户跑会直接抛PermissionError。
# 方式一:直接用 sudo 跑(简单,但整个进程都是 root) sudo python3 arp_watch.py # 方式二:给解释器加能力,脚本本身以普通用户运行(更稳妥) sudo setcap cap_net_raw,cap_net_admin+eip $(readlink -f $(which python3)) python3 arp_watch.py # 输出同时落盘,方便事后翻 sudo python3 arp_watch.py 2>&1 | tee -a /var/log/arp_watch.log方式二更稳妥的原因在于最小权限原则:脚本本身没有写文件的权限,即使代码被人改了也翻不出大浪。setcap那条命令要给 Python 解释器的真实路径(readlink -f是为了解开软链接),加错了对象不生效。tee -a是追加写,不要用>,否则每次重启日志都被清空,出事时正好没有历史记录。
4.3 判读输出:什么时候该报警,什么时候是误报
脚本跑起来之后会持续输出,但「有输出」不等于「有问题」。下面这几种输出要区别对待:
[+] 2024-05-11 09:12:03 首见 192.168.56.1 -> 0a:00:27:00:00:01 [.] 2024-05-11 09:20:15 心跳 192.168.56.1 仍为 0a:00:27:00:00:01 [!] 2024-05-11 09:31:47 MAC 变更 192.168.56.1: 0a:00:27:00:00:01 -> 0a:00:27:00:00:99 [!] 2024-05-11 09:31:49 MAC 变更 192.168.56.1: 0a:00:27:00:00:99 -> 0a:00:27:00:00:01[+]和[.]是正常的,前者是首次观察,后者是长时间无变化后的确认。真正要看的是[!]。
第一种情况:MAC 变更出现一次,几秒后又变回原值。这种「来回跳」是最典型的特征——攻击方在持续发伪造应答,而真实网关也在正常回应,两边打架,目标机的缓存就在两个 MAC 之间反复横跳。看到这种模式,基本可以确定有问题。
第二种情况:MAC 变更之后再也不变回去。这可能是攻击方停止发包后缓存被污染且长期未刷新,也可能是设备正常更换了网卡。区分办法是看变更后的 MAC 是不是你认识的设备——把 MAC 前三位(OUI)查一下厂商,如果是虚拟化软件的 OUI(比如 0a:00:27 这类),那八成是有人用虚拟机在做实验。
第三种情况:完全没有[!],但目标机还是断网。这时候问题大概率不在 ARP 层,要去查 DHCP 租约、DNS 配置、交换机端口状态。ARP 监控只能证明「缓存没被改」,不能证明「网络一定通」,这两件事要分开看。
还有一个容易被忽略的点:脚本只能看到自己网卡收到的报文。如果是交换式网络,其他两台机器之间的单播 ARP 应答你是收不到的,除非端口镜像或者你在网关位置。所以监控脚本的覆盖面取决于你部署在哪台机器上——部署在网关上收益最大,能看到全网段的请求;部署在普通终端上,只能看到广播和发给自己的单播。
5. 避坑:五条踩过的坑,每条都能让你白干半天
这套东西看起来简单,实际动手时翻车的地方比想象中多。下面五条是按「现象 → 原因 → 解决」整理的,都是实打实遇到过的。
5.1 抓包一条都抓不到,脚本却显示在运行
现象:sniff()启动了,日志里也打了「监听中」,但几个小时过去一条输出都没有,用tcpdump同时抓却能抓到一堆。
原因:网卡名写错了,或者抓的不是承载实验网段的那张网卡。多网卡机器上这是高频问题——eth0是真实网卡,实验网段挂在ens33上,脚本盯着eth0自然什么都收不到。另一种可能是 BPF 过滤器写成了arp and host 192.168.56.1,但实际流量里源地址和目的地址都不匹配,被过滤掉了。
解决:先用ip -brief addr和ip route get 192.168.56.1确认实验网段到底走哪张网卡,把网卡名抄下来。再在脚本启动时打印一次get_if_hwaddr(IFACE)的结果,和ip -brief addr里的 MAC 对照。过滤器尽量写宽,先filter="arp"跑通,确认能收到包了再收窄条件。
5.2 虚拟机里看到的 MAC 全是宿主机的
现象:在虚拟机里跑监控,发现所有 ARP 应答的源 MAC 都一样,而且这个 MAC 看着像宿主机物理网卡的。
原因:虚拟机网络模式设成了 NAT。NAT 模式下,虚拟机的流量都要经过宿主机的地址转换,二层信息被重写,你在虚拟机里看到的链路层地址是虚拟网关的,不是真实对端的。ARP 层面的实验在 NAT 模式下基本没法做。
解决:切换到仅主机模式或者内部网络模式,重启虚拟网卡。切换之后 IP 可能会变(DHCP 重新分配),记得用ip -brief addr重新确认,并且把所有机器的地址规划表更新一遍。切换前先打快照,切错了能回滚。
5.3 静态绑定做完,重启就失效
现象:手动用ip neigh replace ... nud permanent绑定了网关 MAC,当场验证有效,第二天开机发现又变回动态条目了。
原因:ip neigh的修改是运行时状态,不写任何配置文件,重启即失效。而且部分发行版的网络管理服务(NetworkManager、systemd-networkd)会在网卡 up 的时候重置邻居表。
解决:不要指望手工命令长期生效,要么写进网络管理服务的配置(systemd-networkd用.network文件里的[Neighbor]段,NetworkManager 用 dispatcher 脚本),要么用 systemd timer 每 30 秒重放一次绑定命令。第 6 章会给一个可用的写法。
5.4 只盯网关,漏掉了同网段横向
现象:网关 MAC 一直没变,监控风平浪静,但内网某台数据库服务器连不上了。
原因:同网段通信不走网关,所以针对内网主机的缓存污染完全不会体现在「网关 MAC 有没有变」上。只盯一个 IP 的监控等于只装了一扇门。
解决:把关键资产都加进WATCH_IPS——数据库、域控、文件服务器、监控服务器。名单不要超过 20 个,太多会稀释告警的可读性。另一个办法是在交换机上配端口镜像,把整个网段的 ARP 流量镜像到观测机,这样覆盖面最全,但需要交换机管理权限。
5.5 在真实网络里「顺手试一下」
现象:在自己的办公网里跑了一遍,结果自己先断网了,重启网络服务才恢复,还被网管找上门。
原因:办公网通常有 ARP 防护(动态 ARP 检测、端口安全、ARP 限速),一旦检测到异常报文,交换机会直接把端口 err-disable 掉。你以为是「剪别人」,实际是「剪自己」。更糟的情况是影响面扩散到整个 VLAN。
解决:没有任何例外,所有涉及 ARP 报文的实验都必须在隔离环境里做。判断标准很简单:ip route show default里的网关地址,是不是你自己配的那台虚拟机。不是,就停手。真实网络里唯一该做的事是「观察」和「加固」,不是「复现」。
注意:第 5.5 条不是危言耸听。绝大多数网络设备对 ARP 异常的容忍度很低,端口被禁之后要联系管理员手动恢复,这个过程可能比实验本身长得多。
6. 加固与验证:把后悔药提前吃掉
搞清楚原理之后,真正有价值的部分是「怎么让自己的网段不被剪」。加固分三层,从下往上依次是主机侧静态绑定、接入侧端口防护、监控侧持续告警。三层不必全上,但至少要有两层,只靠一层很容易被绕过。
主机侧的静态绑定是最容易落地的,代价是要维护 MAC 地址清单:
| 系统 | 绑定命令 | 是否持久 |
|---|---|---|
| Linux(临时) | sudo ip neigh replace 192.168.56.1 lladdr 0a:00:27:00:00:01 nud permanent dev ens33 | 否,重启失效 |
| Linux(持久) | 写入 systemd-networkd 的.network文件[Neighbor]段 | 是 |
| Windows | netsh interface ipv4 add neighbors "以太网" 192.168.56.1 0a-00-27-00-00-01 | 是,写注册表 |
| macOS | sudo arp -s 192.168.56.1 0a:00:27:00:00:01 | 否,重启失效 |
Linux 的持久化写法,在对应网卡的.network文件里加一段:
# /etc/systemd-networkd/10-ens33.network [Match] Name=ens33 [Network] DHCP=yes # 网关与关键服务器写死,nud permanent 表示永不老化 [Neighbor] Address=192.168.56.1 LinkLayerAddress=0a:00:27:00:00:01改完sudo systemctl restart systemd-networkd生效。这段配置的逻辑是让 networkd 在网卡 up 的时候自动下发邻居表项,效果等价于手工执行ip neigh replace ... nud permanent,但重启后依然存在。参数上唯一要注意的是Name必须和实际网卡名完全一致,写错了整段配置静默失效,不会有任何报错。
验证是不是真的生效,看状态列:
# PERMANENT 说明绑定成功;REACHABLE / STALE 说明还是动态的 ip neigh show 192.168.56.1 # 主动触发一次刷新,确认绑定扛得住 ping -c 3 192.168.56.1 && ip neigh show 192.168.56.1第二行是关键。光看状态不够,得主动发几个包把缓存「顶」一下——动态条目在收到应答后会被更新,永久条目不会。如果 ping 完之后状态从 PERMANENT 变成了 REACHABLE,说明绑定没生效,多半是配置文件没被加载或者网卡名对不上。
接入侧防护需要在交换机上做,常见的是动态 ARP 检测和端口安全:前者依赖 DHCP Snooping 建立的 IP-MAC 绑定表,收到 ARP 报文时逐条比对,不匹配直接丢弃;后者限制单个端口能学到的 MAC 数量,防止有人拿一台机器伪造一堆地址。这两项都需要设备支持,配置方法各家不同,落地前先在测试口上验证,别一把推到全楼。
监控侧就是第 4 章那个脚本,部署在网关或者核心交换机镜像口上,接一个简单的告警通道。告警阈值建议设成「5 分钟内出现 3 次以上 MAC 变更」,太少误报多,太多漏报。
最后说个血泪教训。早些年我在一个共享办公网里排查「间歇性断网」,查了两天路由、换了三台交换机,最后抓包发现网关 MAC 每隔十几秒跳一次——有人在用类似的工具做「限速实验」。那次之后我养成了一个习惯:任何网络异常,先跑 60 秒 ARP 抓包,再谈其他。这一分钟的成本,比事后翻三天日志低太多。ARP 这东西平时不显山不露水,真出事的时候,它是唯一能说清真相的证据链。希望帮到你。
本文还有配套的精品资源,点击获取