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

Linux C 語言實現 ping (ICMPv4 Echo Request) 完整原始碼解析

     編輯:關於C語言

程式架構總覽

使用原始套接字 (SOCK_RAW, IPPROTO_ICMP) 手工構造/解析 ICMP 封包,實現 ping 核心功能:每秒發送 Echo Request、接收 Echo Reply、計算 RTT。

關鍵資料結構


struct proto {
    struct sockaddr *sasend;   // 發送目標位址
    struct sockaddr *sarecv;   // 接收來源位址
    socklen_t salen;           // 位址長度
    int icmpproto;             // IPPROTO_ICMP
};

pid_t g_pid;          // 進程 ID 低 16 位作為 ICMP 識別碼
int g_sockfd;         // 原始套接字
struct proto g_proto = { NULL, NULL, 0, IPPROTO_ICMP };

核心流程

  1. main():解析主機名 → getaddrinfo → 設定 g_proto → signal(SIGALRM, sig_alrm) → readloop()
  2. readloop():建立原始套接字 (需 root) → setuid(getuid()) 放棄權限 → 設定接收緩衝區 → 首次 sig_alrm 發送 → recvmsg 迴圈接收 → proc_msg 處理
  3. sig_alrm():每秒觸發 → send_msg() → alarm(1) 重設定時器
  4. send_msg():填充 ICMP 標頭 (Type=8, Code=0, ID=pid, Seq=nsent++)、資料區填入 gettimeofday 時間戳 → 計算校驗和 → sendto
  5. proc_msg():剝離 IP 標頭 → 驗證 ICMP 類型/ID/序號 → 取出時間戳 → tv_sub 計算 RTT → 輸出

關鍵技術細節

原始套接字權限


g_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  // 需 CAP_NET_RAW (root)
setuid(getuid());  // 發送後立即降權,安全原則

ICMP 校驗和計算 (RFC 1071)


uint16_t in_cksum(uint16_t *addr, int len) {
    uint32_t sum = 0;
    while (len > 1) { sum += *addr++; len -= 2; }
    if (len == 1) { sum += *(uint8_t*)addr; }
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    return ~sum;
}

時間差計算


void tv_sub(struct timeval *out, struct timeval *in) {  // out -= in
    if ((out->tv_usec -= in->tv_usec) < 0) {
        --out->tv_sec;
        out->tv_usec += 1000000;
    }
    out->tv_sec -= in->tv_sec;
}

位址轉字串 (僅 IPv4)


char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen) {
    static char str[128];
    struct sockaddr_in *sin = (struct sockaddr_in *)sa;
    inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str));
    return str;
}

編譯與執行


gcc -o myping myping.c
sudo ./myping www.baidu.com

輸出範例

PING www.a.shifen.com (115.239.210.26): 56 data bytes
64 bytes from 115.239.210.26: seq=0, ttl=128, rtt=31.272 ms
64 bytes from 115.239.210.26: seq=1, ttl=128, rtt=34.722 ms
...

限制與擴展方向

  • ❌ 僅支援 IPv4,單一主機參數,無選項解析
  • ✅ 可擴展:支援 -c count、-i interval、-s packetsize、IPv6 (ICMPV6)、統計摘要 (min/avg/max/mdev)
  • ⚠️ 原始套接字需 root,生產環境考慮 capabilities (setcap cap_net_raw+ep) 或 setuid 二進制
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved