3G模块PPPD拨号流程分析

来源:互联网 发布:ubuntu14.04安装mysql 编辑:程序博客网 时间:2024/04/24 02:38

1       案例描述

现在公司的前端产品很多都使用3G模块来作为网络连接的接口,而3G拨号使用PPPD程序,了解拨号程序的流程对解决3G连接网络的问题会有很大的帮助,鉴于此重要性,对PPPD的拨号流程做一个简要的分析。

因为PPPD程序不依赖与硬件平台,这里没有指定具体应用的场景,目的在于说明这个拨号程序的通用性。

目前公司平台采用的这个程序的版本是ppp-2.4.3。

本文只对PPPD的拨号流程进行说明,对拨号程序所用的详细参数以及所使用协议的不做具体说明。

 2       案例分析

总体说明

PPPD 是一个用户空间的后台服务进程(daemon),负责与3G模块进行通信会话来进行必要的初始化设置,然后开始按照协议所要求的步骤进行拨号。初始化设置是由PPPD自带的一个辅助工具CHAT程序来完成的,这个程序利用AT指令和3G模块进行通信,主要是交互一些拨号的参数设置、进行拨号的用户名和密码、是否采用数据加密等连网必不可少的参数;之后PPPD开始正常的拨号工作。下面开始分析这个拨号的流程。

补充说明:AT指令是配置3G模块的通用指令,一般通过RS232等串行线路发送到3G等无线模块,格式以及举例如下:

AT+CGMI?    -------给出模块厂商的标识

AT---------是AT指令的开始符号,后面的+号表示后面有具体的配置参数;

CGMI-----这就是具体的配置参数,这个参数后面的?号标示是向3G查询;

关于3G的AT指令,业界推出了一些标准的指令集合,这些指令在任何厂家的任何模块上都可以使用,也有一些是3G模块生产厂家自己规范的一些AT指令,这些指令就只能在特定的模块上才能使用。

2.2     PPPD拨号流程

下面结合PPPD程序的代码对拨号流程做一下分析。

主程序是pppd_start,这个函数首先是做一些初始化的工作:

void *pppd_start(void *arg)

{

 ……………

  for (i = 0; (protp = protocols[i]) != NULL; ++i)

                (*protp->init)(0);

tty_init();

………

首先是对几个所使用的协议进行初始化,我们这里所用到的分别是lcp_protent、chap_protent、ipcp_protent。初始化的工作基本一致,就是建立各个协议的状态机进行默认设置,为以后网络的维护做好准备;tty_init()是对3G模块所使用串口进行设置,包括所使用的默认拨特率、停止位、是否校验等,在这个函数里还有一个操作就是设置好网络连接所用的通道:

the_channel = &tty_channel(这是在tty_init函数里),这个所谓的连接通道是3G连接的重要函数,后面有这个通道的使用说明;

在完成一些初始化后,正式开始进行协议的协商:

……

pppd_start ->lcp_open();: 打开LCP协议状态机,设置状态为STARTING;

各个协议的状态机分别是-----

STARTING 、INITIAL、CLOSED、CLOSING、STOPPED、OPENED。在上面的初始化时把所有的协议状态机都设置为INITIAL。这里打开LCP协议就是把状态设置为STARTING。接着往下看程序,还是在pppd_start()主函数里:

pppd_start ->start_link();这个函数比较重要,我们把他展开:

void start_link(unit)

{        

char *msg;

        new_phase(PHASE_SERIALCONN);

        devfd = the_channel->connect();

        msg = "Connect script failed";

        if (devfd < 0)

                goto fail;

 

        /* set up the serial device as a ppp interface */

        /*

         * N.B. we used to do tdb_writelock/tdb_writeunlock around this

         * (from establish_ppp to set_ifunit).  However, we won't be

         * doing the set_ifunit in multilink mode, which is the only time

         * we need the atomicity that the tdb_writelock/tdb_writeunlock

         * gives us.  Thus we don't need the tdb_writelock/tdb_writeunlock.

         */

        fd_ppp = the_channel->establish_ppp(devfd);

        msg = "ppp establishment failed";

        if (fd_ppp < 0) {

                status = EXIT_FATAL_ERROR;

                goto disconnect;

        }

        /*

         * Start opening the connection and wait for

         * incoming events (reply, timeout, etc.).

         */

        if (ifunit >= 0)

                notice("Connect: %s <--> %s", ifname, ppp_devnam);

        else

                notice("Starting negotiation on %s", ppp_devnam);

        add_fd(fd_ppp);

        status = EXIT_NEGOTIATION_FAILED;

        new_phase(PHASE_ESTABLISH);

        lcp_lowerup(0);

        return;

disconnect:

        new_phase(PHASE_DISCONNECT);

        if (the_channel->disconnect)

                the_channel->disconnect();

fail:

        new_phase(PHASE_DEAD);

        if (the_channel->cleanup)

                (*the_channel->cleanup)();

}

首先调用the_channel->connect(),这是个函数指针,实际调用的是connect_tty(),(这个函数在上面pppd_start->tty_init里进行了设置)他设置一些TTY通道的参数,然后开辟一个线程chat_main,发送AT指令对3G进行配置;一切正常后从connect_tty返回到start_link继续往下执行;

start_link->the_channel->establish_ppp(devfd)

下面把establish_ppp()函数展开:

int tty_establish_ppp (int tty_fd)

{

………

       ret_fd = generic_establish_ppp(tty_fd);

………

}

一个重要的函数generic_establish_ppp()执行,然后generic_establish_ppp->make_ppp_unit(),在make_ppp_unit()函数里:

……

ppp_dev_fd = open("/dev/ppp", O_RDWR),打开/dev/ppp

ioctl(ppp_dev_fd, PPPIOCNEWUNIT, &ifunit)

建立一个网络接口ppp0, 如果网络接口成功建立后会显示:

notice("Connect: %s <--> %s", ifname, ppp_devnam)==Connect: serial0 <--> /dev/ttyUSB0 ,说明网络接口已经建立; (这个时候可以在SHELL下ifconfig看到这个接口);之后再次返回到start_link执行;

 

接下来开始发起LCP通信:

start_link->lcp_lowerup():

这个函数首先和内核的PPP协议进行协商,主要是MTU等链路参数,协商后设置LCP的DELAYED_UP标志,把lcp_delayed_up函数设置给一个定时器,这个定时器会根据DELAYED_UP标志周期执行之前赋给他的函数,就是这个lcp_delayed_up,在这里函数里将主动发起LCP协议请求,之后等待对端的回应。start_link函数结束返回到 pppd_start()主函数继续执行:

接下来,主函数pppd_start会进入一个无限循环,这个循环主要执行两个函数handle_events()、get_input();

handle_events主要处理接收到的信号,还有一个功能就是执行定时器的函数,前面曾经设置过一个lcp_delayed_up函数,那么建立连接首先执行这个函数;

get_input主要是负责处理接收到的数据包,这个函数下面会介绍。先来看lcp_delayed_up:

 

调用lcp_delayed_up->fsm_lowerup,在上面的lcp_open()函数里LCP的状态已经是STARTING,所以根据这个状态调用fsm_sconfreq(),发送LCP初始化配置请求,然后LCP状态设置为:REQSENT,函数返回;

fsm_sconfreq先组建一些连接用的参数,然后调用fsm_sdata(f, CONFREQ, f->reqid, outp, cilen),通过/dev/ppp->串口发送请求给3G;然后设置一个超时函数给定时器,一旦发送的这个LCP请求没有回应,则重新发送。

  如果没有超时,那么会收到一个LCP请求的回应:rcvd [LCP ConfReq id=0x0,这个时候数据的接收get_input函数来处理的,那么是如何收到对端发来的数据呢?就是这个get_input(),现在来看一下:

首先阻塞的读/dev/ppp,一旦收到一个数据包,检查包里的协议,看是哪种协议数据(在Establishment Phase阶段,只接收LCP的协议包,其他的就丢弃);然后调用lcp_input进行实际的数据接收处理;lcp_input->fsm_input,在这个函数里根据收到包的请求命令进行分别处理:比如:rcvd [LCP ConfReq id=。。。。。。;那么就调用fsm_rconfreq()进一步处理,如果接收正确,则会调用fsm_sdata()发送CONFACK命令;然后设置LCP为ACKSENT

然后等待数据包接收;当接收到一个ConfAck的包后,调用fsm_rconfack时,设置LCP为OPENED,然后lcp_up->lcp_echo_lowerup,注意,此时会调用ipcp_lowerup,设置IPCP为CLOSED!!!之后发送sent [LCP EchoReq id;之后返回到lcp_up,执行link_established,这个函数设置PPP为PHASE_AUTHENTICATE认证阶段,开始加密等认证;之后返回;

当接收到一个rcvd [CHAP Challenge时,调用chap_input-》chap_respond,发送一个CHAP_RESPONSE,sent [CHAP Response id=0x1,然后继续接收数据包;

当收到一个rcvd [CHAP Success后,调用chap_handle_status,这个函数打印CHAP authentication succeeded

然后执行auth_withpeer_success,他继续打印notice("%s authentication succeeded", prot);

执行network_phase,network_phase->start_networks,改变PPP为PHASE_NETWORK状态,依次执行各个协议的open函数:这个时候IPCP已经为CLOSED的,所以会发送sent [IPCP ConfReq,并设置IPCP为REQSENT

当接收到一个rcvd [IPCP ConfNak时,会发送sent [IPCP ConfReq id=0x2,再次请求

当再次收到rcvd [IPCP ConfReq,接着发送sent [IPCP ConfNak id=0x0 <addr 0.0.0.0>]

当接收到rcvd [IPCP ConfAck id=0x4时,调用IPCP_UP,显示

    notice("local  IP address %I", go->ouraddr);

    notice("remote IP address %I", ho->hisaddr);

整个的协议协商过程用图表表示为:

 

Send lcp confreq

Send lcp ack

Send chap confreq

Send chap ack

Send ipcp confreq

Send chap ack

Send ipcp confreq

Send ipcp ack

3G网络基站

拨号端

 

 

 

 

 

                         图2-1  PPPD协议协商流程图

   。说明:箭头表示是数据的发送方向

 

此时,已经从3G网络获得动态分配的IP地址,表明已经建立连接,可以象使用普通以太网那样使用3G网络的接口。

从上面的代码中看到,网络的连接是PPPD主动发起的,就是函数start_link,之后就是根据所接收的状态是否满足要求进而进行下一阶段的工作。

2.3     PPP链路的工作过程

上面的拨号过程中使用到了PPP拨号的几个不同阶段,这里做一个简单的介绍

(1) 链路不可用阶段(Link Dead Phase)在最开始,整条链路处于链路不可用状态,此阶段有时也称为物理不可用阶段,PPP链路都需从这个阶段开始和结束,当通信双方的两端检测到物理线路激活时,就会从当前这个阶段进入到链路建立阶段

(2) 链路建立阶段 (Link Establishment Phase) 在此阶段,PPP链路将进行LCP相关协商,协商内容包括工作方式,认证方式,连路压缩等,LCP在协商成功后进入Opened状态,表示底层链路已经建立,如果链路协商失败,则会返回到第一阶段,在链路建立阶段成功后,如果配置了PPP认证,则会进入认证阶段,如果没有配置PPP认证,则会直接进入网络层协议阶段

(3) 认证阶段 (Authentication Phase) 在此阶段,PPP将进行用户认证工作,PPP支持PAP和CHAP两种认证方式,关于这两种认证方式在后面将会详细介绍,如果认证失败,PPP链路会进入链路终止阶段,拆除链路,LCP状态转为DOWN,如果认证成功就进入网络层协议阶段

(4) 网络层协议阶段 (Network-Layer Protocol Phase) 一旦PPP完成了前面几个阶段,每种网络层协议(IP,IPX等)会通过各自相应网络控制协议进行配置,只有相应的网络层协议协商成功后,该网络层协议才可以通过这条PPP链路发送报文,对于IPCP协议,协商的内容主要包括双方的IP地址等

(5) 链路终止阶段 (Link Termination Phase) PPP能在任何时候终止链路,载波丢失,认证失败后用户人为关闭链路等情况均会导致链路终止,PPP协议通过交换LCP的链路之中报文来关闭链路,当链路关闭时,连路层会通知网络层做相应的操作,而且也会通过物理层强制关断链路

通过学习PPPD拨号过程,可以更好的理解3G的连接情况,为定义拨号过程以及拨号成功后的数据通信可能出现的问题,有比较清晰的解决思路。

原创粉丝点击