Linux PPP 数据收发流程

 

Linux PPP 数据收发流程编程

 

更新时间:2007-5-4浏览器

 

PPP(Point-to-Point)提供了一种标准的方法在点对点的链接上传输多种协议数据包,它最多见的用途多是传统的拨号上网了(听说如今的宽带接入也有采用PPPOE方式的)。在Linux Mobile Phone上,网络应用程序使用PPP做为与GSM模组之间的通讯协议,最近遇到了一点关于PPP的麻烦,因此花了点时间去研究它。 网络

 

PPP协议确定不是最复杂的网络协议,不过pppd、chat、tty、socket、ccp、chap、pap、eap、ecp、ipcp和不少其它概念搅在一块儿以后,谁都会被搞得晕头转向。我关心的实际上是PPP协议中各个实体之间的协做关系,而不是协议的状态转换或者服务的配置,因为没有找到这方面的资料,只好去读RFC,内核、pppd和一些网络工具的代码。 socket

 

PPP协议提供两个实体之间的数据链路链接的创建、维持和释放,负责流量和差错控制等等功能,因此它应该是属于数据链路层协议的。 async

 

PPP协议之下是以太网和串口等物理层,之上是IP协议等网络层。这里,对于下层,咱们只讨论串口的状况,对于上层,咱们只讨论TCP/IP的状况。发送时,TCP/IP数据包通过PPP打包以后通过串口发送。接收时,从串口上来的数据经PPP解包以后上报给TCP/IP协议层。 ide

 

网络协议是分层实现的,上层通常只须要知道其直接下层,只有在极少数据状况才使用间接下层的接口。好比,彩信、浏览器和邮件等应用程序使用socket接口编程,它们只须要知道TCP/IP协议,而无须要知道PPP协议的存在。这种分层设计简化了协议的实现和应用程序的开发。 函数

 

问题来了:PPP协议不仅是提供了简单的数据链路层功能,它还提供了诸如鉴权(如PAP/CHAP),数据压缩/解压(如CCP)和数据加密/解密(如ECP)等扩展功能。应用程序要求使用透明化,不关心这些扩展功能的存在,而反过来,PPP协议处理模块自己又没法处理这些策略性的东西,由于它不知道用户名/密码,不知道是否要进行压缩,不知道是否要进行加密。 工具

 

怎么办?如何在对应用程序透明的状况下使用扩展功能呢?上帝说,这个问题必须解决!因而pppd就出现了。pppd是一个后台服务进程(daemon),是一个用户空间的进程,因此把策略性的内容从内核的PPP协议处理模块移到pppd中是很天然的事了。pppd实现了全部鉴权、压缩/解压和加密/解密等扩展功能的控制协议。 加密

 

pppd只是一个普通的用户进程,它如何扩展PPP协议呢?这就是pppd与内核中的PPP协议处理模块之间约定了,它们之间采用了最传统的内核空间与用户空间之间通讯方式:设备文件。 spa

 

设备文件名是/dev/ppp。经过read系统调用,pppd能够读取PPP协议处理模块的数据包,固然,PPP协议处理模块只会把应该由pppd处理的数据包发给pppd。经过write系统调用,pppd能够把要发送的数据包传递给PPP协议处理模块。经过ioctrl系统调用,pppd能够设置PPP协议的参数,能够创建/关闭链接。

 

pppd里,每种协议实现都在独立的C文件中,它们一般要实现protent接口,该接口主要用于处理数据包,和fsm_callbacks接口,该接口主要用于状态机的状态切换。数据包的接收是由main.c: get_input统一处理的,而后根据协议类型分发到具体的协议实现上。而数据包的发送则是协议实现者根据须要调用output函数完成的。

 

chat是pppd所带一个辅助工具。呵,它和xchat不是一个类型的,xchat用来与人聊天,而chat用来与GSM模组创建会话。它的实现比较简单,它向串口发送AT命令,创建与GSM模组的会话,以便让PPP协议能够在串口上传输数据包。

 

应用程序经过socket接口发送TCP/IP数据包,这些TCP/IP数据包如何流经PPP协议处理模块,而后经过串口发送出去呢?pppd在make_ppp_unit函数中调用ioctrl(PPPIOCNEWUNIT)建立一个网络接口(如ppp0),内核中的PPP协议模块在处理PPPIOCNEWUNIT时,调用register_netdev向内核注册ppp的网络接口,该网络接口的传输函数指向ppp_start_xmit。

 

当应用程序发送数据时,内核根据IP地址和路由表,找到ppp网络接口,而后调用ppp_start_xmit函数,此时控制就转移到PPP协议处理模块了。ppp_start_xmit调用函数ppp_xmit_process去发送队列中的全部数据包,ppp_xmit_process又调用ppp_send_frame去发送单个数据包,ppp_send_frame根据设置,调用压缩等扩展处理以后,又经ppp_push调用pch->chan->ops->start_xmit发送数据包。

 

pch->chan->ops->start_xmit是什么?它就是具体的传输方式了,好比说对于串口发送方式,则是ppp_async.c: ppp_asynctty_open中注册的ppp_async_send函数,ppp_async_send经ppp_async_push函数调用tty->driver->write把数据发送串口。

 

用户数据发送过程以下图所示:

 

pppd的控制协议数据发送过程以下图所示:

 

 

反过来,接收数据的情形又是如何的?ppp_async.c在初始化时(ppp_async_init),调用tty_register_ldisctty注册了行规程处理接口,也就是一组回调函数,当串口tty收到数据时,它就会回调ppp_ldiscppp_asynctty_receive函数接收数据。ppp_asynctty_receive调用ppp_async_input把数据buffer转换成sk_buff,并放入接收队列ap->rqueue中。

 

ppp_async另外有一个tasklet(ppp_async_process)专门处理接收队列ap->rqueue中的数据包,ppp_async_process一直挂在接收队列ap->rqueue上,一旦被唤醒,它就调用ppp_input函数让PPP协议处理模块处理该数据包。

 

ppp_input函数中,数据被分红两路,一路是控制协议数据包,放入pch->file.rqb队列,交给pppd处理。另一路是用户数据包,经ppp_do_recv/ppp_receive_frame进行PPP处理以后,再由netif_rx提交给上层协议处理,最后经socket传递到应用程序。

 

数据接收过程以下图所示:

 

 

 

PPP协议的流程基本清楚了,不过还有很多细节有待进一步研究。

 

~~end~~