pppoe(point.to.point protoeol over ethernet)是将ppp协议封装在以太网帧上进行传输,它的通信过程分为探测(discovery)和ppp会话(session)2个阶段。pppoe discovery阶段主要是客户机确定ac(access concentrator)以及客户机与ac协商session id。而pppoe sess-ion用于完成数据包的接收与发送,同时它也用于完成ppp链路的协商(lcp),以及网络层的控制协商(ipcp)等。
传统的pppoe先会用raw socket读取数据,然后采用用户态程序对其封包解包,然后再发送给内核。但是这种方法会引起大量的内核空间与用户空间的上下文切换,从而带来不必要的开销。而对于实现于内核态的pppoe,它会把所有的封包以及解包实现于内核,这样就大大提高pppoe的效率。
1 pppoe协议概述
1. 1 pppoe discovery阶段
在pppoe discovery阶段,客户机首先广播一个padi(0x09)帧。收到padi帧的一个或多个服务器会发送pado(0x07)帧,这个包中包含了服务器的各种标识。然后,客户机会选择其中一个服务器发送padr(0x19),表明主机选择了这个服务器。最终,收到padr(0x65)帧的服务器会为新的会话分配资源并向客户机发送pads(0x65)。当此阶段完成,这两次的对话完成了session_id以及双方物理地址,为后续数据会话打好基础。
同时ppp协议还提供了一个padt请求,该请求用于结束这次pppoe会话。这个请求可以由任何一方发出,同时代表这次回话的结束,图1描述整个discovery过程。
1.2 pppoe session阶段
pppoe discovery阶段是为整个pppoe会话获取双方物理地址以及session_id,这个session_id就成为了双方的通信凭证,在整个会话过程中保持不变。ppp帧数据被封在以太帧中,它在以太帧的标识为0x8864,当碰到0x8864时,就认为是一个pppoe包。
在pppoe session阶段,pppoe除数据传输以外,还提供了链路的协商(lcp),以及网络层的控制协商(ipcp)等其他服务。对于lcp,它主要用于配置和测试数据通信链路,用来协商ppp协议的一些配置参数选项;处理不同大小的数据帧:检测链路环路和一些链路的错误;终止一条链路,其作用类似于ip层的icmp协议。而对于ipcp,它主要用于动态地协商客户机与服务器双方ip,实际的数据报文交换过程中主要涉及config-request、config-ack、config-nak和config-re-ject。图2描述ipcp协商ip的过程。
2 pppoe设计实现
从上述pppoe协议描述中,了解到整个pppoe会话包括discovery、链路协商和数据传输3种交互。这3种数据中,数据传输最重要,数据量最大。对于用户空间模式的pppoe,数据包收发需要通过pty,由用户空间的pppoe进程处理pppoe包头后通过raw socket收发。也就是说所有的pppoe数据封包结果都在用户空间执行,这样就大大增加了内核空间与用户空间数据切换的次数。为了减少这种开销。这里采用一个内核模块来处理数据的封包解包,这样就大大减少了内核空间与用户空间数据切换的次数,提高效率。所以对内核空间模式的pppoe的处理策略是:discovery以及链路协商交互全部在用户空间完成,而数据的封解包则通过一个内核模块在内核空间完成。
2.1 pppoe会话初始化
pppoe的初始化包括整个discovery阶段以及session阶段的ipcp和lcp等其他链路协商过程,所有的工作都在用户空间完成,图3描述了整个初始化的流程。
首先通过raw socket发送pads、padr、pado、padi,当这个请求完成后,得到远端服务器的mac地址以及双方建立起来的session_id。由于后面的内核封包需要用到这些数据,所以需要将这些数据切换到内核空间去。由于数据量较小,则采用proc文件系统完成内核空间与用户空间的数据切换。
对于proc中的数据,设计以下数据格式:接口名词smac dmac session_id rx tx。
其中“接口名词”为接口名称,设计中不再用虚拟ppp接口传输数据,而是将数据迁移到物理网卡上,这样在某种程度上减轻了路由模块的负担,但需要去proc文件读取接口名称以此判断该物理接口是否启用pppoe拨号。“smac”和“dmac”参数为双方的mac地址,在内核封包中用到。“session_id”双方建立起来的session_id,也用于内核封包。而“rx”和“tx”是用于记录最后一次解包与封包的时间点,该数据用于按需拨号。
在建立好ppp连接后就进行ipcp协商,这个过程将为协商到双方的ip的地址,并将这个ip地址配置到物理口上,而这些数据将通过ppp口通信。除此之外,还需要为pppoe链路配置相应的路由、更新arp列表以及获取相应的dns服务器地址。
2.2 pppoe数据接收
pppoe数据接收主要是对数据进行解包,其全部动作在内核空间完成。当一个pppoe数据包从网卡驱动里面读出来时,是一个完整的pppoe包。而上层模块无法识别这样的包,所以需要将中间的那些ppp协议数据从包中剥离出来,使其变成一个普通的ip数据包,图4描述了pppoe接收数据的整体流程。
当从网卡驱动上读取数据时,也就是获取数据的skb,首先需要判断这个skb是否有效,然后再判断该网卡是否起动了pppoe服务,很显然这里需要读取proc的接口信息。如果已经pppoe拨号服务,还需要判断该包是不是一个lcp或者ipcp等其他协商数据,也就是判断协议域的数据是不是0x0021。因为如果是协商数据,则不需要解包,而直接将其转发到ppp虚拟接口上。对于具体的解包过程将进行代码分析。解完包以后该数据包就属于普通的ip包,后续流程与普通的ip包处理相同。
2.3 pppoe数据发送
pppoe数据发送流程基本上是数据接收的逆过程,图5描述整个数据发送过程。首先从用户空间或者forwarding模块获取一个数据包,这个数据包属于正常的ip包。很显然这个包是无法发送到ppp链路上的,因为pppoe服务器并不识别这样的数据包。所以需要利用proc文件中的session_id、远端mac和本地mac数据来封装ip包,使其成为一个标准的ppp包。
2.4 核心代码实现
整个pppoe内核模式拨号的核心代码主要集中在内核模块上,该模块主要功能有proc文件读写、数据包的封装、解包等操作,下面是这个内核模块的包封装的部分程序。
以上只是部分代码,由于代码太多,而且很多代码和项目的硬件抽象层模块(hal)息息相关,所以在此不再详细阐述。具体可以参考lin-ux内核中pppoe模块里面的代码,虽然工作的层次有点不一样,但是总体的思路是一样的,本文的实现代码基本上也是参考的linux内核中pp-poe的代码。
3 结束语
本文主要描述了pppoe内核模式拨号的设计与实现,该模式将封包动作从用户空间转移到内核空间,从而大大降低了内核空间与用户空间切换的次数,目前这种内核模式下的拨号已经大量地应用于各种网关产品中。虽然pppoe是一种非常成熟的技术,linux内核也已开始支持pp-poe内核态拨号,同时由卡耐基梅隆大学开发的pppd开源项目已经广泛应用于各种网关产品中,但是熟悉整个内核态拨号的流程是非常重要的,同时pppd目前还存在一些bug,在项目开发的过程中还需做大量修改。本文提供解决方案有别于传统的通过ppp虚拟接口来传输数据的方案,在某种程度上也降低了路由模块的工作任务,尤其是需要实现多路pppoe的时候。另外该设计方案在linux2.6.18上已经成功通过测试,并投入使用。
新型射频前端解决方案(TriQuint)
智能物联网有哪些应用场景?SKYLAB无线模块带您了解
适用于任何预算的前7款USB示波器
大数据告诉你,那些相机、镜头才是最好的!
三极管是什么意思 三极管的分类 三极管的主要参数
怎样设计并实现一个基于Linux的PPPoE内核模式拨号?
SKY65366-21:400 MHz发送/接收前端模块,大功率射频模块在SUB-1G方案
冯军称将以LED灯切入新能源 四处鞠躬被指炒作
电位器选用与代换经验,how to Selection Potentiometer
基于Android开发的ADT获取内存中的敏感信息
静态断路器的电路图
介绍差分信号的优势
最大功率传输分析
浅谈瑞萨电子无线传感器网络解决方案
电动车换电池小心容量缩水
SMT贴片回流焊的温度控制有哪些要求呢?
基于M-LVDS的数据通信架构介绍
磨工实习教案
在高速网卡中实现可编程传输协议
水痕去除的有效方法有哪些