目录
标准的PPP协议是用于串行线路的,基于标准PPP协议在串行链路上建立的点对点连接是物理上的点对点连接,而以太网属于共享模式,因此要在以太网上建立PPP连接就需要对PPP进行扩展,这里PPPoE就是对PPP的扩展,实现在以太网上运行PPP协议,这样就在用户和服务器之间建立了一条逻辑的点对点链路,从而实现对用户的认证。
(modem接入技术面临一些相互矛盾的目标,既要通过同一个用户前置接入设备连接远程的多个用户主机,又要提供类似拨号一样的接入控制,计费等功能,而且要尽可能地减少用户的配置操作)
(通过PPPoE,在一个共享的以太网上的多个主机,可以通过一个或多个简单的桥接入设备,与远程接入集中器进行多个PPP会话。使用这种模型,每个主机使用它自己的PPP协议栈,并且提供给用户一个熟悉的用户接口。接入控制、计费和服务类型能够基于每用户,而不是每站点来处理。)
PPP协议是TCP/IP协议栈中数据链路层的协议,提供一种标准的方式在点对点的链路上传输多个网络层协议的数据报,PPP协议包括各种网络控制协议族(NCP)如:IPCP和IPXCP等,链路控制协议族(LCP)以及验证协议族(CHAP、PAP)等。其中,网络控制协议主要用来协商链路上传输的数据包的格式和类型,链路控制协议主要用来建立、拆除和监控PPP数据链路,验证协议主要用来提供网络安全的保证。
为了在点对点的链路上建立通信,PPP链路的两端必须发送LCP数据包进行数据链路的测试和配置。等链路建立起来之后,还可能要进行端的验证,然后,PPP必须发送NCP数据包选择并配置一个或多个网络层协议,当所选择的网络层协议配置成功之后,每个网络层发送的数据报就可以在链路上传送了。链路一直保持着直到有明确的LCP或NCP数据包断开链路或某些外来的事件发生(如定时器超时或网络管理员干涉)。
在一下关于协议的描述中,我们需要区分两个概念:phase与state,翻译过来就是阶段与状态。在下面的应用中,我们可以看到,整个过程由不同的几个阶段构成,而每个阶段则是由不同的状态转换过程构成的,这些转换过程反映了协商的过程,所以不同的阶段可能包括相同的状态转换过程,表明它们的协商原理是一致的。
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来关闭链路。
PPP协议的所有协商都是通过有限状态机来实现的,有限状态机是由事件、动作以及状态转换来定义的。
需要注意的是,仅仅发送配置请求、发送终止请求或者重启计数器清零会启动或者重新启动重启计数器,当从任何重启计数器运行的状态进入计数器不运行的状态时,重启计数器才会停止。
状态转换:下面以事件作为触发线索查看整个状态的转换以及采取的动作
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秒。
基本的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数据的第一个字节是协议字段。
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链路,完成上层协议的协商认证,进行数据的传送。
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包的一个例子如下:
我们将PPP、PPPoE以及以太网帧的结构结合起来可以得到如下的关系图:
下面是一个PPP LCP阶段的包:
从以上可以看到,最终,以太网中传递的是PPPoE数据包,PPPoE数据包中承载了PPP包,而我们最终需要的ip包是在PPP包中承载的。也如我们上面所提到的,除去PPPoE以及PPP头,最终ip包的最大长度是1492,不再是我们通常所说的1500。