2、ACL工作原理:处理流程、匹配顺序与隐含拒绝

ACL这东西,说白了就是路由器上的一张「访问控制清单」。你告诉路由器:哪些流量能过,哪些必须拦下。但真正让我觉得有意思的,是它背后那套处理逻辑——如果你不理解这个逻辑,配置ACL就跟闭着眼写规则差不多,早晚要踩坑。

我记得刚入行那会儿,给客户配了一组ACL,明明规则写得很「全」,结果该通的业务不通,不该通的反而放过去了。后来一查,问题就出在匹配顺序和那条看不见的「隐含拒绝」上。嗯,咱们今天就把它彻底讲透。

2.1 ACL处理流程:数据包怎么「过堂」的?

一个数据包到达路由器接口,如果这个接口上绑了ACL,它不会直接放行。它会经历下面这几步:

  1. 接收数据包——接口收到一个IP包,先做基本的校验(比如CRC、版本号)。
  2. 检查入站ACL——如果接口配置了ip access-group xxx in,数据包会先过ACL这一关。
  3. 逐条匹配规则——从ACL的第一条规则开始,从上到下依次比对。
  4. 命中即执行——一旦某条规则匹配成功,立即执行permit或deny,不再往下看。
  5. 隐含拒绝收尾——如果所有规则都没匹配上,路由器自动执行deny any,丢弃这个包。

你想想看,这个流程其实很像机场安检——先查证件,再逐项核对,只要有一项不合格,直接拦下,后面的流程根本轮不到你。

关键点:ACL只做一件事——「匹配即停止」。它不会把所有规则都跑一遍再决定,而是找到第一条匹配的规则就立刻执行动作。这个特性决定了规则的排列顺序极其重要。

下面这张图,把整个处理流程画出来了。我建议你多看两眼,后面讲匹配顺序的时候会反复用到这张图。

ACL数据包处理流程 数据包到达接口 检查接口是否绑定入站ACL 从第1条规则开始逐条匹配 是否匹配当前规则? 是 执行 permit / deny 否 取下一条规则继续匹配 说明:匹配即停止。一旦命中规则,不再检查后续规则。所有规则均未匹配时,触发隐含拒绝(deny any)。

2.2 匹配顺序:为什么顺序就是「命」?

ACL的匹配顺序,我直接说结论:顺序匹配,命中即止。这个顺序不是按IP地址大小排的,也不是按掩码长度排的,就是按你写规则的先后顺序。

举个例子你就明白了。假设我有这么两条规则:

access-list 100 permit ip 192.168.1.0 0.0.0.255 any
access-list 100 deny ip 192.168.1.100 0.0.0.0 any

你猜结果会怎样?192.168.1.100这个地址会被放行。为什么?因为第一条规则已经匹配了192.168.1.0/24整个网段,包括.100在内。数据包在第一条就命中了,直接permit,第二条规则根本看不到。

我曾经见过一个案例:某公司网络管理员想封掉研发部一台特定服务器(192.168.10.50)的SSH访问,他在ACL里写了deny tcp any host 192.168.10.50 eq 22,但这条规则放在了permit ip any any的后面。结果呢?所有流量都被放行了,那条deny规则成了摆设。这就是典型的「顺序错误」——更具体的规则必须放在更宽泛的规则前面。

所以,配置ACL的时候,我个人的习惯是:

  • 最具体的规则放最前面——比如针对特定主机、特定端口的deny。
  • 中间放常规控制规则——比如允许某个网段访问某个服务。
  • 最后放默认放行或拒绝——通常是permit ip any any或者什么都不写(触发隐含拒绝)。

说白了,这就是一个「从精确到宽泛」的漏斗结构。你想想看,如果安检人员先放行所有旅客,再回头去查某个可疑人员,那还有什么意义?

2.3 隐含拒绝规则:那条看不见的「最后防线」

很多新手会忽略这一点——每条ACL的末尾,都藏着一条看不见的规则:deny any any。这就是所谓的「隐含拒绝规则」(Implicit Deny)。

什么意思呢?如果你只写了允许某些流量通过,没写拒绝其他流量,那么所有没被明确允许的流量,都会被自动拒绝。注意,是「所有」。

举个例子:你配了一条ACL:access-list 1 permit 192.168.1.0 0.0.0.255。你以为这就够了?不,这条ACL的实际效果是:允许192.168.1.0/24网段通过,拒绝其他所有流量。因为末尾有一条你看不见的deny any。

我记得有一次,一个同事配置了ACL后,发现所有非授权网段的流量都被拦了,他以为是设备出了问题。我过去一看,ACL里只写了permit规则,没写deny规则。我说:「你没写deny,但隐含拒绝帮你写了。」他当时就愣住了。

这里有个重要的点要记住:

场景 ACL规则 实际效果
只写permit permit 192.168.1.0 0.0.0.255 允许该网段,拒绝其他所有(隐含拒绝生效)
写permit + deny permit 192.168.1.0 0.0.0.255
deny 10.0.0.0 0.255.255.255
允许该网段,拒绝10.0.0.0/8,拒绝其他所有(隐含拒绝仍在)
写permit + permit any permit 192.168.1.0 0.0.0.255
permit ip any any
允许该网段,允许所有其他流量(隐含拒绝被覆盖)

避坑指南:如果你想让ACL只拒绝某些流量,其他全部放行,一定要在最后加上permit ip any any。否则,隐含拒绝会帮你「拒绝一切」。我曾经在割接时因为这个疏忽,导致整个分公司断网半小时——教训深刻。

2.4 ACL与路由器的交互:数据包到底走哪条路?

ACL不是独立存在的,它和路由器的转发流程紧密相关。这里有个容易混淆的点:ACL是在路由决策之前还是之后执行?

答案是:取决于ACL绑定的方向。

  • 入站ACL(inbound):数据包到达接口后,先过ACL,再做路由决策。也就是说,如果ACL拒绝了某个包,路由器根本不会去查路由表,直接丢弃。
  • 出站ACL(outbound):数据包先做路由决策,再过ACL。路由器先查路由表,确定从哪个接口转发,然后在这个出接口上检查ACL。

这个顺序在实际排错中非常关键。我举个例子:

假设你在路由器的G0/0接口上绑了一个入站ACL,拒绝来自10.0.0.0/8的流量。那么,即使路由器有一条默认路由指向10.0.0.1,这个包也根本到不了路由决策阶段——在接口上就被ACL拦下了。

注意:入站ACL会消耗更少的CPU资源,因为它在路由查找之前就过滤掉了不需要的流量。但出站ACL更灵活,因为你可以基于路由结果来做更精细的控制。我个人建议:能用入站ACL解决的问题,尽量用入站ACL,性能更好。

下面这张图,展示了ACL与路由决策的交互关系:

ACL与路由决策的交互顺序 入站ACL检查 路由决策 转发/丢弃 入站方向:先ACL,后路由 路由决策 出站ACL检查 转发/丢弃 出站方向:先路由,后ACL 说明:入站ACL在路由查找前过滤,节省CPU;出站ACL在路由查找后过滤,控制更灵活。