簡易版WireShark實現-相關網絡知識(1)

​在Linux下數據鏈路層的訪問通常都是通過編寫內核驅動程序來實現的,在應用層使用SOCK_PACKET類型的協議族可以實現部分功能。

  • SOCK_PACKET類型

    建立套接字的時候選擇SOCK_PACKET類型,內核將不對網絡數據進行處理而直接交給用戶,數據直接從網卡的協議棧交給用戶。建立一個SOCK_PACKET類型的套接字使用如下方式:

    int sock = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL); 

    ​其中,AF_INET表示因特網協議族,SOCK_PACKET表示截取數據幀的層次在物理層,網絡協議棧對數據不做處理。ETH_P_ALL是Linux系統頭文件if_ether.h中的一個宏定義,值爲0x0003,表示截取的數據幀的類型不確定,並且處理所有包。如果只想處理IP層協議的數據包的話,可以將這個參數改成 ETH_P_IP。

    ​ 使用SOCK_PACKET的時候,需要關注的方面主要如下:

    1. 協議族選擇
    2. 獲取原始包
    3. 定位IP包
    4. 定位TCP包
    5. 定位UDP包
    6. 定位應用層數據
  • 從套接字中讀取以太網幀

    ​ 設置完套接字後,接下來就需要從套接字中讀取以太網幀。在此之前,先來了解一下以太網幀的數據結構,具體如下:

    這裏寫圖片描述

    ​以太網幀總長度最大爲1518字節,最小爲64字節。其中,目的MAC與源MAC地址均爲6字節,協議類型爲2字節,含有46~1500字節的數據,尾部爲4個字節的CRC校驗和。以太網的CRC校驗和一般由硬件自動設置或者剝離,應用層不用考慮。

    ​ 頭文件【linux/if_ether.h】中與以太網幀相關的常量定義如下:

/*
 *  IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
 *  and FCS/CRC (frame check sequence).
 */

#define ETH_ALEN    6       /* Octets in one ethernet addr   */
#define ETH_HLEN    14      /* Total octets in header.   */
#define ETH_ZLEN    60      /* Min. octets in frame sans FCS */
#define ETH_DATA_LEN    1500        /* Max. octets in payload    */
#define ETH_FRAME_LEN   1514        /* Max. octets in frame sans FCS */
#define ETH_FCS_LEN 4       /* Octets in the FCS         */

​ 以太網頭部結構的定義如下:

  /*
   *    This is an Ethernet frame header.
   */

  struct ethhdr {
    unsigned char   h_dest[ETH_ALEN];   /* destination eth addr */
    unsigned char   h_source[ETH_ALEN]; /* source ether addr    */
    __be16      h_proto;        /* packet type ID field */
  } __attribute__((packed));

套接字文件描述符建立後,就可以從此描述符中讀取數據,數據格式爲上述的以太網數據,即以太網幀。在套接字建立以後,就可以從此套接字中循環讀取捕獲的鏈路層以太網幀。建立一個大小爲1518的緩衝區,並將以太網頭部指向此緩衝區,即可讀取相關的數據,如下:

unsigned char buffer[1518];       //臨時存儲捕獲的數據包
struct ethhdr* p_ethhdr;
int n;
unsigned short proto_type_ethhdr;
unsigned char *ethhead;
qstring src_mac_str = "", dst_mac_str = ""

n = recvfrom(sock, buffer, sizeof(buffer), 0, null, null);
ethhead = buffer;
p_ethhdr = (ethhdr *)(buffer);
proto_type_ethhdr = htons(ethhdr->h_proto);

/*目的mac地址*/
dst_mac_str.sprintf("%02x:%02x:%02x:%02x:%02x:%02x", 
    ethhead[0], ethhead[1], ethhead[2],
    ethhead[3], ethhead[4], ethhead[5]);

/*源mac地址*/
src_mac_str.sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
    ethhead[6], ethhead[7], ethhead[8], 
    ethhead[9], ethhead[10], ethhead[11]);

qdebug() << dst_mac_str;    //打印
qdebug() << src_mac_str;

switch (proto_type_ethhdr)
{
    case(0x0800):   //ip協議
        break;
    case(0x0806):   //arp協議
        break;
    case(0x8035):   //rarp協議
        break;
}

​以太網幀中的協議類型是一個2字節的大小的變量,讀取它的值可以判斷當前幀是屬於什麼類型的數據:0x0800是IP協議的,0x0806是ARP協議,0x8035是RARP協議,當然,還有很多其他類型的協議,這裏就不一一列舉了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章