123
分类:
2012-09-13 17:25:11
原文地址:linux ppp pppoe 作者:djking1986
PPP主要由两类协议组成:链路控制协议族(LCP)和网络控制协议族(NCP)。
LCP用于建立、拆除和监控PPP数据链路,NCP主要用于协商在该数据链路上所传输的数据包的格式与类型。同时,PPP还提供了用于网络安全方面的验证协议族(PAP和CHAP)。
ppp是一个分层结构。在底层,它能使用同步媒介(如ISDNH或同步DDN专线),也能使用异步媒介(如基于Modem拨号的PSTN网络)。
在数据链路层,PPP在链路层建立方面提供了丰富的服务,这些服务以LCP协商选项的形式提供。
在上层,PPP通过NCPs提供对多种网络层协议的支持。PPP对于每一种网络层协议都有一种封装格式来区别它们的报文。
PPP协商过程分为几个阶段:Dead阶段,Establish阶段,Authenticate阶段,Network阶段和Termintate阶段,在不同的阶段进行不同协议的协商.只有前面的协商出现结果后,才能转到下一个阶段,进行下一个协议的协商.
1)当物理层不可用时,PPP链路处于dead阶段,链路必须从这个阶段开始和结束.当物理层可用时,PPP在建立链路之前首先进行LCP协商,协商内容包括工作方式是SP还是MP,验证方式和最大传输单元等.
2)LCP协商过后就进入Establish阶段,此时LCP状态为Opened,表示链路已经建立.
3)如果培植了验证(远端验证本地或者本地验证远端)就进入Authenticate阶段,开始CHAP或PAP验证.
4)如果验证失败进入Terminate阶段,拆除链路,LCP状态转为Down;如果验证成功就进入Network协商阶段(NCP),此时LCP状态仍为Opened,而IPCP状态从Initial转到Request.
5)NCP协商支持IPCP协商,IPCP协商主要包括双方的IP地址.通过NCP协商来选择和配置一个网络层协议.当选中的网络层协议配置成功后,该网络层协议就可以通过这条链路发送报文了.
6)PPP链路将一直保持通信,直至有明确的LCP或NCP帧关闭这条链路,或发生了某些外部事件.(例如,用户的干预).
PPPoE:
Point-to-Point Protocol over Ethernet:即一种在以太网内封装PPP协议报文的协议。基于包交换的以太网没有“电路”(circuit)或链接的概念,因此对IP或MAC冲突和安全性都没有保证。使用PPPoE用户之间可以建立虚拟链路,在链路上进行安全的通信。
PPPoE分为两个阶段:
PPPoE discovery:传统的PPP连接是基于点到点的,而在以太网是muti-accesss,即在以太网中的任一节点可以访问其他节点。以太网中的frame包含着目的节点的MAC地址以找到目的节点。 所以在转换PPP frame之前,两个通信节点必须事先知道对方的MAC地址。 PPPeE的discovery阶段就是让以太网中的节点交换通知其MAC地址,并建立一个Session ID供后续的包交换使用。
PPPoE session:当节点之间知道了相互的MAC地址后,就进入了session阶段。
PPP in Linux:
PPP框架:
【1】 pppoe程序:首先pppoe完成PPPoE的发现阶段,即相互通知MAC地址。接着这个程序负责发送/接收所有通过ppp网络接口(如ppp0)的数据包。(需要了解类型为0x8863和0x8864的socket是如何工作的,并要了解数据包是如何通过PTY设备在pppoe和PPP协议栈之间传递的)。
【2】pppd: 与pppoe配合起来完成拨号上网的协商与维护。
【3】/dev/ppp:创建了ppp设备后,ppp过程的数据包经过协议栈的分类,会被传送到该接口的队列内。pppd从该接口读取ppp过程的数据包,然后交给相应的协议栈处理。对于响应的数据包同样写入改设备,设备内会将数据包交给协议栈然后转发出去。
【4】socket: pppoe 的会话与发现阶段数据包对应的以太网类型分别为0x8863和0x8864,因为这两种类型的数据包是由pppoe应用程序通过socket来收发的,所以内核中需要定义这两种类型的socket。
【5】PPP协议栈: 主要负责PPP层的封装、压缩与解压。另外,它还对普通数据包和ppp过程的数据包进行了分流,将普通数据包提交给TCP/IP协议栈,而将PPP过程的数据包交给/dev/ppp设备队列中,等待pppd去收取处理。
【6】PTY设备:串行设备,PPP内核协议栈与pppoe应用程序的中转站。因为ppp协议早多运行在串行链路上,所以在Linux内核中PPP协议栈与串行设备结合紧密。
PPPoE拨号建立的过程
==》PPPoE的Discovery过程
==》PPP过程
==》设置上网主机
pppd的实现:
网络协议是分层实现的,上层一般只需要知道其直接下层,只有在极少数的情况下才使用间接下层的接口。如普通的socket编程,它们只需要知道TCP/IP协议,而不需要知道PPP协议的存在。这种分层的设计简化了协议的实现和应用程序的开发。
但是PPP协议并不只提供简单的数据链路层功能,还提供了一些扩展功能(如鉴权PAP/CHAP,加密解密ECP)。应用程序要求使用透明化,不关心这些扩展功能的存在,而反过来。ppp协议处理模块本身又无法处理这些策略性的东西,因为它不知道用户名/密码,不知道是否要进行压缩,不知道是否要进行加密。
pppd的出现解决了这个问题。pppd是一个后台服务进程(daemon),是一个用户空间的进程,所以把那些策略性的内容从内核的PPP协议处理模块移到pppd中是很自然地事情。(pppd实现了所有鉴权、压缩、加密解密等扩展功能的控制协议)
pppd是用户空间的程序,它与内核态通过 设备文件(/dev/ppp) 进行通信。通过read系统调用,pppd可以读取PPP协议处理模块的数据包(PPP协议处理模块只会把应该由pppd处理的数据包发给pppd);通过write系统调用,pppd可以把要发送的数据包传递给PPP协议处理模块。通过ioctrl系统调用,pppd可以设置PPP协议的参数,可以建立/关闭连接。
在pppd里,每种协议实现都在独立的c文件中,它们通常要实现protent接口(该接口主要用于处理数据包)和fsm_callbacks接口(该接口主要用于状态机的状态切换)。数据包的接收是由main.c:get_input统一处理的,然后根据协议类型分法到具体的协议实现上。而数据包的发送则是协议实现者根据需要调用output函数完成的。
======= 流程 =======
应用程序通过socket接口发送TCP/IP数据包,这些TCP/IP数据包要如何经过PPP协议处理模块然后通过串口发送出去?
pppd在make_ppp_unit函数中调用ioctrl(PPPIOCNEWUNIT)创建一个网络接口(如ppp0),内核中的PPP协议模块在处理PPPIOCNEWUNIT时,调用register_netdev向内核注册ppp的网络接口(ppp0),该网络接口的传输函数指向ppp_start_xmit。
当应用程序发送数据时,内核根据IP地址和路由表,找到ppp网络接口(ppp0),然后条用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发送数据包。
PPPoE (用户态):
PPPoE: pppoe客户端版本:rp-pppoe-3.5-32.1 官网:
PPPOE主文件为PPPOE.c 入口函数是main()。 在main函数中主要做了两件事情:
PPPOE协议中的DISCOVERY阶段:处理函数为discovery(),此函数建立一个RAW SOCKET,然后发送报文,接着等待回应,等待处理,最后成功后设置状态进入SESSION阶段。
PPOE协议中的SESSION阶段,处理函数为session(),此函数首先建立一个RAW SOCKET,然后从PPPD中读入数据,在加上PPPOE发送出去,再将接收到的数据发送给PPPD程序,让PPPD处理,处理完后就建立了PPPOE连接,ppp0端口也建立了,并且从SERVER端分配到了IP。
PPPOE建立连接后数据报文的流程:
如果从eth收到PPPOE数据: —— 首先数据被PPPOE中RAW SOCKET数据接收, PPPOE解开数据包的PPPOE报头, 然后将数据包传输到伪终端主设备->伪终端的从设备,然后通过内核到达ppp0解开,ppp0设备通过驱动将数据包处理后向上层传输。
数据包发送: —— 首先根据路由知道数据要发送的端口在ppp0设备端口,数据达到ppp0设备后,ppp0将数据传输到伪终端从设备->伪终端主设备,然后到达PPPOE,PPPOE给数据包加上PPPOE报头,然后通过RAW SOCKET从eth接口发送出去。
【问题】:
数据包的处理——至上而下:PPP封转->PPPOE封装->以太网报头,但是PPPOE用户态(PPPOE内核态的实现是这个理想的流程)的实现流程并不是这样,用户态实现用在PPP和PPPOE中添加了一个伪终端,是为了不破坏LINUX内核中原有的终端实现的接口,因为linux内核中是将串行设备作为终端设备来驱动的。