本文的主要内容包括
Netfilter
是Linux内核中的一个框架,它提供一个标准的接口,通过该接口能够方便的进行不同的网络操作,包括包过滤、网络地址转换和端口转换。Netfilter在内核中提供一组钩子hooks,通过这些hooks,内核模块可以向TCP/IP协议栈注册回调函数。
Netfilter is a framework inside the Linux kernel which offers flexibility for various networking-related operations to be implemented in form of customized handlers. Netfilter offers various options for packet filtering, network address translation, and port translation. Netfilter represents a set of hooks inside the Linux kernel, thus it allows specific kernel modules to register callback functions with the kernel's network stack.
Iptables
是一个配置IPv4包过滤和NAT的管理工具。
iptables is a user-space application program that allows a system administrator to configure the tables provided by the Linux kernel firewall (implemented as different Netfilter modules) and the chains and rules it stores. Different kernel modules and programs are currently used for different protocols; iptables applies to IPv4, ip6tables to IPv6, arptables to ARP, and ebtables to Ethernet frames. There are five predefined chains (mapping to the five available Netfilter hooks), though a table may not have all chains. Predefined chains have a policy, for example DROP, which is applied to the packet if it reaches the end of the chain.
iptables包含4个表,5个链。其中表是按照对数据包的操作区分的,链是按照不同的Hook点来区分的,表和链实际上是netfilter的两个维度。
4个表:
默认表是filter(没有指定表的时候就是filter表)。表的处理优先级:
raw > mangle > nat > filter
5个链:
关于数据包的处理过程,表和链的遍历,请参考Traversing of tables and chains
Netfilter框架为多种协议提供了一套钩子(hooks),用一个
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS]
二维数组结构存储,一维为协议族,二维为hook点
netfilter提供5个hook点:
通过这些hook点,注册回调函数,可以进行包过滤, NAT, 以及细粒度的安全监控。
编写规范(5个参数1个返回值)
unsigned int my_hook(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
printk("Hello packet! ");
return NF_ACCEPT;
}
每个注册的hook函数经过处理后都将返回下列值之一,告知Netfilter核心代码处理结果,以便对报文采取相应的动作:
写完hook函数,就可以调用nf_register_hook()向netfilter进行注册挂接。
关于注册函数nf_register_hook()的源码分析,参考nf_hook_ops 钩子的注册
在调用nf_register_hook()之前,需要填写一个hook options结构
struct nf_hook_ops {
struct list_head list; //挂接到nf_hooks上
/* User fills in from here down. */
nf_hookfn *hook; //hook函数
struct module *owner;
void *priv;
u_int8_t pf; //协议族
unsigned int hooknum; //hook点
/* Hooks are ordered in ascending priority. */
int priority; //优先级
};
注释中说了,用户从第二项开始填写,list在注册时,由内核管理。
协议族pf
hook点
hooknum用于指定挂接的位置(即上面提到的5个hook点) 这5个宏定义在 linux/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
优先级
优先级是有符号的32位数,值越小优先级越高,netfilter预定义了
NF_IP_PRI_FIRST = INT_MIN;
NF_IP_PRI_CONNTRACK = -200;
NF_IP_PRI_MANGLE = -150;
NF_IP_PRI_NAT_DST = -100;
NF_IP_PRI_FILTER = 0;
NF_IP_PRI_NAT_SRC = 100;
NF_IP_PRI_LAST = INT_MAX;
分别在NF_IP_LOCAL_IN和NF_IP_LOCAL_OUT这两个hook点注册一个函数,该函数取得报文的IP报头(如果它是IP包的话),输出源IP和目的IP信息。
#include module.h> / included for all kernel modules
#include kernel.h> / included for KERN_INFO
#include init.h> / included for __init and __exit macros
#include netfilter.h>
#include <linux netfilter_ipv4.h>
#include netdevice.h>
#include <linux vmalloc.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("geeksword");
MODULE_DESCRIPTION("A Simple Hello Packet Module");
enum { NF_IP_PRE_ROUTING,
NF_IP_LOCAL_IN,
NF_IP_FORWARD,
NF_IP_LOCAL_OUT,
NF_IP_POST_ROUTING,
NF_IP_NUMHOOKS };
static struct nf_hook_ops in_nfho; //net filter hook option struct
static struct nf_hook_ops out_nfho; //net filter hook option struct
static void dump_addr(unsigned char *iphdr)
{
int i;
for(i=0; i<4; i++){
printk("%d.", *(iphdr+12+i));
}
printk(" -> ");
for(i=0; i<4; i++){
printk("%d.", *(iphdr+16+i));
}
printk("\n");
}
unsigned int my_hook(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
printk("Hello packet! ");
//printk("from %s to %s\n", in->name, out->name);
unsigned char *iphdr = skb_network_header(skb);
if(iphdr){
dump_addr(iphdr);
}
return NF_ACCEPT;
}
static int init_filter_if(void)
{
//NF_IP_PRE_ROUTING hook
in_nfho.hook = my_hook;
in_nfho.hooknum = NF_IP_LOCAL_IN;
in_nfho.pf = PF_INET;
in_nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(∈_nfho);
//NF_IP_LOCAL_OUT hook
out_nfho.hook = my_hook;
out_nfho.hooknum = NF_IP_LOCAL_OUT;
out_nfho.pf = PF_INET;
out_nfho.priority = NF_IP_PRI_FIRST;
nf_register_hook(&out;_nfho);
return 0;
}
static int hello_init(void)
{
printk(KERN_INFO "[+] Register Hello_Packet module!\n");
init_filter_if();
return 0; // Non-zero return means that the module couldn't be loaded.
}
static void hello_exit(void)
{
nf_unregister_hook(∈_nfho);
nf_unregister_hook(&out;_nfho);
printk(KERN_INFO "Cleaning up Helllo_Packet module.\n");
}
module_init(hello_init);
module_exit(hello_exit);