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

Linux 內核模塊開發:Netfilter 協議棧抓包 + Procfs 輸出實戰

     編輯:關於C語言

架構概覽

  • Netfilter Hook:註冊 NF_INET_PRE_ROUTING 掛鉤,攔截入站 IP 包
  • 協議解析:skb → ethhdr → iphdr → tcphdr,提取 MAC/IP/端口
  • Procfs 接口:創建 /proc/port,用戶態 cat /proc/port 讀取最新捕獲信息

核心數據結構

struct nf_hook_ops nfho;           // Netfilter 掛鉤操作結構
struct proc_dir_entry *proc_file;  // Procfs 入口
char *kbuf;                        // 內核緩衝區 (kmalloc)
int kbuf_len;                      // 有效數據長度

Hook 處理函數

unsigned int hook_func(unsigned int hooknum,
                       struct sk_buff **skb,
                       const struct net_device *in,
                       const struct net_device *out,
                       int (*okfn)(struct sk_buff *)) {
    struct sk_buff *sb = *skb;
    struct ethhdr *eth = (struct ethhdr *)skb_mac_header(sb);
    struct iphdr *iph = (struct iphdr *)skb_network_header(sb);
    struct tcphdr *tcph = (struct tcphdr *)skb_transport_header(sb);

    kbuf_len = 0;
    if (ntohs(eth->h_proto) == ETH_P_IP && iph->protocol == IPPROTO_TCP) {
        // 格式化輸出到內核緩衝區
        kbuf_len += scnprintf(kbuf + kbuf_len, 1024 - kbuf_len,
            "smac=%02x:%02x:%02x:%02x:%02x:%02x\n"
            "dmac=%02x:%02x:%02x:%02x:%02x:%02x\n"
            "sip=%pI4\n"
            "dip=%pI4\n"
            "sport=%d\n"
            "dport=%d\n\n",
            eth->h_source[0], eth->h_source[1], eth->h_source[2],
            eth->h_source[3], eth->h_source[4], eth->h_source[5],
            eth->h_dest[0], eth->h_dest[1], eth->h_dest[2],
            eth->h_dest[3], eth->h_dest[4], eth->h_dest[5],
            &iph->saddr, &iph->daddr,
            ntohs(tcph->source), ntohs(tcph->dest));
    }
    return NF_ACCEPT;  // 繼續正常協議棧處理
}

關鍵點:

  • 使用 skb_mac_header/skb_network_header/skb_transport_header 獲取各層頭指針,比手動偏移更安全。
  • %pI4 內核專用格式化符,直接打印 __be32 IPv4 地址。
  • scnprintf 防緩衝區溢出,返回寫入字節數。

Procfs 讀操作

int proc_read(char *buf, char **start, off_t off, int count, int *eof, void *data) {
    if (off > 0) return 0;  // 簡單實現:不支持分頁,off>0 返回 EOF
    if (count < kbuf_len) return -EINVAL;
    memcpy(buf, kbuf, kbuf_len);
    return kbuf_len;
}

模塊初始化/清理

int init_module(void) {
    kbuf = kmalloc(1024, GFP_KERNEL);
    if (!kbuf) return -ENOMEM;

    // 註冊 Netfilter Hook
    nfho.hook = hook_func;
    nfho.hooknum = NF_INET_PRE_ROUTING;
    nfho.pf = PF_INET;
    nfho.priority = NF_IP_PRI_FIRST;
    nf_register_hook(&nfho);

    // 創建 /proc/port
    proc_file = create_proc_entry("port", 0644, NULL);
    if (!proc_file) { nf_unregister_hook(&nfho); kfree(kbuf); return -ENOMEM; }
    proc_file->read_proc = proc_read;
    proc_file->owner = THIS_MODULE;

    printk(KERN_INFO "netfilter_proc_module loaded\n");
    return 0;
}

void cleanup_module(void) {
    remove_proc_entry("port", NULL);
    nf_unregister_hook(&nfho);
    kfree(kbuf);
    printk(KERN_INFO "netfilter_proc_module unloaded\n");
}

Makefile

obj-m := netfilter_proc.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

編譯、加載、測試

make
sudo insmod netfilter_proc.ko
cat /proc/port        # 查看捕獲信息
sudo rmmod netfilter_proc
dmesg | tail          # 查看內核日誌

現代內核 (5.6+) 遷移提示

  • create_proc_entry 已棄用,改用 proc_create + proc_ops (或 file_operations)
  • Netfilter Hook 簽名變更:nf_hookfn 類型參數調整,參考 include/linux/netfilter.h
  • 推薦使用 seq_file 接口實現 Procfs 讀取,支持大數據分頁。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved