PPP数据帧的编码与解码(转)

来源:互联网 发布:国外优秀插画师 知乎 编辑:程序博客网 时间:2024/06/05 04:29

 

一、摘要

PPP协议是在ATM网络里常用的链路协议,基于PPP协议的PPPoA,PPPoE在xDSL调制解调器中不可或缺。本文简单描述PPP协议的帧封装格式,并给出了一个简单的PPP编码与解码算法,以期望能对需要者有所帮助。

二、PPP协议简介

PPP数据帧的格式看上去很像ISO的HDLC(高层数据链路控制)标准。如图是PPP数据
帧的格式。
PPP frame

每一帧都以标志字符0x7e开始和结束。紧接着是一个地址字节,值始终是0xff,然后是一
个值为0x03的控制字节。



接下来是协议字段,类似于以太网中类型字段的功能。当它的值为0x0021时,表示信息
字段是一个IP数据报;值为0xc021时,表示信息字段是链路控制数据;值为0x8021时,表示
信息字段是网络控制数据。




CRC字段(或FCS,帧检验序列)是一个循环冗余检验码,以检测数据帧中的错误。
由于标志字符的值是0x7e,因此当该字符出现在信息字段中时, PPP需要对它进行转义。
在同步链路中,该过程是通过一种称作比特填充(bit stuffing )的硬件技术来完成的[ Tanenbaum
1989 ]。在异步链路中,特殊字符0x7d用作转义字符。当它出现在PPP数据帧中时,那么紧接
着的字符的第6个比特要取其补码,具体实现过程如下:


1) 当遇到字符0x7e时,需连续传送两个字符: 0x7d和0x5e,以实现标志字符的转义。
2) 当遇到转义字符0x7d时,需连续传送两个字符: 0x7d和0x5d,以实现转义字符的转义。
3 ) 默认情况下,如果字符的值小于0x20(比如,一个ASCII控制字符),一般都要进行转
义。例如,遇到字符0x01时需连续传送0x7d和0x21两个字符(这时,第6个比特取补码后变为
1,而前面两种情况均把它变为0)。



这样做的原因是防止它们出现在双方主机的串行接口驱动程序或调制解调器中,因为有
时它们会把这些控制字符解释成特殊的含义。另一种可能是用链路控制协议来指定是否需要
对这32个字符中的某一些值进行转义。默认情况下是对所有的32个字符都进行转义。

--摘自《TCP/IP详解》卷1·协议 (W. Richard Stevens)

三、编码与解码

1。编码

编码就是按照前面所说的对需要转义的字符进行变换,下面是简单的实现代码:

001 #define PPP_FRAME_FLAG 0x7e /* 标志字符 */

002 #define PPP_FRAME_ESC 0x7d /* 转义字符 */

003 #define PPP_FRAME_ENC 0x20 /* 编码字符 */

004 #define BUF_LEN 1500

005 /* return: bytes encoded */

006 int pppEncode(unsigned char * buf, int len) {
007 unsigned char * pi, * po;
008 int i, olen;
009 unsigned char obuf[BUF_LEN];

010 if(len > (BUF_LEN>>1)) {
011 return -1;
012 }

013 memset(obuf, 0, BUF_LEN);
014 pi = buf;
015 po = obuf;
016 olen = len;

017 for(i=0; i<len; i++) {

018 /* byte needs encode, encode it */
019 if(*pi == PPP_FRAME_FLAG
020 || *pi == PPP_FRAME_ESC
021 || *pi < 0x20) {
022 *po = PPP_FRAME_ESC;
023 po++;
024 olen++;

025 /* xor the 6th bit */
026 *po = *pi ^ PPP_FRAME_ENC;
027 }
028 else {
029 *po = *pi;
030 }
031 pi++;
032 po++;
033 }

034 memcpy(buf, obuf, olen);

035 return olen;
036 }001~003: 定义标志字符,转义字符和编码字符。

010~012: 检查要编码的字符长度,按最坏情况,一个字符会编码成两个字符,所以这里只能编码最大缓冲区长度一半。

018~027: 编码的主要实现,遇到标志字符,转义字符和小于0x20的控制字符,都要进行编码。方法就是在其前面插入一个转义字符0x7d,然后对其第6位取补码。

028~030: 其他字符,不做任何修改。

034~035: 修改缓冲区,返回编码后的字符长度。

2。 解码

解码实际上就是编码的逆运算,它除去转义字符,并对转义字符之后的字符的第6位去补码。

001 /* return: bytes decoded */

002 int pppDecode(unsigned char * buf, int len) {
003 unsigned char * pi, *po;
004 int i, olen;
005 unsigned char obuf[BUF_LEN];

006 if(len > BUF_LEN) {
007 return -1;
008 }

009 memset(obuf, 0, BUF_LEN);
010 pi = buf;
011 po = obuf;
012 olen = len;

013 for(i=0; i<len; i++) {
014 if(*pi == PPP_FRAME_ESC) {

015 /* skip the escape byte */
016 pi++;
017 olen--;

018 /* xor the 6th bit */
019 *po = *pi ^ PPP_FRAME_ENC;
020 }
021 else {
022 *po = *pi;
023 }
024 pi++;
025 po++;
026 }

027 memcpy(buf, obuf, olen);

028 return olen;
029 }006~008: 检查要解码的字符长度。

014~020: 解码的主要实现,遇到转义字符,将其跳过,对紧接其后的字符的第6位去补码。

021~023: 其他字符,不做处理。

027~028: 修改缓冲区,返回解码后的字符长度。

此编解码代码以易读为主,需要者可自行优化。

---------------


仅供学习和交流,转载请注明作者及出处,谢谢!
链接:http://blog.csdn.net/wangxg_7520/article/details/2488491
原创粉丝点击