酒店网络和会议防火墙常常给出一个自定义放行框,有人为了碰运气就让 PPTP 通过,直接在框里填 47。规则建好了,隧道照样不通,下一次尝试又换成了另一个数字。问题出在报文本身:IPv4 有一个协议字段,指明 IP 头之后是什么;而 TCP 和 UDP 的端口字段,只有进到这两个头部里面才会出现。它们不是同一种许可的两种写法。

IPv4 头部指明下一个协议

IPv4 头里的协议字段只有一个字节,里面存放描述载荷的数字:TCP 是 6,UDP 是 17,GRE 是 47,ESP 是 50。任何过滤报文的设备都会先读这个值,再往更深处看,因为更深一层的头部只有在类型已知后才有意义。稍后在会话中更换端口,这个字段并没有动。

因此,针对协议 47 写的规则描述的是 IP 层。端口根本不参与这个判断。

端口只存在于 TCP 和 UDP 内部

端口是 TCP 和 UDP 头部里的十六位标识,作用是区分共用同一个地址的多条会话。GRE 头部携带协议类型值,通常是 EtherType,还带有用于复用的可选密钥。所以“端口 47”这个说法并没有指代任何 GRE 报文能够匹配的东西。

防火墙界面的展示方式各不相同。有的接受协议号,有的提供带名字的列表,少数只接受带端口的 TCP 或 UDP。

PPTP 同时使用两层

PPTP 是最清楚的例子,因为它刻意把工作分到两层。控制连接是到 1723 端口的 TCP 会话,数据通路是 GRE,即协议 47,全程没有任何端口参与。只放行 1723 的防火墙会让协商开始,然后丢弃载荷。隧道看起来是在认证之后卡住,而不是连接失败,所以症状常被当成密码问题上报。

PathOuter protocolPort
Control channelTCP, protocol 61723
Data channelGRE, protocol 47None

写规则时把两行分开读,并逐条对照厂商语法检查。一行需要端口,另一行需要协议号,没有任何一条记录能同时覆盖两者。

为什么端口转发页面表达不了 GRE

只接受 TCP 和 UDP 的转发表单无法描述 GRE,不管这个表单叫什么名字。地址转换又添了第二道难题,因为 GRE 没有端口,转换器只能改为跟踪呼叫标识。有些家用路由器自带能处理这种情况的助手,很多则没有,而且再增加一条 TCP 放行也不会产生这种行为。

如果真正的需求是现代隧道,那就选择有意使用 TCP 或 UDP 的方案,而不是指望转换器猜对。

封装决定过滤器看到什么

封装改变外层报文,而规则匹配的正是外层报文。原生 ESP 是协议 50,NAT 穿越把 ESP 包进 UDP 4500,才能穿过地址转换。放行 UDP 4500 的过滤器永远看不到协议 50,而为协议 50 写的过滤器也永远匹配不上被包起来的形式。

先问客户端和网关约定的是哪种封装,然后只针对那个外层形态写规则,不要多写。

把规则写成别人能检查的样子

把每条规则都表达为地址族、外层协议、传输端口(如果有)、方向以及两个端点。审阅者可以拿这句话和抓包记录对照,得出判断。而“开放 47”这种指示没人能检查,所以它能在操作手册里存活很多年。

补上能证明它的测试:在外网口抓包的同时发起连接,确认抓包记录里实际出现的外层协议和端口。

相关文章