一、摘要
PPP協議是在ATM網路裡常用的鏈路協議,基於PPP協議的PPPoA,PPPoE在xDSL數據機中不可或缺。本文簡單描述PPP協議的幀封裝格式,並給出了一個簡單的PPP編碼與解碼演算法,以期望能對需要者有所協助。
二、PPP協議簡介
PPP資料幀的格式看上去很像ISO的HDLC(高層資料連結控制)標準。是PPP資料
幀的格式。
每一幀都以標誌字元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: 修改緩衝區,返回解碼後的字元長度。
此編解碼代碼以易讀為主,需要者可自行最佳化。
---------------
僅供學習和交流,轉載請註明作者及出處,謝謝!