基于netfilter抓包

背景

本文介绍一种抓取网络数据包的方法,基本思想是通过netfilter/iptables在TCP /IP网络层过滤出网络报文,然后将数据压缩、加密后,上传至云端服务器分析。

过滤出的数据为上行的原始的TCP报文,包括MAC首部、PPP首部(如果是PPPOE连接), IP首部、TCP首部以及部分或全部用户数据。

代码基于linux3.14.10验证通过,文中提供部分代码,主要探讨其中几个模块。

一、框架概览

上网设备负责过滤出数据包并上传到云端服务器,云端服务器将收到的数据进行解析处理。

设备端需要将过滤到的数据包通过网络上传到服务器,为确保数据安全,需采用aes128对原始数据加密。

云端服务器的功能如下:

1)分析各网络设备上传的大数据,从中提炼有价值的数据,这是最主要的工作。

2)管理秘钥,不定期地通知各网络设备更新public key公钥。

3)控制网络设备系统的逻辑行为,是否开启该功能、多长时间运行一次等。

4)查询网络设备的状态,其中获取设备侧的通讯协议比较重要。设备侧软件可能随时升级,导致不同的设备上传的数据格式有差异,通过查询设备侧的通讯协议,云端服务器可以灵活兼容各种网络设备上的软件。

上网设备端的功能如下:

1)通过netfilter/iptables扩展target的方式,过滤处理网络报文。

2)数据压缩加密后上传。

3)系统性能监控。若网络数据流量很大,不停的过滤处理数据包,可能影响系统性能,所以需要控制这种影响性能的情况出现。

服务器与网络设备之间的通讯:

采用http方式。根据自己的需求,为通讯双方定义一份数据包格式,通过post、put请求交互即可。网络传输的报文需加密处理。

数据加密采用openssl库完成。

上传的文件格式为:

 字节序: high byte -----low byte
 pub key num: 云端公钥序列号
 AES128 len: 本地随机生成AES128密钥,用云端公钥对这个AES128密钥加密,加密后的密文长度定义为AES128 len
 AES128:本地随机生成的AES128密钥,用云端公钥对这个AES128密钥加密,加密后的密文内容定义为AES128
  AES128报文数据:获取的所有数据包经过本地随机生成的AES128密钥加密后的密文内容。里面时各个packet信息

二、内核态过滤处理网络数据包

linux中有很多过滤处理网络报文的方法,比如libpcap就是一个功能强大的工具,但如果自己想动手做个工具,或者开源的有一些限制无法满足你的特殊需求,那么就可以自己搞一个。

网络数据包涉及的OSI七层模型如下,我们可以在相关的层进行过滤处理。

从软件的角度考虑,可以在数据链路层或者网络层进行过滤处理,都能达到过滤处理的目的。

如果在数据链路层过滤处理,可以在网卡驱动发送数据包的接口函数hard_start_xmit中添加hook,所有的上行数据都会通过该接口发送出去。这种方法更直接,也更灵活,但是对系统的性能、稳定性影响比较大。网卡中断可能非常多,在hard_start_xmit引入额外的工作量会引起网络性能下降,甚至会占用过多的cpu影响系统整体性能,为平衡性能考虑,需要小心的设计代码逻辑,如果在这层过滤处理网络数据包,需要对代码做大负载压测,以便发现可能存在的隐患。若前期测试不充分,后期可能出现各种各样的系统问题,排查成本较大。

如果在网络层过滤处理,可借助于网络层已有的netfilter框架,扩展iptables target去过滤处理数据包。该层在内核网络代码逻辑架构上位于更上层,footprint开销比底层的数据链路层小,对系统性能的影响就少一些。另外只需在linux netfilter的框架上扩展一个iptables模块去过滤处理数据包,新增代码与内核代码没有耦合,独立性强,易于维护。相比于数据链路层实现方式,在网络层过滤处理数据包是个更好的选择。这篇文章采取了该方法。

在网络层过滤处理数据包,首先需要了解网络报文的流经路径。数据包会在netfilter框架中转一圈,在这个框架中有各种规则,若数据包符合某个规则(也叫匹配,iptables match),netfilter就会将该数据包交给该规则对应的hook(也叫iptables target)处理。netfilter中有链、表概念,在每个链中有很多规则一条一天串起来,netfilter按规则的先后顺序判断数据包是否match。不同的链中会有功能相似的规则,这些规则组成表。表、链是逻辑上对hook的划分。在linux3.14.10版本的内核代码中,netfilter实现了4表5链,主要功能描述如下:

4张表:

filter表:一般用于过滤数据包,有INPUT、FORWARD、OUTPUT三个链。
nat表:用于nat功能(端口映射,地址映射等),有PREROUTING、POSTROUTING两个链。
mangle表:用于对特定数据包的修改,有INPUT、FORWARD、OUTPUT、PREROUTING、POSTROUTING五个链。
raw表:有限级最高,设置raw时一般是为了不再让iptables做数据包的链接跟踪处理,提高性能,有OUTPUT、PREROUTING两个链。

5个链:

INPUT链——进来的数据包应用此规则链中的策略
OUTPUT链——外出的数据包应用此规则链中的策略
FORWARD链——转发数据包时应用此规则链中的策略
PREROUTING链——对数据包作路由选择前应用此链中的规则(所有的数据包进来的时侯都先由这个链处理)
POSTROUTING链——对数据包作路由选择后应用此链中的规则(所有的数据包出来的时侯都先由这个链处理)

简化的网络数据的路径如下:

假如代码是运行在路由器设备中,那么我们可以选择filter表中的FORWARD链,在此添加我们的hook,就能过滤处理出通过该路由器上网的数据报文。

加入代码时运行在手机中,那么我们可以选择filter表中的OUTPUT链,在此添加我们的hook,就能过滤处理出手机上网的数据报文。

netfilter/iptables有默认的一些iptables target,当数据包匹配规则后,可以用这些target处理。不过为过滤处理网络报文,需要自己扩展实现target,假设我们添加的过滤处理网络报文的hook称为PCAP,那么我们可以通过下面的命令来过滤处理数据报文,将符合规则的tcp报文送入PCAP中处理:

iptables -t filter -w -I FORWARD -i br-lan -o eth0.2 -p tcp -j PCAP (具体参数可以根据网络设备实际情况修改)

所以为了过滤处理数据包,我们只需要在内核中实现一个PCAP模块,然后在用户态执行上面的iptables命令即可。

三、内核态与用户态的数据交互

内核里面过滤处理到数据包以后需要发给用户态,由用户态做压缩加密等处理后上传服务器。内核态与用户态数据的交互有普通磁盘文件、proc文件、sys文件、mmap文件映射、netlink通讯、信号等方式,由于我们需要传输大量的数据,且需要双向通讯,所以用netlink通讯比较好。

网上有很多关于netlink的介绍,用起来也比较简单,这篇文章就不再介绍netlink了,直接上代码。

自定义的一些数据结构:

static struct sockaddr_nl src_addr, dst_addr;
static struct iovec iov_recv;
static int sockfd = -1;
static struct nlmsghdr *nlh_recv = NULL;
static struct msghdr msg_recv;

/* 内核空间发送给用户空间的netlink payload消息长度。
* 因为netlink消息额外有nlmsghdr消息头(16字节),
* 所以用户空间recvmsg收到的消息长度为NL_BUFFER_SIZE+sizeof(nlmsghdr)。
*/
#define NL_BUFFER_SIZE (1024*4)

/* netlink消息类型 */
#define NLMSG_U2K_CAP_UNSPEC (1 << 0)   /* 未定义 */
#define NLMSG_U2K_CAP_START (1 << 1)    /* 用户空间-->内核空间  开始过滤处理 */
#define NLMSG_U2K_CAP_STOP (1 << 2) /* 用户空间-->内核空间  停止过滤处理 */
#define NLMSG_K2U_BUFFER_FULL (1 << 3)  /* 内核空间-->用户空间  缓冲区满 */
#define NLMSG_U2K_DEBUG_ON (1 << 4) /* 用户空间-->内核空间  打开测试功能 */
#define NLMSG_U2K_DEBUG_OFF (1 << 5)    /* 用户空间-->内核空间  关闭测试功能 */

/* 内核与用户空间netlink通信用到的一些数据结构 */
static struct sockaddr_nl src_addr, dst_addr;
static struct iovec iov_recv;
static int sockfd = -1;
static struct nlmsghdr *nlh_recv = NULL;
static struct msghdr msg_recv;

用户态创建netlink套接字:

/* 初始化netlink,用户空间与内核双向通信
 * 1)用户空间-->内核空间  设置过滤处理开关
 * 2)内核空间-->用户空间  发送过滤处理到的数据数据报文给用户空间
 *
 * 这个函数的资源需要通过release_netlink释放
 */
static int create_netlink(void)
{
    /* 源地址 */
    sockfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CAPTURE_PACKET);
    if (-1 == sockfd) {
        LOG_MSG_E("%s %s %d can't create netlink socket!\n", __FILE__,
              __func__, __LINE__);
        return -1;
    }
    bzero(&src_addr, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid();
    src_addr.nl_groups = 0;
    bind(sockfd, (struct sockaddr *)&src_addr, sizeof(src_addr));

    /* 目的地址 */
    bzero(&dst_addr, sizeof(dst_addr));
    dst_addr.nl_family = AF_NETLINK;
    dst_addr.nl_pid = 0;
    dst_addr.nl_groups = 0;

    /* 构造接收消息缓冲区 */
    nlh_recv = malloc(NL_BUFFER_SIZE);
    if (!nlh_recv) {
        syslog(LOG_ERR, "%s %s %d can't create netlink socket!\n",
               __FILE__, __func__, __LINE__);
        close(sockfd);
        sockfd = -1;
        return -1;
    }
    nlh_recv->nlmsg_len = NL_BUFFER_SIZE;
    nlh_recv->nlmsg_pid = getpid();
    nlh_recv->nlmsg_flags = 0;
    nlh_recv->nlmsg_type = NLMSG_U2K_CAP_UNSPEC;
    iov_recv.iov_base = (void *)nlh_recv;
    iov_recv.iov_len = nlh_recv->nlmsg_len;
    msg_recv.msg_name = (void *)&dst_addr;
    msg_recv.msg_namelen = sizeof(dst_addr);
    msg_recv.msg_iov = &iov_recv;
    msg_recv.msg_iovlen = 1;

    return 0;
}

static inline void release_netlink(void)
{
    if (-1 != sockfd) {
        close(sockfd);
        sockfd = -1;
    }
    if (nlh_recv) {
        free(nlh_recv);
        nlh_recv = NULL;
    }
}

用户态发送数据给内核态:

/* 发送消息到内核空间
 * state取值:
 * NLMSG_U2K_CAP_START      开始过滤处理
 * NLMSG_U2K_CAP_STOP       停止过滤处理
 * NLMSG_K2U_BUFFER_FULL    缓冲区满
 * NLMSG_U2K_DEBUG_ON       打开内核测试功能
 * NLMSG_U2K_DEBUG_OFF      关闭内核测试功能
 */
int send_msg_to_kernel(int state)
{
    struct nlmsghdr *nlh_send = NULL;
    struct iovec iov_send;
    struct msghdr msg_send;

    memset(&iov_send, 0, sizeof(struct iovec));
    memset(&msg_send, 0, sizeof(struct msghdr));

    /* 构造发送消息缓冲区 */
    nlh_send = malloc(NLMSG_SPACE(0));
    if (!nlh_send) {
        syslog(LOG_ERR, "%s %s %d can't get mem fail!\n", __FILE__,
               __func__, __LINE__);
        return -1;
    }
    nlh_send->nlmsg_len = NLMSG_SPACE(0);
    nlh_send->nlmsg_pid = getpid();
    nlh_send->nlmsg_flags = 0;
    iov_send.iov_base = (void *)nlh_send;
    iov_send.iov_len = nlh_send->nlmsg_len;
    msg_send.msg_name = (void *)&dst_addr;
    msg_send.msg_namelen = sizeof(dst_addr);
    msg_send.msg_iov = &iov_send;
    msg_send.msg_iovlen = 1;

    nlh_send->nlmsg_type = state;
    sendmsg(sockfd, &msg_send, 0);
    free(nlh_send);
    LOG_MSG_D("send_msg_to_kernel send msg:%d\n", state);

    return 0;
}

内核态初始化netlink:

struct sock *sock;

struct netlink_kernel_cfg nlcfg = {
    .input = nl_custom_data_ready,
};

/* 新增一个netlink协议类型,协议类型最多32个。也可以复用内核已有的通用协议 NETLINK_GENERIC */
#define NETLINK_CAPTURE_PACKET 30    /* capture packet */

sock = netlink_kernel_create(&init_net, NETLINK_CAPTURE_PACKET, &nlcfg);
if (!sock) {
    err = -1;
    printk("pcap create netlink fail\n");
}

五、附上内核空间的源码(iptables target、netlink通讯)


#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/netlink.h>
#include <net/net_namespace.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/netfilter/x_tables.h>
#include <linux/ip.h>

MODULE_DESCRIPTION("pcap");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("geshifei@126.com");

#define dprintk(fmt, args...) do {\
    if (debug_mode) {\
        printk(fmt, ## args);\
    }\
} while(0)

/* netlink消息类型 */
#define NLMSG_U2K_CAP_UNSPEC (1 << 0)   /* 未定义 */
#define NLMSG_U2K_CAP_START (1 << 1)    /* 用户空间-->内核空间  开始过滤处理*/
#define NLMSG_U2K_CAP_STOP (1 << 2) /* 用户空间-->内核空间  停止过滤处理 */
#define NLMSG_K2U_BUFFER_FULL (1 << 3)  /* 内核空间-->用户空间  缓冲区满 */
#define NLMSG_U2K_CAP_DEBUG_ON (1 << 4) /* 内核空间-->用户空间  打开调试模式 */
#define NLMSG_U2K_CAP_DEBUG_OFF (1 << 5)    /* 内核空间-->用户空间  关闭调试模式 */

/* netlink消息长度,3k。
 * nlmsg_new还会加上nlmsghdr、skb_shared_info等额外信息,定义3k目的是为了nlmsg_new只申请一个page */
#define NL_BUFFER_SIZE (1024*3)
/* pcap_buffer最前面的2个字节用于表示pcap_buffer中实际的数据长度,不包括前面保留的2个字节 */
#define PCAP_BUFFER_HEAD_RESERVE 2
/* 每个抓到的包的第三个字节开始为6个字节的设备MAC*/
#define PCAP_MAC_HEAD_RESERVE 6
/* 这个宏表示单个tcp数据包存储的最长长度 ,超出部分忽略。pcap_buffer尾部少于1024字节时,就认为满了 。*/
#define PCAP_BUFFER_TAIL_RESERVE 1024
/* 尾部没有空间存放(PCAP_BUFFER_TAIL_RESERVE+2+2+6)个字节数据 */
#define NL_TX_SIZE (NL_BUFFER_SIZE - 4 - PCAP_BUFFER_TAIL_RESERVE - PCAP_MAC_HEAD_RESERVE)

static bool debug_mode = false;
static char *pcap_buffer = NULL;
static char *pcap_curr_ptr = NULL;

/* pcap_buffer中已经写入的数据包字节数 */
static __u32 pcap_buffer_len = 0;
static bool pcap_buffer_full = true;

static struct sk_buff *out_skb = NULL;

static struct task_struct *pcap_kthread = NULL;
static __u32 user_nlmsg_pid = 0;

static bool pcap_mod_exit = false;

/* iptables target 用这个完成量通知pcap_thread_fn线程缓冲区满了 */
static DECLARE_COMPLETION(pcap_buffer_completion);
static DEFINE_SPINLOCK(pcap_buffer_lock);

static struct sock *sock;
/*
 * 测试线程,用来模拟iptables的target,往pcap_buffer写数据 。
 * 周期1分钟写满缓冲区
 */
#ifdef CAPTURE_PACKET_DEBUG_THREAD
static struct task_struct *test_kthread = NULL;

static int pcap_test_thread_fn(void *dummy)
{
    int i = 0;
    unsigned long flags;

    while (!kthread_should_stop()) {
        if (pcap_buffer_full) {
            ssleep(1);
            continue;
        }
        spin_lock_irqsave(&pcap_buffer_lock, flags);
        /* must check again */
        if (pcap_buffer_full) {
            spin_unlock_irqrestore(&pcap_buffer_lock, flags);
            continue;
        }

        if (pcap_buffer) {
            for (i = PCAP_BUFFER_HEAD_RESERVE;
                 i < NL_BUFFER_SIZE - 1; i++) {
                pcap_buffer[i] = 'a' + i % 26;
            }
            pcap_buffer[i] = '\0';
            pcap_buffer[0] =
                (unsigned
                 char)((NL_BUFFER_SIZE -
                    PCAP_BUFFER_HEAD_RESERVE) >> 8);
            pcap_buffer[1] =
                (unsigned char)(NL_BUFFER_SIZE -
                        PCAP_BUFFER_HEAD_RESERVE);
            pcap_buffer_full = true;
            printk("pcap_test_thread_fn buffer is full\n");
            complete(&pcap_buffer_completion);
        }
        spin_unlock_irqrestore(&pcap_buffer_lock, flags);
        ssleep(60);
    }
    return 0;
}

static int debug_create_thread(void)
{
    test_kthread =
        kthread_run(pcap_test_thread_fn, NULL, "pcap_test_thread");
    if (IS_ERR(test_kthread)) {
        //printk(KERN_ERR "%s %d create thread error %ld!\n", __FILE__, __LINE__, PTR_ERR(test_kthread));
        return PTR_ERR(test_kthread);
    }
    return 0;
}
#endif

/* iptables target
 * 过滤出的数据包写入缓冲区pcap_buffer中。
 * 缓冲区写满后通知pcap_thread_fn线程把数据发送到用户空间处理。
 * 此时target不会再去写pcap_buffer,只有当用户程序处理完收到的数
 * 据并发送NLMSG_U2K_CAP_PACKET通知内核后,target才会继续用采集
 * 到的数据包填充pcap_buffer。
 *
 * pcap_buffer缓冲区数据包格式如下:
 * [总长度高字节 总长度低字节][长度高字节 长度低字节 MAC tcp数据包1][长度高字节 长度低字节 MAC tcp数据包2]
 * 总长度不包括两个字节的“长度”本身
 * 各个数据包内的长度不包括两个字节的"长度"本身和6个字节的设备MAC
 */
static unsigned int
capture_packet_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
    int ret = -1;
    unsigned int data_len = 0;
	unsigned char s_mac[6];
    struct iphdr *iph;  /* debug */
	struct ethhdr *eth;

    if (pcap_buffer_full) {
        return XT_CONTINUE;
    }

    iph = ip_hdr(skb);
	eth = eth_hdr(skb);
	memcpy(s_mac, eth->h_source, 6);
    dprintk(" pcap: %x ----> %x, len=%d\n", iph->saddr,
        iph->daddr, skb->len);
	dprintk("source_mac:%02x:%02x:%02x:%02x:%02x:%02x\n", s_mac[0],s_mac[1],s_mac[2],s_mac[3],s_mac[4],s_mac[5]);

    /* 在eth层判断data_len < 82 ,在ip层减去以太网头长度 */
    data_len = skb->len;
    if (data_len <= 68) {
        return XT_CONTINUE;
    }

    if (data_len > PCAP_BUFFER_TAIL_RESERVE) {
        data_len = PCAP_BUFFER_TAIL_RESERVE;
    }

    ret = spin_trylock(&pcap_buffer_lock);
    if (!ret) {
        return XT_CONTINUE;
    }

    /* 必须再次检查 */
    if (pcap_buffer_full) {
        goto unlock;
    }

    if (pcap_buffer && pcap_curr_ptr) {
        /* 写缓冲区 */
        *(pcap_curr_ptr++) = (unsigned char)(data_len >> 8);
        *(pcap_curr_ptr++) = (unsigned char)(data_len);
        *(pcap_curr_ptr++) = (unsigned char)(s_mac[0]);
        *(pcap_curr_ptr++) = (unsigned char)(s_mac[1]);
        *(pcap_curr_ptr++) = (unsigned char)(s_mac[2]);
        *(pcap_curr_ptr++) = (unsigned char)(s_mac[3]);
        *(pcap_curr_ptr++) = (unsigned char)(s_mac[4]);
        *(pcap_curr_ptr++) = (unsigned char)(s_mac[5]);
        memcpy(pcap_curr_ptr, skb->data, data_len);
        pcap_curr_ptr += data_len;
        pcap_buffer_len += (data_len + PCAP_BUFFER_HEAD_RESERVE + PCAP_MAC_HEAD_RESERVE);
        dprintk("pcap:record data_len:%d\n",
            data_len);

        if (pcap_buffer_len > NL_TX_SIZE) {
            pcap_buffer_full = true;
            pcap_buffer[0] =
                (unsigned char)(pcap_buffer_len >> 8);
            pcap_buffer[1] =
                (unsigned char)(pcap_buffer_len);
            dprintk(" pcap: buf is full, len=%d\n",
                pcap_buffer_len);
            complete(&pcap_buffer_completion);
        }
    }
unlock:
    spin_unlock(&pcap_buffer_lock);

    return XT_CONTINUE;

}

/* 负责用户空间、内核空间通信的线程。
 * 这个线程大多数时候都是睡眠状态,只有当target写满pcap_buffer后才会被唤醒。
 *
 * 这个线程还负责接收用户空间线程退出的信息,以后扩展用。
 */
static int pcap_thread_fn(void *dummy)
{
    struct nlmsghdr *nlh;

    while (1) {
        /* payload 大小NL_BUFFER_SIZE,用户程序收到的数据大小还需要加上sizeof(nlmsghdr) */
        out_skb = nlmsg_new(NL_BUFFER_SIZE, GFP_KERNEL);
        if (!out_skb) {
            ssleep(60 * 5); /* sleep 5 min */
            dprintk(KERN_ERR "%s %s %d err\n", __FILE__, __func__,
                __LINE__);
            continue;
        }
        nlh =
            nlmsg_put(out_skb, 0, 0, NLMSG_K2U_BUFFER_FULL,
                  NL_BUFFER_SIZE, 0);
        if (!nlh) {
            dprintk(KERN_ERR "%s %s %d err\n", __FILE__, __func__,
                __LINE__);
            nlmsg_free(out_skb);
            ssleep(60 * 5);
            continue;
        }

        pcap_buffer = nlmsg_data(nlh);
        /* 保留最前面两个字节单元, [总长度高字节 总长度低字节][长度高字节 长度低字节 MAC tcp数据包1][长度高字节 长度低字节 MAC tcp数据包2] */
        pcap_curr_ptr = pcap_buffer + PCAP_BUFFER_HEAD_RESERVE;

        dprintk("pcap_thread_fn wait completion\n");
        wait_for_completion(&pcap_buffer_completion);
        dprintk("pcap_thread_fn after completion\n");

        while (pcap_mod_exit) {
            /* 这个时候回收内存时安全的,因为capture_packet_exit中已经把pcap_buffer_full置为true了 */
            if (out_skb) {
                nlmsg_free(out_skb);
                out_skb = NULL;
            }

            if (kthread_should_stop()) {
                return 0;
            }
            ssleep(1);
        }

        if (out_skb) {
            /* 必须注意,nlmsg_unicast内部会释放out_skb,所以不能再次操作out_skb */
            dprintk("nlmsg_unicast to user\n");
            nlmsg_unicast(sock, out_skb, user_nlmsg_pid);
            out_skb = NULL;
            pcap_buffer = NULL;
            pcap_curr_ptr = NULL;
            pcap_buffer_len = 0;
        }

    }
    return 0;
}

static void nl_custom_data_ready(struct sk_buff *skb)
{
    struct nlmsghdr *nlh;

    nlh = nlmsg_hdr(skb);
    switch (nlh->nlmsg_type) {
    case NLMSG_U2K_CAP_START:
        dprintk
            ("nl_custom_data_ready receive start msg, nlh->nlmsg_pid:%d\n",
             nlh->nlmsg_pid);
        spin_lock(&pcap_buffer_lock);
        user_nlmsg_pid = nlh->nlmsg_pid;
        pcap_buffer_full = false;
        pcap_buffer_len = 0;
        spin_unlock(&pcap_buffer_lock);
        break;

    case NLMSG_U2K_CAP_STOP:
        dprintk("nl_custom_data_ready receive stop msg\n");
        /* 禁止iptables写数据 */
        spin_lock(&pcap_buffer_lock);
        pcap_buffer_full = true;
        spin_unlock(&pcap_buffer_lock);
        break;

    case NLMSG_U2K_CAP_DEBUG_ON:
        printk("nl_custom_data_ready debug mode enable\n");
        debug_mode = true;
        break;

    case NLMSG_U2K_CAP_DEBUG_OFF:
        dprintk("nl_custom_data_ready debug mode disable\n");
        debug_mode = false;
        break;

    default:
        dprintk(KERN_INFO "%s %s %d unknow msg type:%u recieved!\n",
            __FILE__, __func__, __LINE__, nlh->nlmsg_type);
    }
    return;
}

static void nl_custom_data_ready(struct sk_buff *skb);

static struct xt_target capture_packet_tg_reg __read_mostly = {
    .name = "pcap",
    .revision = 0,
    .family = NFPROTO_UNSPEC,
    .target = capture_packet_tg,
    .me = THIS_MODULE,
};

/* 初始化 */
static int __init capture_packet_init(void)
{
    int err = 0;

    struct netlink_kernel_cfg nlcfg = {
        .input = nl_custom_data_ready,
    };

    err = xt_register_target(&capture_packet_tg_reg);
    if (err) {
        printk("pcap register target fail\n");
        return err;
    }

    sock = netlink_kernel_create(&init_net, NETLINK_CAPTURE_PACKET, &nlcfg);
    if (!sock) {
        err = -1;
        printk("pcap create netlink fail\n");
        goto unregister_target;
    }

    pcap_kthread = kthread_run(pcap_thread_fn, NULL, "pcap_thread");
    if (IS_ERR(pcap_kthread)) {
        err = PTR_ERR(pcap_kthread);
        printk("pcap create pcap thread fail\n");
        goto fail_free_netlink;
    }
#ifdef CAPTURE_PACKET_DEBUG_THREAD
    err = debug_create_thread();
    if (err) {
        printk("pcap create test thread fail\n");
        goto fail_stop_thread;

    }
#endif
    printk(KERN_INFO "pcap ok\n");
    return 0;

#ifdef CAPTURE_PACKET_DEBUG_THREAD
fail_stop_thread:
    spin_lock(&pcap_buffer_lock);
    pcap_buffer_full = true;
    spin_unlock(&pcap_buffer_lock);
    kthread_stop(test_kthread);
    pcap_mod_exit = true;
    complete(&pcap_buffer_completion);
#endif

fail_free_netlink:
    netlink_kernel_release(sock);
unregister_target:
    xt_unregister_target(&capture_packet_tg_reg);
    printk("pcap init fail, err:%d\n", err);
    return err;
}

void __exit capture_packet_exit(void)
{
    xt_unregister_target(&capture_packet_tg_reg);
    netlink_kernel_release(sock);

    /* 必须确保iptables target不会去操作 pcap_buffer了,因为线程pcap_kthread
     * 退出,会回收pcap_buffer内存。
     */
    spin_lock(&pcap_buffer_lock);
    pcap_buffer_full = true;
    spin_unlock(&pcap_buffer_lock);
#ifdef CAPTURE_PACKET_DEBUG_THREAD
    kthread_stop(test_kthread);
#endif
    pcap_mod_exit = true;
    complete(&pcap_buffer_completion);

    kthread_stop(pcap_kthread);

    printk(KERN_INFO "pcap ok\n");
}

module_init(capture_packet_init);
module_exit(capture_packet_exit);

openssl加密代码、网络设备与云端交互、压缩等,都是现成的技术,网上资料很多。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值