ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LWIP PPP拨号实战:4G模组CHAP认证避坑指南

LWIP PPP拨号实战:4G模组CHAP认证避坑指南 有段时间我一直在跟4G模组较劲。客户现场的充电桩主控板要上云业务层跑MQTT但既要有线网口接入又要支持蜂窝网络两边不能各搞一套协议栈。当时我就在LWIP里把PPP协议栈完整跑了一遍从lwipopts.h里的宏开关到串口底层对接再到CHAP认证的坑前前后后折腾了快两周。这篇文章把这套流程完整拆给你看重点是CHAP认证那一段网上资料少踩坑多我这里一次性说清楚。这篇文章适合谁看就是那些要在STM32、ESP32或者其他MCU上通过4G Cat.1/Cat.4模组如EC200U、Air724UG、ML302这类拨号上网做产品的兄弟。如果你正在用LWIP又需要让上层代码不区分“有线网口”和“4G拨号”那这篇文章应该能帮你省下不少时间。先说明我用的lwIP版本是2.1.x不同版本宏命名和API会有差异但整体流程是一样的具体以你工程里的头文件为准。1. 先搞清楚这个需求设备侧PPP拨号是怎么个事1.1 什么场景下会用到LWIP的PPP我之前给一个充电桩项目做联网方案设备本身是STM32F407 DM9161以太网PHY走的是LWIP RT-Thread以太网口用来接现场工业交换机。后面客户加了一个要求设备得支持4G备份链路现场没网线的时候自动切到蜂窝网络。这时候问题来了要不要在4G模组上用AT指令的TCP/IP协议栈如果只用AT指令MQTT客户端、CoAP、LwM2M这些上层协议都得单独适配一套底层socket逻辑无法复用。而且AT指令的TCP/IP栈通常只提供透传通道UDP广播、DNS解析、多连接管理都有不少限制。所以我选择了在LWIP里直接跑PPP——把4G模组当成一个“串口调制解调器”PPP协议栈在MCU侧跑底层通过串口和模组交互。这样对上层应用来说就是一个普通的netif接口跟以太网口没有区别MQTT、HTTP、CoAP全部照跑。这种场景在目前的工业设备里很常见DTU、RTU、充电桩、智能网关、便携式采集器基本都是MCU 4G模组 LWIP协议栈的组合。包括很多用Linux做嵌入式的朋友其实也会遇到PPPoS的配置需求只不过Linux下pppd是现成的底层modem对接要自己写。LWIP里这一套更轻量也更适合资源受限的MCU环境。1.2 先对齐三个术语PPP、PPPoS、CHAPPPPPoint-to-Point Protocol点对点协议是数据链路层协议运行在串口、ISDN、拨号等点对点链路上。它做的事情包括链路建立LCP、身份认证PAP/CHAP、网络层协议协商IPCP三个主要阶段。今天我们用的4G模组内部已经把PPPoE、TCP/IP那套东西都封装掉了我们通过串口发AT指令让它进入“数据模式”后剩下的就是MCU和运营商网络侧之间的PPP协商。LWIP里对PPP的支持分为两种承载类型PPPoSPPP over Serial串口PPP和PPPoL2TP。我们日常用的90%是PPPoS核心文件是pppos.c它负责把PPP帧通过串口字节流发出去、收回来。需要注意的是LWIP从2.0开始把PPP模块重写过很多老的写法已经变了网上不少教程还是1.x时代的代码直接搬过来编译都不一定能过。我建议你打开自己工程里的ppp.h和pppos.h以这些头文件为准。CHAPChallenge Handshake Authentication Protocol挑战握手认证协议。它和PAP的区别在于PAP是把用户名密码明文发过去CHAP则不会直接传密码而是用MD5对“Challenge挑战值 用户名 密码”做一次摘要运算再把摘要回传。密码本身不出本地安全性高不少。很多运营商的专用APN要求CHAP认证这也是我在实际项目里主要踩坑的地方。1.3 LWIP跑PPP和AT指令直连TCP/IP哪个更靠谱这个问题每次聊都会有人问。我的建议是如果只是做简单的透传上报、流量不大、业务协议不依赖本地socket那用AT指令自带的TCP/IP栈完全够了开发量小稳定性也容易保证。但如果你想在设备端跑完整的MQTT客户端、要做TLS、要同时维护多条连接、还要在网口和4G之间无缝切换那么尽量用LWIP PPP。理由很简单上层代码只认netif接口不用管底层到底是以太网还是蜂窝网代码复用率最高。当然LWIP跑PPP也有代价。首先是内存PPP协议栈本身占不少RAMppp_pcb结构体、收发缓冲、IP重组缓冲加起来可能好几个KB到十几KB不等。其次是状态机PPP协商是异步的LCP、认证、IPCP三个阶段都可能失败你得做好超时重试和状态管理。再一个就是和RTOS的配合串口收包、协议栈处理、应用层监控多个上下文切换要小心处理不能直接用裸中断喂给pppos_input就完事。但整体来说如果项目周期长、业务要扩展走LWIP跑PPP是更稳妥的选择。下面我把每一步怎么落地都写出来。2. 动手之前的准备把LWIP的PPP模块裁剪出来2.1 硬件环境和调试工具先列一下我实际调试用的环境你做个参考MCUSTM32F407ZGT6内部Flash 1MBRAM 192KB实际可用大约128KB左右4G模组移远EC200U-CNCat.1支持PPP拨号串口对接操作系统RT-Thread 4.1.x串口设备驱动 DMA收发LWIP版本2.1.2集成在RT-Thread软件包中开发工具VS Code Embedded插件 ST-Link调试串口调试助手用于AT指令交互调试CCC工具很重要建议准备一个TTL转USB模块把模组的TX/RX同时引出来。很多时候只靠日志里“Failed to connect”这种提示根本定位不了问题。我后来直接买了一个USB转双串口的小板子一路接MCU调试串口一路接模组串口两个串口助手同时开着能清清楚楚看到AT指令交互和PPP报文时序。还需要强调一下如果手头有PC上能用的3G/4G上网卡可以先在Windows下手动拨号确认运营商侧的认证类型、APN参数再把这些参数原样搬到MCU上。这个步骤能帮你排除“是不是运营商侧配置就有问题”的疑问。2.2 打开LWIP PPP相关宏定义一半的工程在这里就废了lwIP的裁剪是出了名的“宏地狱”PPP模块尤其明显。很多人代码写好了编译也过了就是拨不上最后发现是宏没开全。在lwipopts.h里和PPP相关的主要宏如下以2.1.x常见命名为例希望确认一下你的版本对应的宏/* 打开LWIP PPP总开关 */ #define LWIP_PPP 1 #define PPP_SUPPORT 1 /* 选择PPP承载类型串口PPP */ #define PPP_SUPPORT_PPPOS 1 /* 认证方式支持 */ #define PPP_SUPPORT_PAP 1 #define PPP_SUPPORT_CHAP 1 #define PPP_SUPPORT_MSCHAPv2 0 /* 网络层选项IPCP需要 */ #define LWIP_DNS 1 /* 调试开关出问题时打开 */ #define PPP_DEBUG LWIP_DBG_ON #define CHAP_DEBUG LWIP_DBG_ON这里有几个非常容易踩的坑。第一个是LWIP_PPP和PPP_SUPPORT两个宏都要打开有些旧版本工程里只有其中一个结果PPP文件根本没编译进去。第二个是PPP_SUPPORT_CHAP必须显式打开很多lwIP默认配置里CHAP是关的、PAP是开的这时就算你在代码里把认证方式设置成CHAP也会回退到PAP甚至直接认证失败。第三个是PPP_DEBUG和CHAP_DEBUG这两个调试宏建议开发阶段打开正式发布再关掉否则日志输出的开销会影响串口时序。顺便说一句如果你是STM32CubeMX生成工程CubeMX生成的LWIP默认只配置了ETH以太网接口完全没有PPPoS的入口。你需要手动在lwipopts.h里改上面这些宏并且在代码里手动创建PPPoS接口。这也是不少新手觉得“STM32上配不了PPP”的主要原因——不是LWIP不支持是CubeMX的图形化界面根本不提供这个配置项。2.3 注册PPPoS网络接口代码怎么挂上去以LWIP 2.1.x为例核心流程是先调用pppos_create创建一个PPP控制块然后注册ppp_link_status_cb回调再调用pppapi_set_auth设置认证参数最后用pppapi_connect发起连接。大致代码如下struct netif ppp_netif; ppp_pcb *g_ppp_pcb NULL; /* 底层串口发送函数需要你自己实现 */ static u32_t prv_ppp_output_cb(struct ppp_pcb *pcb, const u8_t *data, u32_t len, void *ctx) { /* 把data通过串口DMA发送给4G模组 */ uart_send_bytes(ctx, data, len); return len; } /* 链路状态回调 */ static void prv_ppp_status_cb(struct ppp_pcb *pcb, int err_code, void *ctx) { switch (err_code) { case PPPERR_NONE: /* 链路已建立PPP网络接口激活 */ if (g_ppp_pcb ! NULL) { netif_set_up(ppp_netif); } break; case PPPERR_AUTHFAIL: /* 认证失败 */ debug_printf(PPP auth failed\r\n); break; case PPPERR_CONNECT: /* LCP/认证/IPCP全部通过进入数据模式 */ break; case PPPERR_USER: case PPPERR_PROTOCOL: case PPPERR_IPCP: default: /* 其他错误需要根据错误码进一步排查 */ break; } } void ppp_bring_up(void) { struct netif *nif ppp_netif; /* 注册网络接口 */ netif_add(nif, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY, NULL, prv_ppp_if_init, prv_ppp_if_input); /* 创建PPPoS协议栈实例 */ g_ppp_pcb pppos_create(nif, prv_ppp_output_cb, prv_ppp_status_cb, NULL); /* 设置认证方式这里使用CHAP */ pppapi_set_auth(g_ppp_pcb, PPPAUTHTYPE_CHAP, username, password); /* 发起PPP拨号 */ pppapi_connect(g_ppp_pcb, 0); }pppos_create的第一个参数是LWIP的netif结构体指针第二个参数是串口发送回调第三个参数是链路状态回调。这里需要说明的是回调函数名、错误码枚举在不同版本里有小差异但整体逻辑一致。prv_ppp_if_init和prv_ppp_if_input是标准LWIP接口初始化函数和输入函数可以参考以太网接口那套写法不过这里具体逻辑比较简单因为PPP模块内部已经做了大部分工作。2.4 底层串口收发对接中断里千万别直接喂协议栈PPPoS对底层串口的要求是可靠、不丢字节、速率稳定。我在这个项目里用的是串口DMA IDLE中断。收到一帧PPP数据后利用空闲中断来判断帧结束然后把整包数据交给一个环形缓冲最后唤醒PPP任务去调用pppos_input。这里有个非常重要的经验不要在串口接收中断里直接调用pppos_input。原因是PPP协议栈内部有重入和锁要求如果你在中断上下文直接调很可能造成死锁或者状态错乱。正确做法是开辟一个线程挂起等待信号量串口DMA接收到完整一帧数据后把数据拷入缓冲释放信号量线程里拿到数据后再调用pppos_input。发送方向也一样prv_ppp_output_cb会在协议栈上下文中被调用你只需要把数据通过串口DMA发出去就行不需要实时等待发送完成。如果用的是轮询发送要注意发送耗时不要太长否则会拖垮整个协议栈的时序。我在RT-Thread下是基于rt_device_write实现加了互斥锁实测可以稳定运行。3. 拨号全流程拆解AT指令与PPP协商的分工3.1 先用AT指令把模组轰进PPP数据模式很多人以为LWIP里开了PPPoS直接调pppapi_connect就能拨号其实不是。模组还是AT指令状态必须先通过一组合适的AT指令把它切换成“PPP数据模式”PPP协议栈才有机会开始工作。整个过程分两步第一步用AT指令做基本配置第二步发拨号指令等待模组返回CONNECT然后真正启动PPPoS。我用的拨号流程是这样/* 1. 关闭回显减少调试干扰 */ AT /* 2. 查询/设置APN关键参数 */ ATCGDCONT1,IP,cmnet /* 3. 如果需要设置用户名密码专用APN加上下面这条 */ ATCGDCONT1,IP,your.apn,user,pass /* 4. 开始拨号 */ ATD*99#发完ATD*99#之后模组会返回CONNECT然后串口全部转入PPP数据模式。这个返回的CONNECT不能通过AT解析的方式处理应该在拨号状态机里等待。有些模组会返回CONNECT 115200表示协商的波特率注意别把这些额外字符写死否则判定逻辑会出问题。这里还有个大坑APN必须在PPP链路建立前配好。如果APN没设置或者设置错误LCP和IPCP协商可能都能成功因为LCP在链路层不关心APN但你拿到IP后会发现上不了网。我遇到过一次就是IPv4地址拿到了DNS也拿到了但TCP连接就是建立不了后来查资料才知道是APN配错了。运营商通过APN来选路APN不对数据包根本不会走到对应的PGW网关上。APN里的用户名密码和PPP认证的用户名密码要分清楚。APN的用户名密码是拨号时告诉模组的用于运营商接入点认证PPP认证的用户名密码是PPP协议栈在LCP协商后、进入认证阶段时回传的。很多专用APN两边用的是同一组账号但有些模组场景下APN认证由模组内部完成PPP认证则必须额外设置容易搞混。3.2 LCP协商链路层选项不谈拢后面全是白搭进入PPP数据模式后MCU侧的LWIP会主动发送LCP Configure-Request里面包含MRU最大接收单元、Magic Number、认证协议等选项。运营商网络侧会回复LCP Configure-Ack或者Configure-Nak/Reject。如果收到Nak/RejectLWIP会自动调整协商选项这是正常流程。LCP阶段最容易出的问题有两个。第一个是认证协议匹配不上。LWIP通过pppapi_set_auth设置认证方式时如果指定PPPAUTHTYPE_CHAP在LCP协商中就会要求对端接受CHAP如果对方坚持PAP协商会失败或降级。实际运营商网络侧往往两种都支持但也见过只支持其中一种的情况。遇到这种问题先尝试把认证方式改成PPPAUTHTYPE_ANY让双方自动协商能通后再收紧到CHAP。第二个是MRU设置不一致。LWIP默认MRU是1500但4G蜂窝链路上运营商网络侧可能下发更小的MRU。如果两边MRU不一致大包会在链路上被分片导致性能下降甚至丢包。LWIP里面MRU可以通过ppp_set_mru接口或者自定义宏调整我一般建议设置成1400或者1280能有效规避很多蜂窝网络下的MTU黑洞问题。有些开发者习惯用抓包工具看LCP交互。其实不用那么麻烦把PPP_DEBUG打开LWIP的日志会把LCP协商的收发选项打印出来包括协商到什么地址、认证类型是什么、MRU是多少。只要日志足够清晰大部分问题都能直接看出来。3.3 IPCP协商拿到IP、DNS的第一现场LCP协商通过、认证也过了紧接着就是IPCP阶段。IPCP是网络控制协议NCP的一种负责给PPP链路分配IP地址和DNS服务器地址。这一阶段通过后prv_ppp_status_cb会收到PPPERR_CONNECT此时netif才算真正可用。IPCP阶段最常见的坑是LWIP没有开启DNS功能导致网络侧下发的DNS地址被丢弃。你在lwipopts.h里如果没有定义LWIP_DNS1IPCP协商时虽然可能还是会收到DNS配置但netif内部不会保存上层域名解析就永远是“找不到主机”。这个问题挂得很隐蔽因为TCP连接如果直接用IP地址是通的DNS解析就不对。另一个容易忽略的点是本地IP地址的更新。PPPoS链路建立后本地IP地址是由网络侧分配的比如10.xxx.xxx.xxx这种私有地址。你需要在状态回调里把netif-ip_addr打印出来确认获取到的地址不全是0。如果一直停留在0.0.0.0说明IPCP协商有问题最可能的原因是APN没配好或者账号权限不对。IPCP协商完成后链路状态是“数据模式”可以正常收发TCP/IP报文。如果你发现netif已经up但应用还是连不上服务器别急着怀疑协议栈先检查一下链路层默认路由。在LWIP里需要确认ppp_netif被设置成默认路由否则数据包可能走以太网口也就是我常说的“明明PPP通了数据却从网口出去了”。可以通过netif_set_default(ppp_netif)来设置。4. CHAP认证配置与避坑指南4.1 CHAP是怎么认证的三步握手和MD5CHAP全称Challenge Handshake Authentication Protocol核心特点是“挑战-响应”。它不像PAP那样把用户名密码明文发给对方而是分三步完成认证方这里就是运营商网络侧向被认证方我们的设备发送一个Challenge报文里面包含一个ID、一段随机数Challenge值和认证方的主机名。设备端收到Challenge后用MD5算法对“ID 密码 Challenge值”这段数据进行摘要计算然后把结果连同用户名一起作为Response发给认证方。认证方验证Response如果和自己计算的MD5值一致就回复成功否则回复失败并可能直接断开链路。整个过程里密码始终没有在链路上传输传输的只是密码参与哈希计算后的结果。这也是为什么CHAP比PAP安全得多。CHAP还支持在链路存活期间周期性重新挑战即使有人抓包抓到了Response也无法逆向出密码而且下一次挑战值变了每次Response都不一样。在LWIP源码里CHAP相关逻辑在netif/ppp/chap.c里主要是chap_challenge和chap_response两个核心函数。你可以看到它在拿到随机的Challenge之后调用了md5_into来计算摘要。顺便说一句LWIP自带MD5实现不需要你额外引入加密库。4.2 CHAP在LWIP里的配置与代码位置代码上看设置CHAP认证就一行pppapi_set_auth(g_ppp_pcb, PPPAUTHTYPE_CHAP, username, password);这行代码必须在pppapi_connect之前调用而且要确保lwipopts.h里已经打开PPP_SUPPORT_CHAP。否则编译不会报错但运行时会发现CHAP报文发送不出去或者直接回退成PAP。如果你希望CHAP和PAP都支持、让运营商侧来选择可以把认证方式改成pppapi_set_auth(g_ppp_pcb, PPPAUTHTYPE_ANY, username, password);但我的建议是如果业务允许还是坚持用CHAP。一是安全性高二是现在运营商侧基本都支持CHAP如果PPPoS链路不支持CHAP日志里会明确报错你一眼就能发现问题。用PPPAUTHTYPE_ANY的时候日志有时候不够明确反而不好排查。4.3 CHAP实战避坑清单能踩的坑我基本都踩了这块内容我希望你看之前先收藏因为每一条都是我用几天时间换来的教训。第一宏没开全导致CHAP不可用。前面提过多次但我还是想再强调一遍PPP_SUPPORT_CHAP这个宏如果不打开你用PPPAUTHTYPE_CHAP设置认证参数运行时会直接走PPPAUTHTYPE_NONE或者报错“CHAP support not compiled in”。这个错误在日志里还算明显但如果只是log级别没打开你根本看不到。第二用户名或密码里包含特殊字符或不可见字符。PPP协议里用户名和密码是字节流但很多模组和运营商侧对字符集有要求。最常见的是密码里不能有换行符或者\0。如果你从配置参数文件里读取密码一定要确认字符串以\0结尾否则MD5计算会截断或者多算认证必失败。第三用户名带了域名后缀问题。有些APN的用户名是“userdomain”格式CHAP计算时要原样把domain一起算进去。如果你只填了user可能认证失败。这个要看运营商提供的账号格式别自己想当然地截掉。第四拨号账号和APN账号混用。我曾经在一个项目里运营商给了两个账号一个用于APN拨号一个用于CHAP认证。我当时图省事只配置了一个结果LCP协商和IPCP都能过唯独CHAP老失败。后来才知道是账号配错了一个。建议你在调试初期把APN、用户名、密码三个信息单独打日志出来对照确认。第五认证方式被运营商侧拒绝后LWIP会自动尝试其他方式导致日志看起来“明明是CHAP实际走了PAP”。如果你看到日志里出现“Peer/Protocol option unacceptable”之类的字样说明协商类型没匹配上。想强制CHAP时把认证方式设置成PPPAUTHTYPE_CHAP并且关闭PAP支持。第六MD5计算对大小写敏感。用户名密码的大小写必须和运营商侧完全一致CHAP没有“忽略大小写”这种选项。我遇到过因为配置文里密码首字母被自动转成大写导致认证失败的这种问题不看代码根本想不到。第七认证回调错误码的含义要分清。PPPERR_AUTHFAIL表示认证服务器明确拒绝了认证PPPERR_PROTOCOL可能表示CHAP协议阶段某帧格式错误PPPERR_IPCP则是认证通过后IPCP阶段出了幺蛾子。不要一看到状态回调报错就以为是账号密码问题先对准错误码。5. 调试实录常见问题与排查思路5.1 卡在LCP协商反复超时重传我调试时遇到最多的问题是ATD指令之后模组返回了CONNECT但LCP协商一直卡在Configure-Request超时重传链路始终建立不起来。排查思路是先把底层串口链路确认好。模组返回CONNECT后是否真的进入了数据模式你可以用串口助手直接往模组发PPP报文看不出效果但可以确认的是如果你发现LWIP一直发LCP Configure-Request而模组侧没有任何回复大概率是串口收发方向有问题。再一个常见原因是波特率不匹配。模组默认波特率和MCU串口波特率不一致会导致模组发出的报文MCU解不出来。有些模组支持自动波特率检测但稳定性一般。建议把两边固定在115200或者460800不要用自适应。最后看日志里的LCP选项。如果收到了Configure-Reject说明某个选项不支持LWIP会自动调整。如果收到了Configure-Ack说明LCP通了。这一步假如一直停在Configure-Request发送那多半不是网络问题是底层字节流根本没送到模组。5.2 CHAP认证失败从日志和抓包两头查CHAP认证失败最直观的日志是PPPERR_AUTHFAIL但这个错误码只告诉你“认证失败”不告诉你为什么失败。这时必须开调试日志#define PPP_DEBUG LWIP_DBG_ON #define CHAP_DEBUG LWIP_DBG_ON打开后LWIP会把CHAP的Challenge和Response的字节长度、部分内容打印出来。你可以看到Recevied Challenge的长度、计算出的Response长度。正常情况两边长度应该一致且核心字段非空。如果你觉得日志还不够直观建议临时把模组串口引到PC上用串口抓包工具记录或者用一个USB转双串口板在一个端口捕捉MCU发给模组的数据另一个端口捕捉模组发给MCU的数据然后手动比对CHAP报文结构。CHAP报文帧格式是固定的Code值为1代表Challenge值为2代表Response、Identifier、Length、Value-Size、Value-Data挑战值、Name。如果看到Response里的Name和你设置的用户名不一致八成是用户名设置错。还有一个很隐蔽的问题有些4G模组在收到PPPoS的LCP Configure-Request后会主动帮设备侧完成CHAP认证甚至模组内部有自己的用户名密码配置。这时候你MCU侧如果设置了CHAP反而会和模组内部的认证逻辑冲突。这种模组通常提供AT指令来关闭内部认证或者允许透传PPP帧时绕过内部协议栈。选模组的时候尽量选“支持透传PPP模式”的别被内置协议栈误导。5.3 IP拿到了、引脚也通了但ping不通链路数据通了但无法正常通信这个问题的排查路径比较长我按频率列一下最常见的是MTU问题。PPP链路协商下来MRU可能是1500但蜂窝网络的物理链路实际最大包长可能不到1500。一旦应用发了个超过链路上限的包网络侧静默丢弃TCP就会表现为“连接建立慢、传数据卡住”。解决办法是把MRU调低比如1400甚至1280或者让上层启用的TCP MSS更小。第二常见的是DNS问题。IPCP虽然拿到了IP地址但如果LWIP没开LWIP_DNSDNS服务器地址不会写入netif调用dns_gethostbyname会一直失败。这个在调试时用IP直连能通、域名连不通就能判断。第三是默认路由问题。如果设备上同时有以太网口和PPP接口LWIP只会有一个默认接口。你需要在PPP链路up之后把默认路由切换到ppp接口否则数据包走以太网口出去自然连通不了。这个前面提过但值得注意的是切换路由后要记得在以太网口恢复时做二次切换否则设备会“失联”一会儿。第四是运营商分配的IP可能是内网IP而服务器和客户端之间有防火墙策略限制。这个属于环境问题很多时候不是代码问题。排查时用PC尝试拨同网络对比一下能不能通就能快速定位。5.4 问题速查表把高频故障一次说清楚现象可能原因排查/解决方式拨号后一直发LCP Request无回复串口链路异常、波特率不匹配、模组未进入数据模式确认ATD返回CONNECT检查串口收发和DMA尝试115200固定波特率LCP协商成功认证不通过认证类型不支持、账号密码错误、CHAP宏未开打开CHAP_DEBUG日志核对账号密码确认PPP_SUPPORT_CHAP认证通过但一直不进入IPCPIPCP选项冲突、APN未设置查看LCP协商成功后IPCP报文配置APN检查LWIP_DNSIPCP成功但ping不通网关APN选路错误、MTU过大、默认路由错误核对APN是否可上网降低MRU确认默认路由在ppp接口业务能通但DNS解析失败LWIP_DNS未开启、IPCP未拿到DNS打开LWIP_DNS打印netif里的dns地址运行一段时间后掉线重拨运营商心跳超时、信号弱、链路保活未配置启用LCP Echo保活做周期拨号状态机检查信号质量这个表格是我在自己的多个项目里沉淀出来的每次接到新的PPP相关需求我都会先对着它过一遍。不能保证覆盖所有情况但大概率能帮你少走弯路。说一点最后的体会。PPP拨号这件事说起来就是LCP、认证、IPCP三轮谈判但真做起来每一轮都可能卡你三五天。我个人的项目习惯是把整个拨号过程做成一个状态机状态包括AT_CONFIGURE、AT_DIALING、PPP_CONNECTING、PPP_LINK_UP、PPP_LINK_DOWN再配合LWIP的ppp_link_status_cb回调来驱动状态迁移而不是在回调里直接写业务逻辑。这样代码看起来很清晰出问题也方便在日志里定位。还有一个非常实用的小技巧如果你手头有能正常拨号的PC端工具先用PC把运营商侧支持的认证方式、APN、MRU全部抓一遍然后把这些参数固化成设备端的默认配置。这样可以避免在MCU上反复试错尤其是需要同时支持多种运营商的场景提前把每种SIM卡的参数都验证好后面量产切换运营商就只是个配置下发问题。希望这篇文章能帮你把LWIP里的PPP配置一次搞定。
返回列表