最近碰到一个PPP拨号拿到了假的DNS地址10.11.12.13/14
, 之前也发生过一次, 但是一直没有有效的日志, 所以就简单粗暴的做了一个方案: 在PPP拨号进行网络参数协商时, 如果发现拿到了10.11.12.13/14
这样的DNS地址就修改为指定的运营商DNS地址. 本来以为万事大吉了, 没想到运营商DNS一改, 问题又暴露了, 好在重现抓到了现场日志, 终于找到了原因. 网上看有原来也有不少人碰到了类似现象(https://bugzilla.redhat.com/show_bug.cgi?id=467004#c31), 在这里简要的描述下整个问题的来龙去脉并给出几种可能的解决方案.
在介绍这个问题的根因之前, 先来了解下PPP协议的一些基本概念.
PPP协议介绍
PPP(Point-to-Point Protocol)是在SLIP(Serial Line Internet Protocol)的基础上发展而来, 其通过在终端与远端(remote peer)之间建立一个IPPP数据链路, 将终端设备接入网络. 早些年上网的时候, 把一个ADSL modem跟电脑连接后, 拿着运营商给的用户名与密码, 然后拨号接入互联网,这里边用到的协议就是PPP.
PPP协议位于TCP/IP协议栈的第二层-数据链路层(见下图), 其主要有三个部分组成:
- 包封装: 如何将网络报文如IP报文封装成类似与HDLC的格式
- LCP协议: Link Control Protocol, 负责建立数据链路, 验证用户身份以及测试链路的连通状态
- NCP协议: Network Control Protocol, 负责适配其他协议, 如针对IP协议有IPCP协议, 该协议的功能是从网络侧拿到IP地址以及DNS地址
一般来说, PPP拨号上网有如下几个阶段:
- 链路建立阶段(Establish): LCP协议负责建立数据链路, 建立链路过程主要是协商数据的压缩格式以及最大传输单元MRU(Maximum Receive Unit, 类似于MTU)
- 身份验证阶段(Authenticate): 主要是通过PAP/CHAP等协议验证用户的身份(用户名与密码)
- 网络阶段(Network): 协商网络参数, 如IP地址, DNS地址等. 网络阶段完成后, 就可以真正进行数据报文的接发了.
在NCP阶段, PPP拨号进行参数协商过程中一般都有如下几个指令: Configure-Request
(发起参数协商), Configure-Ack
(如果同意该参数则发送ACK给对端), Configure-Nak
(如果参数合法, 但是有不同意对方的某些参数, 则发送NAK给对端), Configure-Reject
(如果不同意对方发送过来的参数, 则直接发送Reject), 具体可以参考RFC1661的文档(rfc1661).下图是一个完整基于IPCP协议的消息交换过程:
有了这些知识背景, 就来看下具体的问题是怎么样的.
假DNS问题的来由
问题发生终端大致的网络架构如下图所示:终端通过modem与运营商的LNS节点建立一个PPP的链路, 然后通过该链路接入私有网络;对Android来说,一般是在RILD
中调用PPPD进程发起PPP
拨号, 命令格式大致如下:
1 2 3
| /system/bin/pppd /dev/ttyUSB0 115200 user ctnet@mycdma.cn password vnet.mobi nocrtscts usepeerdns debug ipcp-accept-local ipcp-accept-remote defaultroute
|
图中的LAC
/LNS
分别指L2TP Access Concentrator
/L2TP Network Server
, 参考Wikipedia
这里系统使用的是高通的modem, 在LTE网络下使用PPP拨号从运营商网关拿IP/DNS地址. PPP拨号时, 实际已经通过了身份验证, 但在IPCP协议进行参数协商时, 前面一直拿到了10.11.12.13/14
这样的DNS地址, 后面运营商下发了正确的DNS地址, modem也未能把正确的DNS地址发送过来, PPPD拨号的过程日志如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 07-02 20:40:37.024 3183 3183 D pppd : using channel 3 07-02 20:40:37.024 3183 3183 D pppd : sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x6c3c9df8> <pcomp> <accomp>] 07-02 20:40:37.024 3183 3183 D pppd : rcvd [LCP ConfReq id=0x3 <asyncmap 0x0> <auth pap> <magic 0xf2cb3c8d> <pcomp> <accomp>] 07-02 20:40:37.024 3183 3183 D pppd : sent [LCP ConfAck id=0x3 <asyncmap 0x0> <auth pap> <magic 0xf2cb3c8d> <pcomp> <accomp>] 07-02 20:40:37.024 3183 3183 D pppd : rcvd [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0x6c3c9df8> <pcomp> <accomp>] 07-02 20:40:37.034 3183 3183 D pppd : rcvd [LCP DiscReq id=0x4 magic=0xf2cb3c8d] 07-02 20:40:37.034 3183 3183 D pppd : rcvd [PAP AuthAck id=0x1 ""] 07-02 20:40:37.034 3183 3183 I pppd : PAP authentication succeeded 07-02 20:40:37.034 3183 3183 D pppd : sent [CCP ConfReq id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
07-02 20:40:37.034 3183 3183 D pppd : sent [IPCP ConfReq id=0x1 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns3 0.0.0.0>] 07-02 20:40:37.034 3183 3183 D pppd : rcvd [LCP ProtRej id=0x5 80 fd 01 01 00 0f 1a 04 78 00 18 04 78 00 15 03 2f] 07-02 20:40:38.034 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x1 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:38.034 3183 3183 D pppd : sent [IPCP ConfReq id=0x2 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14>] 07-02 20:40:39.034 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x2 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:39.034 3183 3183 D pppd : sent [IPCP ConfReq id=0x3 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14>] 07-02 20:40:40.034 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x3 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:40.034 3183 3183 D pppd : sent [IPCP ConfReq id=0x4 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14>] 07-02 20:40:41.044 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x4 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:41.044 3183 3183 D pppd : sent [IPCP ConfReq id=0x5 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14>] 07-02 20:40:42.044 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x5 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:42.044 3183 3183 D pppd : sent [IPCP ConfReq id=0x6 <compress VJ 0f 01> <addr 0.0.0.0>] 07-02 20:40:43.044 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x6 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:43.044 3183 3183 D pppd : sent [IPCP ConfReq id=0x7 <compress VJ 0f 01> <addr 0.0.0.0>] 07-02 20:40:44.044 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x7 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:44.044 3183 3183 D pppd : sent [IPCP ConfReq id=0x8 <compress VJ 0f 01> <addr 0.0.0.0>] 07-02 20:40:45.044 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x8 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>] 07-02 20:40:45.044 3183 3183 D pppd : sent [IPCP ConfReq id=0x9 <compress VJ 0f 01> <addr 0.0.0.0>]
07-02 20:40:45.644 3183 3183 D pppd : rcvd [IPCP ConfReq id=0x2] 07-02 20:40:45.644 3183 3183 D pppd : sent [IPCP ConfNak id=0x2 <addr 0.0.0.0>] 07-02 20:40:45.644 3183 3183 D pppd : rcvd [IPCP ConfRej id=0x9 <compress VJ 0f 01>] 07-02 20:40:45.644 3183 3183 D pppd : sent [IPCP ConfReq id=0xa <addr 0.0.0.0>] 07-02 20:40:45.644 3183 3183 D pppd : rcvd [IPCP ConfReq id=0x3] 07-02 20:40:45.644 3183 3183 D pppd : sent [IPCP ConfAck id=0x3] 07-02 20:40:45.644 3183 3183 D pppd : rcvd [IPCP ConfNak id=0xa <addr 10.200.16.23>] 07-02 20:40:45.644 3183 3183 D pppd : sent [IPCP ConfReq id=0xb <addr 10.200.16.23>] 07-02 20:40:45.644 3183 3183 D pppd : rcvd [IPCP ConfAck id=0xb <addr 10.200.16.23>] 07-02 20:40:45.644 3183 3183 W pppd : Could not determine remote IP address: defaulting to 10.64.64.64 07-02 20:40:45.654 3183 3183 I pppd : local IP address 10.200.16.23 07-02 20:40:45.654 3183 3183 I pppd : remote IP address 10.64.64.64 07-02 20:40:45.654 3183 3183 I pppd : primary DNS address 10.11.12.13 07-02 20:40:45.654 3183 3183 I pppd : secondary DNS address 10.11.12.14 07-02 20:40:45.654 3183 3183 D pppd : Script /etc/ppp/ip-up started (pid 3229) 07-02 20:40:45.714 3183 3183 D pppd : Script /etc/ppp/ip-up finished (pid 3229), status = 0x0
|
可以看到在第一次PPP参数协商时, PPPD就收到了这样的配置, 这里ms-wins
参数是针对微软的客户端产生的配置(在Linux系统下并没有什么作用):
1 2 3
| 07-02 20:40:38.034 3183 3183 D pppd : rcvd [IPCP ConfNak id=0x1 <ms-dns1 10.11.12.13> <ms-dns3 10.11.12.14> <ms-wins 10.11.12.13> <ms-wins 10.11.12.14>]
|
再来对比下正常的PPPD拨号的情况, PPPD拨号时首先很快就收到了IPCP ConfReq id=0x0
的请求, 这个请求是告诉客户端可以发起IP地址的协商了, 于是客户端就发了NAK
包告诉服务端自己的IP地址是空的sent [IPCP ConfNak id=0x0 <addr 0.0.0.0>]
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 07-02 20:40:01.510 1643 1643 D pppd : using channel 1 07-02 20:40:01.510 1643 1643 D pppd : sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x2d6bff1> <pcomp> <accomp>] 07-02 20:40:01.510 1643 1643 D pppd : rcvd [LCP ConfReq id=0x0 <asyncmap 0x0> <auth pap> <magic 0xf2cab769> <pcomp> <accomp>] 07-02 20:40:01.510 1643 1643 D pppd : sent [LCP ConfAck id=0x0 <asyncmap 0x0> <auth pap> <magic 0xf2cab769> <pcomp> <accomp>] 07-02 20:40:01.510 1643 1643 D pppd : rcvd [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0x2d6bff1> <pcomp> <accomp>] 07-02 20:40:01.510 1643 1643 D pppd : rcvd [LCP DiscReq id=0x1 magic=0xf2cab769] 07-02 20:40:01.510 1643 1643 D pppd : rcvd [PAP AuthAck id=0x1 ""] 07-02 20:40:01.510 1643 1643 I pppd : PAP authentication succeeded 07-02 20:40:01.510 1643 1643 D pppd : sent [CCP ConfReq id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>] 07-02 20:40:01.510 1643 1643 D pppd : sent [IPCP ConfReq id=0x1 <compress VJ 0f 01> <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns3 0.0.0.0>] 07-02 20:40:01.510 1643 1643 D pppd : rcvd [LCP ProtRej id=0x2 80 fd 01 01 00 0f 1a 04 78 00 18 04 78 00 15 03 2f]
07-02 20:40:01.970 1643 1643 D pppd : rcvd [IPCP ConfReq id=0x0] 07-02 20:40:01.970 1643 1643 D pppd : sent [IPCP ConfNak id=0x0 <addr 0.0.0.0>] 07-02 20:40:01.970 1643 1643 D pppd : rcvd [IPCP ConfRej id=0x1 <compress VJ 0f 01>] 07-02 20:40:01.970 1643 1643 D pppd : sent [IPCP ConfReq id=0x2 <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns3 0.0.0.0>] 07-02 20:40:01.970 1643 1643 D pppd : rcvd [IPCP ConfReq id=0x1] 07-02 20:40:01.970 1643 1643 D pppd : sent [IPCP ConfAck id=0x1] 07-02 20:40:01.970 1643 1643 D pppd : rcvd [IPCP ConfNak id=0x2 <addr 10.200.16.23> <ms-dns1 211.xxx.xxx.xxx> <ms-dns3 211.xxx.xxx.xxx>] 07-02 20:40:01.970 1643 1643 D pppd : sent [IPCP ConfReq id=0x3 <addr 10.200.16.23> <ms-dns1 211.xxx.xxx.xxx> <ms-dns3 211.xxx.xxx.xxx>] 07-02 20:40:01.970 1643 1643 D pppd : rcvd [IPCP ConfAck id=0x3 <addr 10.200.16.23> <ms-dns1 211.xxx.xxx.xxx> <ms-dns3 211.xxx.xxx.xxx>] 07-02 20:40:01.970 1643 1643 W pppd : Could not determine remote IP address: defaulting to 10.64.64.64 07-02 20:40:01.980 1643 1643 I pppd : local IP address 10.200.16.23 07-02 20:40:01.980 1643 1643 I pppd : remote IP address 10.64.64.64 07-02 20:40:01.980 1643 1643 I pppd : primary DNS address 211.xxx.xxx.xxx 07-02 20:40:01.980 1643 1643 I pppd : secondary DNS address 211.xxx.xxx.xxx 07-02 20:40:01.980 1643 1643 D pppd : Script /etc/ppp/ip-up started (pid 1682) 07-02 20:40:02.060 1643 1643 D pppd : Script /etc/ppp/ip-up finished (pid 1682), status = 0x0
|
接着又发了一个地址配置的请求, 在收到服务端的配置后,并相互确认后最终拿到了正确的IP地址/DNS地址:
1 2 3 4 5 6
| sent [IPCP ConfReq id=0x2 <addr 0.0.0.0> <ms-dns1 0.0.0.0> <ms-dns3 0.0.0.0>] rcvd [IPCP ConfNak id=0x2 <addr 10.200.16.23> <ms-dns1 211.xxx.xxx.xxx> <ms-dns3 211.xxx.xxx.xxx>] sent [IPCP ConfReq id=0x3 <addr 10.200.16.23> <ms-dns1 211.xxx.xxx.xxx> <ms-dns3 211.xxx.xxx.xxx>] rcvd [IPCP ConfAck id=0x3 <addr 10.200.16.23> <ms-dns1 211.xxx.xxx.xxx> <ms-dns3 211.xxx.xxx.xxx>]
|
单纯从PPPD拨号的LOG来看, 流程上就有不小的差异: 异常情况迟迟收不到远端(这里就是运营商的服务器)的IP地址协商的请求ConfReq
, 而发出去的IPCP ConfReq
参数协商请求一直拿到了假的DNS地址10.11.12.13/14
(这个地址实际是modem产生的不是来自网络), 对端回过来的地址一直都是全零0.0.0.0
, 导致客户端一直发送ConfReq
,直到远端(remote peer)的NAK
超过最大的次数(PPPD代码默认是5)后,才停止DNS的协商过程(看pppd的代码是默认关闭了对DNS的协商请求).
那么问题的根源在哪里了? 抓了modem的日志给供应商分析才发现, 本端(local peer)在PDN(Packet Data Network)数据链接尚未建立的情况下(相当于终端与运营商还没有建立物理上的通路)就去发起IPCP参数协商了, 此时modem还没有拿到IP地址和DNS地址, 因此就给了一个假的DNS地址了. 等到几秒钟后PDN建立成功, 运营商网络正常下发分配的IP地址与DNS地址后, modem却没有主动下发正确的DNS地址, 而本端的pppd也没有再次请求DNS地址, 这样到了PPP拨号完成本地拿到的始终是一个假的DNS地址.
找到了问题的原因之后, 解决问题就好办了. 大概有这么几个解决方案:
- 在modem侧修改: 如果PDN链接建立成功后, 主动下发正确的DNS地址(运营商给的DNS地址)
- 在本端的PPPD中修改: 如果发现拿到的DNS地址为
10.11.12.13/14
, 则在接收到远端的NAK
时将该地址拒绝, 并且在下次参数协商时主动告诉远端自己没有拿到DNS地址, 这样远端会在下一个NAK
中主动将正确的DNS发送过来(如果已经有了的话)
- 在PPPD拨号时将参数
connect-delay
设置为5000
(5s, 默认是1000
), 这个相当于尽量等待PDN的链接, 等成功后再进行参数协商. 这个方案只能在一定程度上缓解问题发生的概率, 并不能从根本上解决问题.
- 如果收到假的DNS地址
10.11.12.13/14
, 则强制修改为正确的运营商DNS地址(如果运营商DNS地址发生变化, 又会出问题, 不是终极解决方案)
目前采用了方案二, 初步验证可以拿到正确的DNS地址.