random tips on coding, ops, ...

用于网络包检查的简单内核模块

非常简单的网络包检查的内核模块,基于此可以做更多有意义的事情。基于内核 3.2 测试通过。

最新代码请参考这里

pkt_chk.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/**
 * @file
 * @author Z. Liu <liuzx@knownsec.com>
 *
 * @brief kernel module for packet inspection
 */
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter_ipv4.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Z. Liu <liuzx@knownsec.com>");
MODULE_DESCRIPTION("demo of packet inspection");

static unsigned int pkt_chk_out(unsigned int hooknum, struct sk_buff *skb,
		const struct net_device *in,
		const struct net_device *out,
		int (*okfn)(struct sk_buff *))
{
	struct tcphdr *th;
	struct udphdr *uh;
	uint16_t sport = 0, dport = 0;
	struct iphdr *iph = (struct iphdr *)skb_network_header(skb);

	switch (iph->protocol) {
		case IPPROTO_UDP:
			uh = (struct udphdr *)skb_transport_header(skb);
			sport = (unsigned int)ntohs(uh->source);
			dport = (unsigned int)ntohs(uh->dest);
			break;
		case IPPROTO_TCP:
			th = (struct tcphdr *)skb_transport_header(skb);
			sport = (unsigned int)ntohs(th->source);
			dport = (unsigned int)ntohs(th->dest);
			break;
		default:
			break;
	}

	pr_info("OUT: %pI4:%u -> %pI4:%u, proto: %u\n", &iph->saddr, sport, &iph->daddr, dport, iph->protocol);

	return NF_ACCEPT;
}

static unsigned int pkt_chk_in(unsigned int hooknum, struct sk_buff *skb,
		const struct net_device *in,
		const struct net_device *out,
		int (*okfn)(struct sk_buff *))
{
	static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
	struct tcphdr *th;
	struct udphdr *uh;
	uint16_t sport = 0, dport = 0;
	struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
	const char *indev;

	indev = in ? in->name : nulldevname;

	// @note when a packet goes in from wire, it travels from physical layer,
	// data link layer, network layer upwards, therefore it might not go
	// through the functions for skb_transport_header to work as expected.
	// so we need a hack: skip the ip header.
	switch (iph->protocol) {
		case IPPROTO_UDP:
			uh = (struct udphdr *)(skb_transport_header(skb) + (iph->ihl << 2));
			sport = (unsigned int)ntohs(uh->source);
			dport = (unsigned int)ntohs(uh->dest);
			break;
		case IPPROTO_TCP:
			th = (struct tcphdr *)(skb_transport_header(skb) + (iph->ihl << 2));
			sport = (unsigned int)ntohs(th->source);
			dport = (unsigned int)ntohs(th->dest);
			break;
		default:
			break;
	}

	pr_info("IN: %pI4:%u -> %pI4:%u, proto: %u\n", &iph->saddr, sport, &iph->daddr, dport, iph->protocol);

	return NF_ACCEPT;
}

static struct nf_hook_ops packet_ops[] __read_mostly = {
	{
		.hook     = pkt_chk_in,
		.owner    = THIS_MODULE,
		.pf       = NFPROTO_IPV4,
		.hooknum  = NF_INET_PRE_ROUTING,
		.priority = NF_IP_PRI_FIRST,
	},
	{
		.hook     = pkt_chk_out,
		.owner    = THIS_MODULE,
		.pf       = NFPROTO_IPV4,
		.hooknum  = NF_INET_LOCAL_OUT,
		.priority = NF_IP_PRI_FIRST,
	},
};

static int __init pkt_chk_init(void)
{
	int ret;

	pr_info("initialize of packet inspection module\n");

	ret = nf_register_hooks(packet_ops, ARRAY_SIZE(packet_ops));
	if (ret < 0) {
		return ret;
	}

	return 0;
}

static void __exit pkt_chk_exit(void)
{
	nf_unregister_hooks(packet_ops, ARRAY_SIZE(packet_ops));

	pr_info("packet inspection module unloaded.\n");
}

module_init(pkt_chk_init);
module_exit(pkt_chk_exit);

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ifeq ($(KERNELRELEASE),)
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)

.PHONY: modules clean

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	@rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
	@rm -rf modules.order  Module.symvers

else
    obj-m := pkt_chk.o
endif

comments powered by Disqus