首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

ICMP 協定深度解析 (四):RAW Socket 實作 Ping 與 Traceroute 基礎

     編輯:關於VC++

承接前三篇 ICMP 協定原理、WinSock 介紹與 ICMP.dll 應用,本文展示使用 RAW Socket 直接操作 IP/ICMP 封包實作 Ping 功能。雖然程式碼較 ICMP.dll 版本龐大,但這才是網路程式設計的核心功夫——微軟已宣佈不再長期支援 ICMP.dll,而 RAW Socket 屬標準 BSD Socket 延伸,可移植性極強。

核心概念:手工構造 IP + ICMP 封包

RAW Socket (SOCK_RAW, IPPROTO_ICMP) 允許應用程式自行填寫 IP 標頭與 ICMP 承載,接收時包含完整 IP 標頭。關鍵結構定義:


// IP 標頭 (位元欄位對應 RFC 791)
struct IPHeader {
    BYTE  h_len:4;      // 標頭長度 (32-bit 字數)
    BYTE  version:4;    // IP 版本 (4)
    BYTE  tos;          // 服務類型
    USHORT total_len;   // 總長度 (位元組)
    USHORT ident;       // 識別碼
    USHORT flags;       // 旗標 + 片段位移
    BYTE  ttl;          // 生存時間 (TTL) —— Traceroute 關鍵
    BYTE  proto;        // 協定號 (ICMP=1)
    USHORT checksum;    // IP 標頭檢驗和
    ULONG source_ip;    // 來源 IP
    ULONG dest_ip;      // 目的 IP
};

// ICMP 標頭 (Echo Request/Reply)
struct ICMPHeader {
    BYTE  type;         // 8=Echo Request, 0=Echo Reply, 11=TTL Expired
    BYTE  code;         // 子代碼
    USHORT checksum;    // ICMP 檢驗和
    USHORT id;          // 識別碼 (用 Process ID)
    USHORT seq;         // 序號
    ULONG timestamp;    // 非標準欄位,用於計算 RTT
};

完整程式流程 (rawping_driver.cpp)

  1. 參數解析:目標主機、封包大小 (預設 32 bytes)、TTL (預設 30)
  2. WSAStartup:初始化 Winsock 2.1+
  3. 建立 RAW Socket:WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, ...) 需系統管理員權限
  4. 設定 TTL:setsockopt(sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))
  5. 解析目標位址:inet_addr → 失敗則 gethostbyname
  6. 分配收發緩衝區:發送緩衝 = 封包大小;接收緩衝 = 封包大小 + IP 標頭
  7. 初始化 ICMP 封包:Type=8, Code=0, Checksum=0 → 填充資料 (0xDEADBEEF 模式) → 計算檢驗和
  8. 發送:sendto
  9. 接收迴圈:recvfrom → 跳過 IP 標頭 (h_len * 4) 取得 ICMP 標頭 → 驗證 ID/序號 → 解碼回應類型
  10. 統計輸出:位元組數、來源 IP、序號、跳數 (256 - TTL)、RTT

關鍵演算法:Internet 檢驗和計算


USHORT ip_checksum(USHORT* buffer, int size) {
    unsigned long cksum = 0;
    while (size > 1) { cksum += *buffer++; size -= sizeof(USHORT); }
    if (size) cksum += *(UCHAR*)buffer;           // 處理奇數位元組
    cksum = (cksum >> 16) + (cksum & 0xffff);     // 折疊進位
    cksum += (cksum >> 16);
    return (USHORT)(~cksum);                      // 取反碼
}

注意:計算前必須將檢驗和欄位歸零。

回應解碼邏輯 (decode_reply)

  • Type 0 (Echo Reply):正常回應,顯示跳數與 RTT
  • Type 11 (TTL Expired):中繼路由器回報,可用於 Traceroute
  • Type 3 (Destination Unreachable):目標不可達
  • 其他類型視為錯誤

跳數估算假設初始 TTL 為 256 (實際作業系統不同:Windows XP=128, Linux=64 等),作者提供啟發式修正邏輯。

改進建議:解決同步阻塞問題

現行 recvfrom 同步等待,網路異常時會無限阻塞。三種改良方向:

  1. 多執行緒:Ping 封裝於工作執行緒,主執行緒計時 WaitForSingleObject
  2. select():設定 timeval 超時,非阻塞輪詢 Socket 可讀性
  3. WSAAsyncSelect():視窗訊息驅動 (WM_SOCKET_NOTIFY)

延伸應用:Traceroute 原型

修改發送封包的 IP 標頭 TTL = 1, 2, 3...,收到 Type 11 (TTL Expired) 即為該跳路由器 IP。本程式碼已奠定基礎,僅需在迴圈中遞增 TTL 並解析 ICMP_TTL_EXPIRE 回應的來源位址。

完整原始碼包含 rawping_driver.cpp、rawping.h、rawping.cpp 三個檔案,註解詳盡,是學習 RAW Socket 網路程式設計的極佳教材。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved