深入Linux网络核心堆栈之三
----[ 4.3 - 基于地址进行过滤
与根据数据包的接口进行过滤类似,基于数据包的源或目的IP地址进行过滤同样简单。这次我们感兴趣的是sk_buff数据结构。还记得skb参数是一个指向sk_buff数据结构的指针的指针吗?为了避免犯错误,声明一个另外的指向skb_buff数据结构的指针并且将skb指针指向的指针赋值给这个新的指针是一个好习惯,就像这样:
struct sk_buff *sb = *skb; /* Remove 1 level of indirection* /
这样,你访问这个数据结构的元素时只需要反引用一次就可以了。获取一个数据包的IP头通过使用sk_buff数据结构中的网络层包头来完成。这个头位于一个联合中,可以通过sk_buff->nh.iph这样的方式来访问。示例代码3中的函数演示了当得到一个数据包的sk_buff数据结构时,如何利用它来检查收到的数据包的源IP地址与被禁止的地址是否相同。这些代码是直接从LWFW中取出来的,唯一不同的是LWFW统计的更新被移除。
示例代码3 : 检查收到的数据包的源IP
unsigned char *deny_ip = "\x7f\x00\x00\x01"; /* 127.0.0.1 */
...
static int check_ip_packet(struct sk_buff *skb)
{
/* We don''t want any NULL pointers in the chain to
* the IP header. */
if (!skb )return NF_ACCEPT;
if (!(skb->nh.iph)) return NF_ACCEPT;
if (skb->nh.iph->saddr == *(unsigned int *)deny_ip) {
return NF_DROP;
}
return NF_ACCEPT;
}
这样,如果数据包的源地址与我们设定的丢弃数据包的地址匹配,那么该数据包将被丢弃。为了使这个函数能按预期的方式工作,deny_ip的值应当以网络字节序(Big-endian,与Intel相反)存放。虽然这个函数不太可能以一个空的指针作为参数来调用,带一点点偏执狂从来不会有什么坏处。当然,如果错误确实发生了,那么该函数将会返回NF_ACCEPT。这样Netfilter可以继续处理这个数据包。示例代码4展现了用于演示将基于接口的过滤略做修改以丢弃匹配给定IP地址的数据包的简单模块。
示例代码4 : 基于数据包源地址的过滤
/* 安装丢弃所有来自指定IP地址的数据包的Netfilter hook的示例代码 */
#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h> /* For IP header */
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* 用于注册我们的函数的数据结构 */
static struct nf_hook_ops nfho;
/* 我们要丢弃的数据包来自的地址,网络字节序 */
static unsigned char *drop_ip = "\x7f\x00\x00\x01";
/* 注册的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;
// 译注:作者提供的代码中比较地址是否相同的方法是错误的,见注释掉的部分
if (sb->nh.iph->saddr == *(unsigned int *)drop_ip) {
// if (sb->nh.iph->saddr == drop_ip) {
printk("Dropped packet from... %d.%d.%d.%d\n",
*drop_ip, *(drop_ip + 1),
*(drop_ip + 2), *(drop_ip + 3));
return NF_DROP;
} else {
return NF_ACCEPT;
}
}
/* 初始化程序 */
int init_module()
{
/* 填充我们的hook数据结构 */
nfho.hook = hook_func; /* 处理函数 */
nfho.hooknum = NF_IP_PRE_ROUTING; /* 使用IPv4的第一个hook */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* 让我们的函数首先执行 */
nf_register_hook(&nfho);
return 0;
}
/* 清除程序 */
void cleanup_module()
{
nf_unregister_hook(&nfho);
}