天天看點

OpenWrt核心子產品開發(九)-通過linux netfilter架構實作資料包分析

文章目錄

    • 功能描述
    • 功能實作
    • 源碼
    • 輸出結果
    • 作者簡介
    • 源碼和文檔

功能描述

通過netfilter實作資料封包的解析,檢視所有資料包内容,類似于wireshark檢視資料流的功能。我們通常用的tcpdump指令,采用了libpcap庫,底層用的是AF_PACKET協定,該協定會将資料封包拷貝一份(核心到應用層零拷貝)到應用層。

但AF_PACKET會将所有資料拷貝,比較耗性能,在開發過程中我們往往隻需要檢視關鍵資訊,比如端口、ip、url等,這裡我們實作基于netfilter架構将資料封包輸出到核心日志,檢視資料互動的整個流程,當然後續可以自己加以完善,結合netlink或者共享記憶體将資料上報到應用層程序,實作資料審計。

功能實作

  • 添加一個核心子產品
  • 核心子產品注冊netfilter鈎子函數,用于攔截資料封包
  • 鈎子函數中對封包的基本資訊解析,比如ip、端口、協定等
  • 資料輸出,将data部分輸出到日志,并支援ascii顯示

源碼

#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/netfilter.h>
#include <net/netfilter/nf_conntrack.h>
#include <linux/skbuff.h>
#include <net/ip.h>
#include <linux/types.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("openwrt");
MODULE_DESCRIPTION("pkt_capture");


#define MAX_CAPTURE_DATA_LEN 1500
void dump_l4_data(u_int8_t *data, u_int32_t len){
    int i;
    int j;
    int tmp_index = 0;
    u_int8_t tmp_data[32] = {0};

    for (i = 0; i < len && i < MAX_CAPTURE_DATA_LEN; i++){
        if (i % 16 == 0){
            if (i != 0){
                for (j = 0; j < 16; j++)
                    printk(KERN_CONT "%02X ", tmp_data[j]);
                printk(KERN_CONT "%s", "    ");
                for (j = 0; j < 16; j++){
                    if (tmp_data[j] > 32 && tmp_data[j] <= 127)
                        printk(KERN_CONT "%c", tmp_data[j]);
                    else
                        printk(KERN_CONT "%c", '.');
                }
             printk(KERN_CONT "\n");

            }
            tmp_index = 0;
        }
        tmp_data[tmp_index++] = data[i];
    }
    printk(KERN_CONT"\n");
}

static u_int32_t pkt_capture_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
	struct nf_conn *ct = (struct nf_conn *)skb->_nfct;
	struct udphdr * udph = NULL;
    struct tcphdr * tcph = NULL;
	struct iphdr *iph = NULL;

	if(ct == NULL || !nf_ct_is_confirmed(ct)) {
        return NF_ACCEPT;
    }
	iph = ip_hdr(skb);
	if (!iph) 
		return NF_ACCEPT;

    u_int8_t *l4_data = NULL;
    u_int32_t l4_len = 0;
    u_int16_t sport = 0;
    u_int16_t dport = 0;
    switch (iph->protocol){
    case IPPROTO_UDP:
        udph = (struct udphdr *)(iph + 1);
        sport = htons(udph->source);
        dport = htons(udph->dest);
        break;
    case IPPROTO_TCP:
        tcph = (struct tcphdr *)(iph + 1);
        dport = htons(tcph->dest);
        sport = htons(tcph->source);
        break;
    default:
        printk("ignore proto %d\n", iph->protocol);
        return NF_ACCEPT;
    }

    printk(KERN_CONT"%-6s %pI4:%-6u-->%pI4:%-6u\n", iph->protocol == IPPROTO_UDP ? "UDP": "TCP",
        &iph->saddr, sport,  &iph->daddr, dport);

    dump_l4_data(skb->data, skb->len);
	return NF_ACCEPT;
}


static struct nf_hook_ops pkt_capture_ops[] __read_mostly = {
	{
		.hook		= pkt_capture_hook,
		.pf			= PF_INET,
		.hooknum	= NF_INET_FORWARD,
		.priority	= NF_IP_PRI_MANGLE + 1,
	},
};

static int __init pkt_capture_init(void)
{
    nf_register_net_hooks(&init_net, pkt_capture_ops, ARRAY_SIZE(pkt_capture_ops));
	return 0;
}

static void pkt_capture_exit(void)
{
    nf_unregister_net_hooks(&init_net, pkt_capture_ops, ARRAY_SIZE(pkt_capture_ops));
	return ;
}

module_init(pkt_capture_init);
module_exit(pkt_capture_exit);


           

輸出結果

OpenWrt核心子產品開發(九)-通過linux netfilter架構實作資料包分析

作者簡介

OpenWrt應用過濾插件作者(應用過濾用于控制app聯網,可以過濾遊戲、視訊、聊天等幾百款app)

從事嵌入式Linux開發近10年,主要負責路由器網通産品研發,精通OpenWrt系統,包括luci、消息機制、核心子產品等。擅長子產品:路由器上網行為管理、智能流控、上網認證、防火牆、虛拟伺服器、多wan負載均衡等

開源作品位址:

https://github.com/destan19/OpenAppFilter

源碼和文檔

關注微信公衆号可以擷取更多技術文檔、固件、源碼等

微信掃碼關注:

OpenWrt核心子產品開發(九)-通過linux netfilter架構實作資料包分析
OpenWrt核心子產品開發(九)-通過linux netfilter架構實作資料包分析
OpenWrt核心子產品開發(九)-通過linux netfilter架構實作資料包分析

繼續閱讀