上篇我们看了netfilter的实现机制,这篇来实现下netfilter模块实操一把。
为了注册一个钩子,需要填充nf_hook_ops结构体,包括优先级,钩子地点和钩子函数。然后调用nf_register_hook()函数。
struct nf_hook_ops {
/* User fills in from here down. */
nf_hookfn *hook;
struct net_device *dev;
void *priv;
u_int8_t pf;
unsigned int hooknum;
/* Hooks are ordered in ascending priority. */
int priority;
};
定义在include/linux/netfilter.h
本DEMO实现将所有输入的UDP包丢弃。钩子函数为hook_func,通过ip头的协议来指定,协议17就是表示此包是UDP包,如果将17改成1就会丢弃icmp包了。
.hooknum = 1表示是入口点过滤 NF_IP_LOCAL_IN ,如果改成3就是NF_IP_LOCAL_OUT了,这个定在内核文件include/uapi/linux/netfilter_ipv4.h,如下:
/* IP Hooks */
/* After promisc drops, checksum checks. */
#define NF_IP_PRE_ROUTING 0
/* If the packet is destined for this box. */
#define NF_IP_LOCAL_IN 1
/* If the packet is destined for another interface. */
#define NF_IP_FORWARD 2
/* Packets coming from a local process. */
#define NF_IP_LOCAL_OUT 3
/* Packets about to hit the wire. */
#define NF_IP_POST_ROUTING 4
#define NF_IP_NUMHOOKS 5
编写源码netmod.c如下:
#include
#include
#include
#include
#include
#include
/* This function to be called by hook. */
static 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 udphdr *udp_header;
struct iphdr *ip_header = (struct iphdr *) skb_network_header (skb);
if (ip_header->protocol == 17)
{
udp_header = (struct udphdr *) skb_transport_header (skb);
printk (KERN_INFO "Drop udp packet.\n");
return NF_DROP;
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho = {
.hook = hook_func,
.hooknum = 1, /* NF_IP_LOCAL_IN */
.pf = PF_INET,
.priority = NF_IP_PRI_FIRST,
};
static int __init
init_nf (void)
{
printk (KERN_INFO "Register netfilter module.\n");
nf_register_hook (&nfho);
return 0;
}
static void __exit
exit_nf (void)
{
printk (KERN_INFO "Unregister netfilter module.\n");
nf_unregister_hook (&nfho);
}
module_init (init_nf);
module_exit (exit_nf);
MODULE_LICENSE ("GPL");
编写Makefile如下:
obj-m := netmod.o
modules-objs:= netmod.o
KDIR := /lib/modules/`uname -r`/build
PWD := $(shell pwd)
default:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions
编译后即可使用insmod将模块插入到系统中。
测试完毕后,记得卸载模块,不然后来的同学可能需要去定位网络问题了。
关于模块参数的导入,使用module_param。
module_param的定义可以在include/linux/moduleparam.h文件里面查看到,它的原型为:
module_param(name, type, perm);
module_param_array(name, type, nump, perm);
其中module_param是用来传递变量参数的,module_param_array是用来传递数组参数的。
name是在模块中定义的变量名称,type是变量的类型,perm是权限掩码。
权限在include/linux/stat.h中有定义
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
另外nump是传入数组的数目,是一个int指针。参数可以是以下类型:
byte、short、ushort、int、uint、long、ulong、char(字符指针)、bool、invbool(布尔的反,invbool 类型颠倒了值, 所以真值变成 false,)
可以在导入的时候修改模块参数值:
insmod netmod.ko name=hello
源码如下:
#include
#include
#include
#include
#include
#include
#include
#include
/* This function to be called by hook. */
MODULE_LICENSE("Dual BSD/GPL");
static char* n_ip = "192.168.1.63"; //ip=192.168.1.61
module_param(n_ip, charp, S_IRUGO);
static 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 tcphdr *tcph = tcp_hdr (skb);
struct iphdr *iph = ip_hdr (skb);
struct tcphdr *modtcph;
unsigned char *tail;
unsigned char *user_data;
unsigned char *it;
struct sk_buff *modskb;
char *tempPay;
char *payload; //Char array to store original payload before modifications
int lenOrig;
int lenNew;
u16 sport, dport;
u32 saddr, daddr;
int i1,i2,i3,i4;
//tempPay = kmalloc (1500, GFP_KERNEL);
//payload = kmalloc (1500, GFP_KERNEL);
if (!skb)
return NF_ACCEPT;
saddr = ntohl (iph->saddr);
daddr = ntohl (iph->daddr);
sport = ntohs (tcph->source);
dport = ntohs (tcph->dest);
tail = skb_tail_pointer (skb);
user_data = (unsigned char *) ((unsigned char *) tcph + (tcph->doff * 4));
//3232235837 = 192.168.1.61
if (iph->saddr == in_aton(n_ip) ) //判断ip地址
{
i1 = saddr>>24;
i2 = (saddr>>16) & 0x000000ff;
i3 = (saddr>>8) & 0x000000ff;
i4 = saddr & 0x000000ff;
printk ("saddr == %d.%d.%d.%d\n ",i1,i2,i3,i4);
//printk ("saddr == %s\n",iph->saddr);
ip_send_check (iph);
for (it=user_data;it!=tail;it++)
{
*it++;
printk("%x",*it);
}
printk ("\n");
}
return NF_ACCEPT;
}
static struct nf_hook_ops nfho = {
.hook = hook_func,
.hooknum = 3, /* NF_IP_LOCAL_IN */
.pf = PF_INET,
.priority = NF_IP_PRI_FIRST,
};
static int __init
init_nf (void)
{
printk (KERN_INFO "Register netfilter module.\n");
nf_register_hook (&nfho);
printk (" n_ip:%s\n",n_ip);
return 0;
}
static void __exit
exit_nf (void)
{
printk (KERN_INFO "Unregister netfilter module.\n");
nf_unregister_hook (&nfho);
}
module_init (init_nf);
module_exit (exit_nf);
MODULE_LICENSE ("GPL");
Writing a Module For netfilter