Wednesday, October 24, 2012

Linux下Data Alignment工作方式

花點時間紀錄一下自己最近所發生過的小狀況啦~


(1). Data Alignment:
  話說,個人覺得"Data alignment"的部份自己略有小著墨,但這兩三天卻被有關Data alignment的給弄掛了~狀況是這樣的,,為何傳遞的封包大小計算經sizeof(X)應該為58,但卻會大於我自己認為的大小? 經過網路上搜尋後才意識到,,對於32bit(4 Bytes)的CPU在擷取資料時會為了效率問題而執行"Data Alignment的動作, 所以儲存的資料皆為 4 Bytes的倍數為主.下列即為人家所探討的部份. (特別是下列Ex3~Ex5的解釋, 我覺得非常有意思)
http://kf99916cs.blogspot.tw/2012/10/data-structure-alignment.html
http://kezeodsnx.pixnet.net/blog/post/27585076-data-structure%E7%9A%84%E5%B0%8D%E9%BD%8A%28alignment%29
http://linyacheng.blogspot.tw/2011/10/c-data-structure-alignment.html

Data Structure Alignment 究竟是何物?
  它是為了加快執行速度而有的一種方式
  由於 CPU 架構的關係,在讀 memory 的時候會以一個 word 為單位(在 32-bit 系統是4 bytes 為1個 word),因此 data 在儲存時,memory offset 便會是 word size 倍數,藉此增加效能

Data Structrue Alignment 分成兩個部分:
    . Data Alignment: Data Alignment 即是上述所述,data 的 memory offset 會是 word size 的倍數.
    . Data Structure Padding: 在 data structure 尾巴塞入一些無意義的 bytes,使下一個 data structure 的 memory offset 能夠對齊.

在一個 32-bit 系統,常見的 C/C++ compiler 的 default alignments為:

  • char (1 byte): 1-byte aligned
  • short (2 bytes): 2-byte aligned
  • int (4 bytes): 4-byte aligned
  • float (4 bytes): 4-byte aligned
  • double (8 bytes): 8-byte aligned (in windows) / 4-byte aligned (in Linux)
  • Any pointer (4 bytes): 4-byte aligned
  用法:
    #pragma pack(push)  /* push current alignment to stack */
    #pragma pack(n)     /* set alignment to n byte boundary */
    #pragma pack()     /* cancel previous alignment to n byte boundary */ 

    #pragma pack(push)  /* pop previous alignment to stack */
    __attribute__ ((pack(n))) /* 要求某個structure使用pack(n) */ 

 Ex 1: 使用系統預設的alignment下為4Bytes, 所以下列memory佔用的話是12 Bytes, 非7 Bytes.
        struct align_test
        {
                char x; //1 byte
                int y;  //4 bytes
                short int z;  //2bytes
        };


 Ex 2:如果指定的 n 大於 structure 中成員的最大 size 將不起作用,仍依 size 最大的成員進行對齊。sizeof( struct D) 就會是 4 + 1 + p(1) = 6 bytes。
    #pragma pack(2)
    struct D {                            
      int a;  
      char b;        
    };
    #pragma pack()



 Ex 3: 為了更徹底的了解系統預設alinment的行為, 請看下列解釋
 Structure's Size: 12 bytes.
   struct MixedData {
     char data1;
     short data2;     int data3;      char data4;   };
Memory Offset:
  • data1:type 是 char,char 是 1-byte aligned,意思是其 memory 位置開頭必須是1 的倍數,所以就放在 0x00 的位置(假設是從 0x00 開始)
  • data2:type 是 short,short 是 2-byte aligned,所以其 memory 位置開頭必須是 2的倍數,因此 0x01 就會被放置一個 1 byte 的 padding,然後在 0x02 放 data2。因為 short size 比 char size 大,因此會 allocate 2-byte memory
  • data3:同理,4-byte aligned,allocate 4-byte memory
  • data4:雖然 size 是 1 byte,但由於目前最大的 alignment size 為 4 bytes,因此會 allocate 4-byte memory,後面的 3 bytes 就會是 padding
  • Compiler 在 alignment 時最大原則:以目前最大的 alignment size向前 padding,向後 allocate大小.
 Ex 4: 倘若細微變動Ex 3的結構的, 又會是如何?   Structure's Size: 8 bytes
     struct ReorderedMixedData {   // after reordering
        char data1;
        char data4; // reordered
        short data2;
        int data3;
     };
Memory Offset:


  • 在宣告 structure 時, 成員的順序其實是會有所影響的, 因此要宣告一個好的 structure,應該要減少 padding來降低 structure 的 size.



 Ex 5: 設定 alignment pack size 為 1 byte, 因此原本 data2 和 data4 會出現 padding 的情況就消失了. 實際結果如下: Structure's Size: 8 bytes


    #pragma pack(push)  // push current alignment to stack 
    #pragma pack(1)     // set alignment to 1 byte boundary

    struct PackedData {
      char  data1;
      short data2;
      int   data3;
      char  data4;
    };
    #pragma pack(pop)   // restore original alignment from stack


P.S:
   (a). 宣告 structure 時,成員的順序看起來沒有影響,但其實是有的,一個好的 structure 應該要減少 padding 產生,這樣便能降低 structure 的大小.    (b). pack(n) 如果n比structure中最大member size還大 那還是會用最大member的size來alignment 
   (c). 在網路傳輸資料時, 需要用pack(1)來避免compiler作padding的動作.

Tuesday, October 2, 2012

MTD簡介, /dev/mtd與/dev/mtdblok的區別

轉:http://kezeodsnx.pixnet.net/blog/post/34193523-mtd%E7%B0%A1%E4%BB%8B

轉:http://blog.csdn.net/liangkaiming/article/details/6235031

Character or block device?
Character指的是keyboard,mouse這類device。可以向這些device讀東西出來,但不能做seek,也沒有size的觀念。而block device則可seek,也有size,一個block通常是512 bytes。
而flash的bahavior像block device,其不同點為:
1. block device的write/erash是沒有區別的
2. block device是由sector組成,而MTD則是erase block
3. hardware處理bad block,而MTD需要software
因此就有了MTD這個特別的device type。
Note: USB stick, MMC, SD, CompactFlash也稱為flash,但這些都不是flash device。他們內部有flash chip,但除了chip,還有一層模擬為block device的translation layer,且這個translation實作在硬體上,所以應視為hard drive。

MTD subsystem
MTD (Memory Techology Device)是一個subsystem,用來簡化底層的flash device (ROM/NAND/OneNAND/NOR)的driver。在MTD下,driver只需提供read/write/erase的功能,而不需知道其上是使用FTL (Flash Translation Layer),還是FFS (Flash File System)。
FTL是用來模擬Flash device為block device,因此可以在上面create filesystem。而FFS則是為flash device設計的filesystem。各有其優缺點,可見Wiki
為了讓既有的filesystem (fat/ext/xfs...)能用在flash device上,就需要FTL這個software layer。mtdblock就是其中一種FTL。要注意的是使用FTL時,如果改了其中一個sector,就必須讀一整個eraseblock到 memory,然後erase,最後再把整個eraseblock寫回,顯然cost很高。因此通常建議使用mtdblock_ro,也可再加上 squashfs。將mtdblock mount成rw前,一定要多想一下是否值得這樣做。
mtdblock也沒有wear leveling。

Linux系统中/dev/mtd /dev/mtdblock 的區別

MTD(memory technology device內存技朮設備)是用於訪問memory設備(ROMflash)的Linux的子系統。MTD的主要目的是為了使新的memory設備的驅 動更加簡單,為此它在硬件和上層之間提供了一個抽象的接口。MTD的所有源代碼在/drivers/mtd子目錄下。我將CFI接口的MTD設備分為四層 (從設備節點直到底層硬件驅動),這四層從上到下依次是:設備節點、MTD設備層、MTD原始設備層和硬件驅動層。
MTD
字符驅動程序允直接訪問flash器件通常用flash建文件系,也可以用來直接訪問不頻繁修改的數據。
MTD
塊設備驅動程序可以flash器件偽裝塊設備實際上它通把整erase block放到ram里面訪問,然后再更新到flash,用可以在這個塊設備建通常的文件系
1. /dev/mtdN Linux 中的MTD架搆中,系統自己實現的mtd分區所對應的字符設備(mtd設備分成多個區,每個區字符設備),其里面添加了一些ioctl,支持很多命令,如MEMGETINFOMEMERASE等。
mtd-util中的flash_eraseall等工具,就是ioctl實現的工具實現一些關於Flash的操作。比如,mtd 工具中的 flash_eraseall中的:
if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
   fprintf(stderr, "%s: %s: unable to get MTD device info/n", exe_name, mtd_device);
   return 1;
}

其中,MEMGETINFO,就是Linux MTD中的drivers/mtd/mtdchar.c中的:
static int mtd_ioctl(struct inode *inode, struct file *file,
       u_int cmd, u_long arg)
{
。。。。。
case MEMGETINFO:
   info.type = mtd->type;
   info.flags = mtd->flags;
   info.size = mtd->size;
   info.erasesize = mtd->erasesize;
   info.writesize = mtd->writesize;
   info.oobsize = mtd->oobsize;
   /* The below fields are obsolete */
   info.ecctype = -1;
   info.eccsize = 0;
   if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
    return -EFAULT;
   break;

。。。
}
/dev/mtdblockN,是Nand Flash驅動中,驅動用add_mtd_partitions()添加MTD設備分區(其實就是將mtd設備進行不衕的分區,當mtd設備還是一樣的,所以mtdblock分區與mtd分區肯定是對應的),而生成的對應的塊設備
根據以上內容,也就更加明白,為什么不能用nandwrite,flash_eraseall,flash_erase等工具去對/dev/mtdblockN去操作了。因為/dev/mtdblock中不包含對應ioctl,也就沒有定義對應的命令,不支持你這么操作。
2. mtd char 設備的主設備號是90,而mtd block設備的主設備號是31
# ls /dev/mtd* -l crw-r-----    1 root     root      90,   0 May 30 2007 /dev/mtd0
crw-r-----    1 root     root      90,   2 May 30 2007 /dev/mtd1
crw-r-----    1 root     root      90,   4 Jul 17 2009 /dev/mtd2
crw-r-----    1 root     root      90,   6 May 30 2007 /dev/mtd3
crwxrwxrwx    1 root     root      90,   8 May 30 2007 /dev/mtd4
crwxrwxrwx    1 root     root      90, 10 May 30 2007 /dev/mtd5
crwxrwxrwx    1 root     root      90, 12 May 30 2007 /dev/mtd6
crwxrwxrwx    1 root     root      90, 14 May 30 2007 /dev/mtd7
crwxrwxrwx    1 root     root      90, 16 May 30 2007 /dev/mtd8
crwxrwxrwx    1 root     root      90, 18 May 30 2007 /dev/mtd9
# ls /dev/mtdblock* -lbrw-r-----    1 root     root      31,   0 May 30 2007 /dev/mtdblock0
brw-r-----    1 root     root      31,   1 May 30 2007 /dev/mtdblock1
brw-r-----    1 root     root      31,   2 May 30 2007 /dev/mtdblock2
brw-r-----    1 root     root      31,   3 May 30 2007 /dev/mtdblock3
brwxrwxrwx    1 root     root      31,   4 May 30 2007 /dev/mtdblock4
brwxrwxrwx    1 root     root      31,   5 May 30 2007 /dev/mtdblock5
brwxrwxrwx    1 root     root      31,   6 May 30 2007 /dev/mtdblock6
brwxrwxrwx    1 root     root      31,   7 May 30 2007 /dev/mtdblock7
brwxrwxrwx    1 root     root      31,   8 May 30 2007 /dev/mtdblock8
brwxrwxrwx    1 root     root      31,   9 May 30 2007 /dev/mtdblock9
此設備號,定義在/include/linux/mtd/mtd.h中 :
#define MTD_CHAR_MAJOR   90
#define MTD_BLOCK_MAJOR 31
3. 其中,mtd的塊設備的大小,可以通過查看分區信息獲得:
# cat /proc/partitions
major minor #blocks name
31     0       1024 mtdblock0
31     1       8192 mtdblock1
31     2     204800 mtdblock2
31     3      65536 mtdblock3
31     4     225280 mtdblock4
上面中顯示的塊設備大小,是block的數目,每個block1KB
而每字符設備,其就是對應著上面的每個塊設備。即/dev/mtd0對應/dev/mtdblock0,其他以此推。話說mtdblockN的一些性,也就是mtdN性,比如大小。
4。對每個mtd字符設備的操作,比如利用nandwrite去對/dev/mtd0寫數據,實際就是操作/dev/mtdblock0
而這些操作里面涉及到的偏移量offset,都指的是此mtd 分區內的偏移。比如向/dev/mtd1offset0的位置寫入數據,實際操作的是物理偏移offset=/dev/mtd0的大小=1MB=0x100000
5.mtd的字符設備和塊設備的命名規則,可以參考下表:
Table 7-1. MTD /dev entries, corresponding MTD user modules, and relevant device major numbers
/dev entry
Accessible MTD user module
Device type
Major number
mtdN
char device
char
90
mtdrN
char device
char
90
mtdblockN
block device, read-only block device, JFFS, and JFFS2
block
31
nftlLN
NFTL
block
93
ftlLN
FTL
block
44

Table 7-2. MTD /dev entries, minor numbers, and naming schemes
/dev entry
Minor number range
Naming scheme
mtdN
0 to 32 per increments of 2
N = minor / 2
mtdrN
1 to 33 per increments of 2
N = (minor - 1) / 2
mtdblockN
0 to 16 per increments of 1
N = minor
nftlLN
0 to 255 per sets of 16
L = set;[2] N = minor - (set - 1) x 16; N is not appended to entry name if its value is zero.
ftlLN
0 to 255 per sets of 16
Same as NFTL.


The Linux MTD,YAFFS Howto上面這樣寫道:
Erase the mtdblock0
/>eraseall /dev/mtd0Create the mount directory and mount
/>mkdir -p /mnt/flash0
/>mount -t yaffs /dev/mtdblock0 /mnt/flash0
為什么eraseallmtd0操作?而不對mtdblock0操作?nand不是塊設備嘛,mtdblock就是塊設備呀。mtd0,mtd1mtdblock0,mtdblock1是不是一一對應的?

Tuesday, January 10, 2012

C語言中資料結構(struct)的大小 - __attribute__((packed))


轉:http://chunchaichang.blogspot.com/2010/06/cstruct-attributepacked.html?showComment=1326206070971#c2960032654187314345


C語言中資料結構(struct)的大小 - __attribute__((packed))

通常在PC上寫程式時,很少會去管struct會佔掉多少記憶體。

當要使用到時,也不會想去用手算到底佔掉多少,大多是直接使用sizeof來做計算。

然而sizeof計算出來的值往往不會如我們想的一樣。因為compiler為了效能考量,會自動地為我們

做最佳化,也就是資料對齊。為了這個目的,compiler會為struct多準備一些記憶體。

我們可以看以下的code:

struct ABC {

int index;

char name[6];

int score;

};



struct DEF{

int att;

char name[3];

};



int main(void)

{

printf("sizeof(ABC) = %d\n", sizeof(struct ABC));

printf("sizeof(DEF) = %d\n", sizeof(struct DEF));

return 0;



}

說明:

1. 若我們直接去計算struct ABC和strcut DEF時,

struct ABC = 4 + 6 + 4 = 14 (struct ABC用掉14個byte)

strcut DEF = 4 + 3 = 7 (struct DEF用掉7個byte)

2. 但真的是這樣嗎?程式執行出來的結果卻是,

sizeof(ABC) = 16

sizeof(DEF) = 8

3. 這就是compiler為我們做了對齊的最佳化,將這二個的struct都調整成2的次方。

這樣有利於運算。



這樣的做法在PC上通常沒有問題,但若是在嵌入式系統上,記憶體必需要錙珠必較時

,我們就必須要考量到使用struct所佔掉的記憶體空間,可以使用__attribute__((packed));這個關鍵字,

它的作用在於叫compiler不要為我們做對齊的最佳化,因此,計算的結果就會如同我們所想的一樣了。

struct ABC {

int index;

char name[6];

int score;

} __attribute__((packed));;



struct DEF{

int att;

char name[3];

} __attribute__((packed));;



int main(void)

{

printf("sizeof(ABC) = %d\n", sizeof(struct ABC));

printf("sizeof(DEF) = %d\n", sizeof(struct DEF));

return 0;



}

這樣就會得到以下的結果了。

sizeof(ABC) = 14

sizeof(DEF) = 7



這裡沒有哪一種用法比較好的問題,端看在使用上的需求,

要運算速度快,就需要資料對齊。要節省記憶體的使用,就取消對齊。

Introduction PPPoE protocol


轉:http://chunchaichang.blogspot.com/2011/10/introduction-pppoe-protocol.html


Introduction PPPoE protocol

1. 前言

PPPOE(PPP over Ethernet, RFC2516,值得注意的是此RFC不是Standard而是Information類型的)定義了如何在乙太網上傳輸PPP資料包的方法,目前流行的寬頻類型
ADSL就是通過PPPoE實現的。

2. 通信過程概述

建立PPPOE通道(ADSL撥號)分兩個階段:發現階段和PPP會話階段。

在發現階段,乙太網上的客戶機要找到一個訪問集中器(ACAccess Concentrator),就是ADSL MODEM,一般家用時一般就只有一個AC;但如果是一個乙太網內可能會有多條ADSL,就會有多個AC,這時客戶機就從中選擇一個。發現階段完成後,客戶機和AC都得到要在乙太網上建立PPP通道的相關資訊。

發現階段是無狀態的,也就是兩邊都不用保存以前的狀態資訊;只有PPP會話開始後,雙方就要建立一個虛擬的PPP通信介面,具體在Linux下會有ppp0網卡,在windows下網路連接中增加ADSL的介面。

3. 協議頭格式

3.1 協議值

PPPOE資料是直接在乙太頭資料之上的,其等級和ARPIP等是相同的,在乙太頭的類型欄位中,用0x8863 表示是PPPOE發現階段資料,用0x8864表示PPP會話階段資料,如下所示。(類比:0x0800表示IP資料,0x0806表示ARP資料)

                   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |       DESTINATION_ADDR        |
                  |          (6 octets)           |
                  |                               |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |         SOURCE_ADDR           |
                  |          (6 octets)           |
                  |                               |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |    ETHER_TYPE  (2 octets)     |
                  |     0x8863 or 0x8864          |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |                               |
                  |       PPPOE Header            |
                  |          (6 octets)           |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  ~                               ~
                  ~           payload             ~
                  ~                               ~
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  |           CHECKSUM            |
                  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.2 PPPOE協議頭
                        1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  VER  | TYPE  |      CODE     |          SESSION_ID           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |            LENGTH             |           payload             ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

PPPOE協定頭有6個位元組,正好和14位元組的乙太頭實現了4位元組對齊,包括以下欄位:
VER:版本號,4位,必須為0x01
TYPE:類型,4位,必須是0x01
CODE8位,在發現階段和PPP會話階段有不同的定義,表示PPPOE資料類型
SESSION_ID16位,用來定義一個PPP會話,在發現過程中定義。
LENGTH16位元,表示負載長度,不包括乙太頭和PPPOE頭。

4. 發現階段

PPPOE發現階段資料的乙太類型是0x8863

4.1 TAG

在發現階段用於交換客戶機和AC的資訊,建立PPPOE通道,負載資訊都是PPPOE資訊,並沒有上層協定資料。
發現階段的負載稱為TAG,一個TAG資訊格式如下,負載資訊中可能會包含多個TAG
                        1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          TAG_TYPE             |        TAG_LENGTH             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          TAG_VALUE ...                                        ~
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

TAG_TYPE16位,TAG類型
TAG_LENGTH16位元,表示TAG_VALUE部分的長度
TAG_VALUETAG
TAG_TYPE可取以下值(注意第一位元組為2表示是錯誤資訊)
   0x0000 End-Of-List
   0x0101 Service-Name
   0x0102 AC-Name
   0x0103 Host-Uniq
   0x0104 AC-Cookie
   0x0105 Vendor-Specific
   0x0110 Relay-Session-Id
   0x0201 Service-Name-Error
   0x0202 AC-System-Error
   0x0203 Generic-Error

4.2 PPPOE主動發現初始包

PPPOE主動發現初始包(PPPoE Active Discovery Initiation, PADI)由客戶機發出,乙太頭中的目的地址是乙太廣播位址FF:FF:FF:FF:FF:FFPPPOE頭中的CODE 0x09SESSION_ID值必須為0,負載部分必須只包含一個Service-Name類型的TAG表示請求的服務類型,另外可以包含其他TAG,整個PPPOE包不能超過1484位元組,這樣省出的16位元組可以由ADSL中繼設備添加中繼TAG
一個PADI包的例子為:
                     1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         0xffffffff                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           0xffff              |        Host_mac_addr          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Host_mac_addr (cont)                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    ETHER_TYPE = 0x8863        | v = 1 | t = 1 |  CODE = 0x09  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     SESSION_ID = 0x0000       |      LENGTH = 0x0004          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      TAG_TYPE = 0x0101        |    TAG_LENGTH = 0x0000        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4.3 PPPOE主動發現提議包

PPPOE主動發現提議包(PPPoE Active Discovery Offer, PADO)AC發出,用來回應客戶機的PADI包,乙太頭中的目的地址是客戶機的MAC地址,PPPOE頭中的CODE 0x07SESSION_ID值必須為0,負載部分必須包含一個AC-Name類型的TAG,用來指示本AC的名稱,一個在PADI包中指定的 Service-NameTAG,另外可以包含其他Service-NameTAG。如果AC不對該客戶機提供服務,AC就不回應PADO包。

一個PADO包的例子為:
                     1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                         Host_mac_addr                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Host_mac_addr (cont)     | Access_Concentrator_mac_addr  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             Access_Concentrator_mac_addr (cont)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    ETHER_TYPE = 0x8863        | v = 1 | t = 1 |  CODE = 0x07  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     SESSION_ID = 0x0000       |      LENGTH = 0x0020          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      TAG_TYPE = 0x0101        |    TAG_LENGTH = 0x0000        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      TAG_TYPE = 0x0102        |    TAG_LENGTH = 0x0018        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x47      |     0x6f      |     0x20      |     0x52      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x65      |     0x64      |     0x42      |     0x61      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x63      |     0x6b      |     0x20      |     0x2d      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x20      |     0x65      |     0x73      |     0x68      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x73      |     0x68      |     0x65      |     0x73      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     0x68      |     0x6f      |     0x6f      |     0x74      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

4.4 PPPOE主動發現請求包

PPPOE主動發現請求包(PPPoE Active Discovery Request, PADR)由客戶機發出,因為可能會有多個AC對客戶機發出的PADI包回應了PADO包,客戶機從回應的PADO包中選擇一個AC發送PADR包,乙太頭中的目的地址是該ACMAC地址,PPPOE頭中的CODE0x19SESSION_ID值必須為0,負載部分必須只包含一個Service- Name類型的TAG表示請求的服務類型,另外可以包含其他TAG

4.5 PPPOE主動發現會話確認包

PPPOE主動發現會話確認包(PPPoE Active Discovery Session-confirmation, PADS)AC發出,收到客戶機的PADR包後,AC將產生一個SEESSION_ID值用來標誌本次PPP會話,以PADR包方式發送給客戶機。乙太頭中的目的地址是客戶機的MAC地址,PPPOE頭中的CODE0x65SESSION_ID值必須為所生成的那個SESSION_ID,負載部分必須只包含一個Service-Name類型的TAG,表示該服務類型被AC接受,另外可以包含其他TAG。如果AC不接受PADR中的Server- NamePADS中則包含一個Service-Name-Error類型的TAG,這時SESSION_ID設置為0

4.6 PPPOE主動發現停止包

PPPOE主動發現停止包(PPPoE Active Discovery Terminate, PADT)表示PPPOE會話過程的結束,AC和客戶機都可以主動發出。乙太頭中的目的地址是對方的MAC地址,PPPOE頭中的CODE 0xa7SESSION_ID值必須為PPPOE會話過程的SESSION_ID,不需要TAG

5. PPP會話階段

PPP會話階段,PPP包被封裝在PPPOE乙太幀中,乙太包目的地址都是單一的,乙太協定為0x8864PPPOE頭的CODE必須為 0SESSION_ID必須一直為發現階段協商出的SEESION_ID值,PPPOE的負載是整個PPP包,PPP包前是兩位元組的PPP協定ID值。
一個PPPOE會話過程包的例子為:
                     1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Access_Concentrator_mac_addr                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Access_Concentrator_mac_addr(c)|        Host_mac_addr          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Host_mac_addr (cont)                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    ETHER_TYPE = 0x8864        | v = 1 | t = 1 |  CODE = 0x00  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     SESSION_ID = 0x1234       |      LENGTH = 0x????          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    PPP PROTOCOL = 0xc021      |        PPP payload            ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

注意:由於PPPOE頭是6位元組,PPP協定ID號兩個位元組,一共要佔用8個位元組,而乙太網的MTU值為1500,所以上層PPP負載資料不能超過1492位元組,所以PPP協商時協商的最大接收單元值不能超過1492位元組,也就是相當於在PPPOE環境下的MTU1492位元組。

6. ADSL撥號過程簡述

客戶機啟動撥號程式,發送PADI包,ADSL MODEM回應PADO包,客戶機再發送PADR包,ADSL MODEM回應PADS包後建立PPPOE通道,隨後客戶機進行普通的PPP協議撥號過程,不過PPP資料包都是包裝進乙太幀中的,撥號成功後客戶機和伺服器之間建立了PPP通道,ADSL MODEM起到將乙太幀轉換為PPP包的作用。ADSL雖然是用電話線,但所用頻率不是通話用的頻率,所以ADSL撥號不影響打電話。通信結束後,會發送 PADT斷開PPPOE通道。

7. 結論

由於ADSL的大面積使用,PPPOE也隨之應用,瞭解其通信協定和資料格式對於底層驅動的開發、協定分析、存取控制等是必要的,現在不論在windows還是linux下都可以很好地支援PPPOE