• Lwip之PPP、PPPoE实现(一)


    目录

    一:概述

    二:协议

    2.1 PPP协议及结构

    2.1.1 PPP链路阶段转换

    2.1.2 PPP状态机

    2.1.3 PPP数据格式

    2.2 PPPoE协议及结构

    2.2.1 PPPoE状态转换

    2.2.2 PPPoE数据格式

    2.3 从网络数据包结构看PPP与PPPoE


    一:概述

    标准的PPP协议是用于串行线路的,基于标准PPP协议在串行链路上建立的点对点连接是物理上的点对点连接,而以太网属于共享模式,因此要在以太网上建立PPP连接就需要对PPP进行扩展,这里PPPoE就是对PPP的扩展,实现在以太网上运行PPP协议,这样就在用户和服务器之间建立了一条逻辑的点对点链路,从而实现对用户的认证。

    (modem接入技术面临一些相互矛盾的目标,既要通过同一个用户前置接入设备连接远程的多个用户主机,又要提供类似拨号一样的接入控制,计费等功能,而且要尽可能地减少用户的配置操作)

    (通过PPPoE,在一个共享的以太网上的多个主机,可以通过一个或多个简单的桥接入设备,与远程接入集中器进行多个PPP会话。使用这种模型,每个主机使用它自己的PPP协议栈,并且提供给用户一个熟悉的用户接口。接入控制、计费和服务类型能够基于每用户,而不是每站点来处理。)

    二:协议

    2.1 PPP协议及结构

    PPP协议是TCP/IP协议栈中数据链路层的协议,提供一种标准的方式在点对点的链路上传输多个网络层协议的数据报,PPP协议包括各种网络控制协议族(NCP)如:IPCP和IPXCP等,链路控制协议族(LCP)以及验证协议族(CHAP、PAP)等。其中,网络控制协议主要用来协商链路上传输的数据包的格式和类型,链路控制协议主要用来建立、拆除和监控PPP数据链路,验证协议主要用来提供网络安全的保证。

    为了在点对点的链路上建立通信,PPP链路的两端必须发送LCP数据包进行数据链路的测试和配置。等链路建立起来之后,还可能要进行端的验证,然后,PPP必须发送NCP数据包选择并配置一个或多个网络层协议,当所选择的网络层协议配置成功之后,每个网络层发送的数据报就可以在链路上传送了。链路一直保持着直到有明确的LCP或NCP数据包断开链路或某些外来的事件发生(如定时器超时或网络管理员干涉)。

    在一下关于协议的描述中,我们需要区分两个概念:phase与state,翻译过来就是阶段与状态。在下面的应用中,我们可以看到,整个过程由不同的几个阶段构成,而每个阶段则是由不同的状态转换过程构成的,这些转换过程反映了协商的过程,所以不同的阶段可能包括相同的状态转换过程,表明它们的协商原理是一致的。

    2.1.1 PPP链路阶段转换

    PPP链路的基本阶段的转换关系如下图所示:(需要说明的是下图中并不是将所有的转换关系都列了出来)

    阶段说明

    Link dead(物理层未准备好)

    链路在该阶段启动和结束。当外部事件指示链路准备好去使用的时候,PPP将进入建立阶段。在该阶段,lcp自动机将处于初始化或正在启动状态,链路向建立阶段的转换将发送一个up事件到自动机

    实现注意事项:典型的,在调制解调器断开连接后,链路应当自动转换到该状态。

    Link establishment

    LCP通过交换配置信息建立连接,当一个configure-ack包发送和接收后即完成配置阶段,进入打开状态。如果接收到LCP配置请求,会引起状态由网络协议层或着认证状态回退到LCP的建立阶段。

    Authentication

    该过程完成双方的认证,这并不是强制要求的,但是如果使用了,则在没有完成认证前不能进入网络协议配置阶段。一个实现不能简单的使认证过程失效因为超时或者缺少响应,而应该在多次尝试失败后才认为认证失败。

    Network-layer protocol

    完成上述两个阶段的配置后就可进入这一阶段进行网络层的协议的协商。当NCP进入打开状态后,PPP就可以承载响应网络协议的数据包了。在进入opened状态之前,任何收到的网络层的数据包都将被丢弃。实现上需注意,如果lcp处于打开状态,收到任何不被支持的协议的数据包都将会发送一个协议拒绝包,仅仅支持的协议才会被悄悄的丢弃。

    Link termination

    PPP可以在任何时候终止链路,这可能在一下情况下发生:丢失信号、认证失败、链路质量确认失败、超过最大空闲时间以及管理员的主动关闭行为。Lcp通过交换terminate数据包来进行关闭链路的操作。当链路正在关闭时,PPP会通知网络层,以便其采取相应的措施。终止请求的发送者将关闭链路在收到终止请求响应后或者重启计数器超时。终止请求的接收着应当等待对方断开链路,在发送一个终止响应后重启计数器超时前接收者不能断开链路。之后,PPP进入dead阶段。

        通过lcp关闭链路已经足够了,而且应当就由lcp来关闭链路。

    2.1.2 PPP状态机

    PPP协议的所有协商都是通过有限状态机来实现的,有限状态机是由事件、动作以及状态转换来定义的。

    需要注意的是,仅仅发送配置请求、发送终止请求或者重启计数器清零会启动或者重新启动重启计数器,当从任何重启计数器运行的状态进入计数器不运行的状态时,重启计数器才会停止。

    • 状态:下面简单描述了PPP状态机的所有状态
      • 初始化:底层不可用,无open事件,重启计数器不运行。
      • 正在启动:初始化的open对应物,底层仍然不可用,重启计数器不运行
      • 已关闭:链路可用,无open操作发生,重启计数器不运行
      • 已停止:已关闭的open对应物,当自动机在发送终止ack或者进行tlf动作后在等待down事件的时候进入该状态。重启计数器不运行。已停止状态是链路终止、链路配置失败或其他自动机的失败状态的交叉汇合点。此时存在一个down事件与配置请求事件的竞争,如果配置请求事件先到达就会推迟对down事件的相应,这将阻止重复请求的攻击。
      • 正在关闭:该状态,将产生关闭连接的企图,终止请求已发送,重启计数器正在运行,但是终止相应还没有收到。接收到终止相应后就进入已关闭状态,当重启计数器超时后会重发终止请求,当这一过程超过最大次数后也会自动进入已关闭状态。
      • 正在停止:为正在关闭的open对应物,同上,终止请求已发送,重启计数器正在运行,但是终止相应还没有收到。正在停止状态提供一个很好的机会在允许新的网络通信之前终止链路。当链路终止后,一个新的配置可能发生通过已停止或正在停止状态。
      • 请求发送状态:该状态会发送配置请求。处在该状态表明已发送一个配置请求,重启计数器正在运行,但是配置响应既没有收到也没有发送。
      • 接收到响应状态:该状态,配置请求发送并收到响应,但是配置响应还没有发送,所以,重启计数器还在运行。
      • 发送响应状态:该状态配置请求和配置响应都已经发送,但是配置响应还没有收到。因此,重启计数器仍然在运行。
      • 打开状态:处在该状态表明配置响应和请求均已经发送和接收到,重启计数器不再运行。处在该状态应该通知上层该层已经起来了,同样,当离开打开状态时应该通知上层该层已经down了。
    • 事件:描述PPP状态机过程中的所有事件
      • Up事件:当下层准备好承载数据包的时候,该事件发生。典型的,该事件被用来触发LCP进入建立阶段。当然,它也可以被lcp用来通知ncp,表明链路正在进入网络协议层阶段。因此,来自lcp的tlu(this layer up)将触发ncp中的up事件。
      • Down事件:当更低的层表明它不再准备承载数据包的时候,该事件就会发生。该事件用来通知lcp链路正在进入dead阶段。当然,它也可以被lcp用来通知ncp,表明链路正在离开网络协议层阶段,因此,来自lcp的tld(this layer down)将触发ncp中的down事件。
      • Open事件:发生该事件时表明链路可以用于传输数据了。如果当前lcp不在打开状态,发生该事件时,状态机会发生配置请求到对端。如果自动机不能够开始配置,链路的建立将会被自动延迟。当接收到终止请求或其他导致链路不可用的事件发生时,自动机会自动转入一种链路可以重新打开的状态,管理员额外的干涉是不需要的。
      • 经验表明:当用户想就链路进行重新谈判时,他们将额外的执行一条Open命令。这表明新的值将被协商。既然这不是Open事件的含义,那就暗示着在Opened, Closing, Stopping或Stopped状态,当执行一条Open用户命令时,执行发行一个Down事件,紧接着一个Up事件。一定要注意不能有从另一个源发生的Down事件的干涉。紧接着Up事件的Down事件将引起一次有秩序的链路的再协商(通过先前进到Starting状态,再进入到Request-Sent状态)。该再协商没有负面影响。
      • Close事件:当close事件发生后,并且链路不在已关闭状态,自动机将试图终止连接。进一步的重新配置链路的尝试将被拒绝,直到一个新的open事件发生。当认证失败时,链路应当被终止,以防止受到重复性的攻击和为其他用户服务。这可以通过模仿一个Close事件给LCP,然后紧跟着一个Open事件来完成,因为链路在管理上是可被访问的。一定要注意不能有从另一个源发生的Down事件的干涉。紧接着close事件的open事件将引起一次有秩序的链路的再协商(通过先前进到Closing状态,再进入到Stopping状态),This-Layer-Finished动作能断开链路的连接。在Stopped或Starting状态,自动机等待下一次连接尝试。
      • Timeout事件:该事件表明Restart timer期满。Restart timer用于记录对Configure-Request和Terminate-Request packets的响应的时间。To+表明重启计数器大于零,这将触发对配置请求和终止请求的响应的重新传输。
      • Receive-configure-request事件:收到一个来自peer的Configure-Request packet时,该事件发生。Configure-Request packet表明希望开创一个连接并且可以指定配置选项。Rcr+指示配置请求时可接受的,并将触发ack响应的发送。Rcr-指示配置请求是不可接受的,因此触发nak或者reject响应的发送。上述事件可能发生在连接已经处在打开状态的时候,实现上,必须立即重新进行配置协商。
      • Receive-configure-ack事件:当收到一个有效的ack的时候,该事件发生,
      • Receive-configure-nak or reject事件:尽管Configure-Nak和Configure-Reject在自动机中引起相同的状态转换,但这些packets对发送于Configure-Request packet中的配置选项有着截然不同的影响。
      • Receive-Terminate-Request事件:当收到一个Terminate-Request packet时,该事件发生。Terminate-Request packet表明希望peer去关闭连接。该事件不同于close事件,不应当忽略网络管理员的open命令,实现上,必须准备接收一个新的配置请求。
      • Receive-Terminate-Ack 事件:当收到一个来自peer的Terminate-Ack packet时,该事件发生。
      • Receive-Code-Reject, Receive-Protocol-Reject 事件:当收到一个来自peer的Code-Reject或Protocol-Reject packet时,该事件发生。
      • 当拒绝值可接受时(例如一个扩充编码的Code-Reject,或一个NCP的Protocol-Reject,这些在一般操作的范围内),RXJ+事件出现。执行必须停止发送损坏了的packet类型。
      • 当拒绝值是灾难性的时候(例如一个Configure-Request的Code-Reject,或一个LCP的Protocol-Reject),RXJ- 事件出现。该事件传达了一个不可校正的错误(导致连接终止)。
    • 动作:描述状态机中特定状态下收到特定事件后的行为
      • This-layer-up:该动作指示上层的协议,自动机正在进入打开状态。典型的,该动作被lcp用来发生一个up事件给ncp以及其他协议。
      • This-layer-down:该动作指示上层的协议,自动机正在离开打开状态。典型的,该动作可以被lcp用来发送一个down事件给ncp以及其他协议。
      • This-layer-started:该动作指示更低的层次,自动机正在进入开始状态,对于链路更低的层次是必须的。如果底层可用,它应当响应一个up事件。
      • This-layer-finished:该动作指示更低的层次,自动机正在进入初始化、已关闭或者已停止状态,更低的层次对链路而言,已不再需要。当底层已经终止的话,则应该响应一个down事件。典型的,该动作可以被lcp用与进入链路dead状态,或者,也有可能被ncp用来指示lcp链路可能终止了,当不再有其他的ncps打开时。

    状态转换:下面以事件作为触发线索查看整个状态的转换以及采取的动作

    Up事件

    (状态图与采取的动作:)

    如果当前处于初始状态,则直接进入关闭状态  非正常启动

    如果处于正在启动状态,则进入发送配置请求阶段,进入正常启动阶段,进行下一步的配置动作。

    Down 事件

    如果当前为关闭或正在关闭状态,则直接进入初始状态,其他情况则进入正在启动状态。已停止状态产生的动作为this-layer-started,打开状态产生的动作为this-layer-down。

    Open 事件

    如果当前状态为初始状态,则进入正在启动状态,产生tls动作,如果已经关闭了,则进入发送配置请求状态,同时初始化重启计数器,并发送配置请求。如果为正在关闭状态,则进入正在停止状态,所有在其他状态的都不发生状态改变。

    Close 事件

    如果当前处于配置或者已经打开状态,则初始化重启计数器,发送配置请求,并进入正在关闭状态,对于打开状态,还要产生tld动作,表明离开已打开状态。如果当前处于正在启动状态,返回到初始化状态,对于正在停止的进入正在关闭状态,已停止的进入已关闭状态。

    TO+事件()

    对于正在关闭或正在停止状态,仍在本状态循环,只是会发送终止请求。若处于正在进行配置的状态,则发送配置请求,如果已经收到ack,则重新进入请求发送配置信息状态,忽略本次收到的ack

    TO-事件()

    如果产生该事件,我们应该尝试关闭连接。如果处在正在关闭状态,进入已关闭状态,其他情况下进入已停止状态

    RCR+

    如果系统之前已经收到了ack应答,即处于ack已接收状态,则发送ack给对端,并进入打开状态,表明当前层已经建立起来了。而在发送请求状态、已停止状态以及打开状态,都将转入ack已发送状态,并在此状态循环,等待接收对端的ack到来

    RCR-

    如果已经收到ack,则发送scn,如果在发送ack状态或者打开状态或者已停止状态,都将进入发送请求状态,继续进行状态的配置

    RCA

    如果处在发送了ack状态,则进入打开状态,表明该层配置已经完成,而如果是在打开状态又收到ack,则进入发送请求状态,继续进行协商。

    RCN

    如果已经接收到ack,则继续等待接收ack,如果在其他状态,则转入配置请求发送阶段,继续进行配置协商。

    RTR

    当收到终止请求时,大部分状态下都会发送sta响应,如果处于已打开状态,则清零重启计数器,转到正在关闭状态,在其他配置状态的都转到配置请求发送状态

    RTA

    如果在正在关闭和正在停止状态,则进入关闭和停止状态。如果时打开状态,转入配置请求状态,重新进行配置请求。

    RUC

    此时所有的状态都不发生转移,只是发送scj

    RXJ+

    此时,如果处在ack已接收状态,则跳回请求发送配置状态信息状态

    RXJ-

    如果正在关闭状态,则进入已关闭状态,如果正在配置信息协商过程中,则转入已停止状态,如果已经处于打开状态,则进入正在停止状态,并发送终止请求信息str。Tlf表明这一层已经完成了。

    RXR

    RXR事件表明系统需要响应对端的回波请求或者发送回波请求,故状态都没有进行转换,只是如果当前已是打开状态的话,就发送回波响应信息ser

    通过以上的状态转换说明,可以总结如下:

    对于每一层,可以在功能上近似分为如下三个层

    当该层的配置完成后,就进入最内层的打开状态

    如果在进行本状态的协商阶段,主要在此外层进行状态变换

    最外层则主要进行端的维护

    额外的说明:

    协议做了一个可靠的尝试在关于避免配置选项协商循环方面,但是不能保证循环不会发生。实现者应该实现循环检测机制或者更高层次的超时。

    关于计数器与定时器:

    自动机使用一个特殊的定时器。重启计数器被用来计算传输配置请求和终止请求的包的时间。重启计时器的超时将引发一个超时事件,并重传相应的配置请求和终止请求包。重启计数器必须是可配置的,但是应当默认为3秒。

    2.1.3 PPP数据格式

    基本的PPP数据包格式如下:

    协议域:指示了封装在信息域中的数据的类型,常见的协议如下:

    0021

    Internet Protocol

    002d

    Van Jacobson Compressed TCP/IP

    002b

    Novell IPX

    002f

    Van Jacobson Uncompressed TCP/IP

    802b

    Novell IPX Control Protocol

    8021

    Internet Protocol Control Protocol

    8031

    Bridging NC

    c023

    Password Authentication Protocol

    c021

    Link Control Protocol

    c223

    Challenge Handshake Authentication Protocol

    信息域:就是协议域中的协议所确定的协议相关的数据;

    填充域:信息域中的数据会被填充任意数量的字节直到达到mru

    针对具体协议的数据(比如lcp/pap/ipcp),它们的数据又有自己特有的格式,简单介绍如下:

    Lcp :

    在LCP数据包中,Code域指明数据包的类型,标识符域用来匹配请求和响应,不同的数据报标识符应不同,重发的数据包标识符不变,请求和它相应的响应的标识符应相同。

    Code域的取值及其意义如下表所示:

    0x01

    Configure-Request

    0x07

    Code-Reject

    0x02

    Configure-Ack

    0x08

    Protocol-Reject

    0x03

    Configure-Nak

    0x09

    Echo-Request

    0x04

    Configure-Reject

    0x0A

    Echo-Reply

    0x05

    Terminate-Request

    0x0B

    Discard-Request

    0x06

    Terminate-Ack

    0x0C

    RESERVED

       

    Ncp 。。。

    这里需要说明的一点是标准PPP是针对串行线路设计的,所以在PPP包头和包尾有许多与在串行线路上传输相关的定义,比如控制字节以及转义序列等。在PPPoE中承载的PPP包是从我们这里定义的包头格式开始的,即PPP数据的第一个字节是协议字段。

    2.2 PPPoE协议及结构

    2.2.1 PPPoE状态转换

    PPPoE的实现包括发现和会话两个阶段,在发现阶段主机主要用来发现接入服务器以及服务器所提供的服务,除此之外,主机还会获得一个接入集中器提供的会话id。此后,双方就进入会话阶段,在会话阶段完成PPP链路的建立、认证以及协议的协商,并最终建立起一条点对点的逻辑链路。在会话阶段,主机一直使用发现阶段获得的会话id,该id在整个会话过程中都不会改变,因此,会话id唯一标识了本地主机与接入集中器的一次会话。

    发现阶段主要包括如下四个步骤:

    PPPOE主动发现初始包:PPPOE主动发现初始包(PPPoE Active Discovery Initiation, PADI)由客户机发出,以太头中的目的地址是以太广播地址FF:FF:FF:FF:FF:FF,PPPOE头中的CODE为0x09,SESSION_ID值必须为0,负载部分必须只包含一个Service-Name类型的TAG表示请求的服务类型,另外可以包含其他TAG,整个PPPOE包不能超过1484字节,这样省出的16字节可以由ADSL中继设备添加中继TAG。

    PPPOE主动发现提议包:PPPOE主动发现提议包(PPPoE Active Discovery Offer, PADO)由AC发出,用来回应客户机的PADI包,以太头中的目的地址是客户机的MAC地址,PPPOE头中的CODE为0x07,SESSION_ID值必须为0,负载部分必须包含一个AC-Name类型的TAG,用来指示本AC的名称,一个在PADI包中指定的Service-Name的TAG,另外可以包含其他Service-Name的TAG。如果AC不对该客户机提供服务,AC就不回应PADO包。

    PPPOE主动发现请求包:PPPOE主动发现请求包(PPPoE Active Discovery Request, PADR)由客户机发出,因为可能会有多个AC对客户机发出的PADI包回应了PADO包,客户机从回应的PADO包中选择一个AC发送PADR包,以太头中的目的地址是该AC的MAC地址,PPPOE头中的CODE为0x19,SESSION_ID值必须为0,负载部分必须只包含一个Service-Name类型的TAG表示请求的服务类型,另外可以包含其他TAG。

    PPPOE主动发现会话确认包:PPPOE主动发现会话确认包(PPPoE Active Discovery Session-confirmation, PADS)由AC发出,收到客户机的PADR包后,AC将产生一个SEESSION_ID值用来标志本次PPP会话,以PADR包方式发送给客户机。以太头中的目的地址是客户机的MAC地址,PPPOE头中的CODE为0x65,SESSION_ID值必须为所生成的那个SESSION_ID,负载部分必须只包含一个Service-Name类型的TAG,表示该服务类型被AC接受,另外可以包含其他TAG。如果AC不接受PADR中的Server-Name,PADS中则包含一个Service-Name-Error类型的TAG,这时SESSION_ID设置为0。

    PPPOE主动发现停止包:PPPOE主动发现停止包(PPPoE Active Discovery Terminate, PADT)表示PPPOE会话过程的结束,AC和客户机都可以主动发出。以太头中的目的地址是对方的MAC地址,PPPOE头中的CODE为0xa7,SESSION_ID值必须为PPPOE会话过程的SESSION_ID,不需要TAG。

    当系统收到PADS包后就进入PPP会话阶段,这一阶段执行标准的PPP连接过程,建立PPP链路,完成上层协议的协商认证,进行数据的传送。

    2.2.2 PPPoE数据格式

    PPPoE的数据格式如下:

    可以看到PPPoE头占用了6个字节,加上PPP头两个字节,所以最后传到上层的ip数据包会比正常的以太网的Ip包要小8个字节,也就是此时的ip包的最大长度为1492,而不是我们通常所说的1500。

    其中,VER和TYPE的值在PPPOE的不同阶段不变,都为0x1。CODE域为8比特长,在发现阶段有以下几种CODE值:

    客户机与AC之间信息的交换是通过承载在PPPoE中的TAG来完成的,一个TAG的格式如下:

    TAG_TYPE:16位,TAG类型  

    TAG_LENGTH:16位,表示TAG_VALUE部分的长度

    TAG_VALUE:TAG值

    TAG_TYPE可取以下值(注意第一字节为2表示是错误信息):

    0x0000

    End-Of-List

    0x0101

    Service-Name

    0x0102

    AC-Name

    0x0103

    Host-Uniq

    0x0104

    AC-Cookie

    0x0105

    Verdor-Specific

    0x0110

    Relay-Session-ID

    0x0201

    Service-Name-Error

    0x0202

    AC-System-Error

    0x0203

    Generic-Error

    类似于PPP,在PPPoE发现阶段发送的不同数据包又有自己特定的格式,说明如下:

    Padi包的一个例子如下:

    Pado包的一个例子如下:

    Padr包的一个例子如下:

    Pads包的一个例子如下:

    Padt包的一个例子如下:

    2.3 从网络数据包结构看PPP与PPPoE

    我们将PPP、PPPoE以及以太网帧的结构结合起来可以得到如下的关系图:

    下面是一个PPP LCP阶段的包:

    从以上可以看到,最终,以太网中传递的是PPPoE数据包,PPPoE数据包中承载了PPP包,而我们最终需要的ip包是在PPP包中承载的。也如我们上面所提到的,除去PPPoE以及PPP头,最终ip包的最大长度是1492,不再是我们通常所说的1500。

  • 相关阅读:
    同旺科技 FLUKE ADPT 隔离版发布 ---- 说明书
    基于node.js的学生管理系统设计
    区域气象-大气化学在线耦合模式(WRF/Chem)在大气环境领域实践技术应用
    【Linux-网络编程】
    OpenCV模块介绍
    231n--神经网络和反向传播
    对系统的 Go 版本进行升级
    浏览器事件循环最详细的帖子总结
    get与post的区别
    026-第三代软件开发-C++&QML交互
  • 原文地址:https://blog.csdn.net/wwwyue1985/article/details/126473270