Iptables 与netfilter的分析(源于LINUX-2.6.34)
Msn: mewmaker@163.com
一 Netfilter 提供了一个基于HOOK的框架,基于优先级的。
下面是每个HOOK的描述
struct nf_hook_ops {
struct list_head list;
/* User fills in from here down. */
nf_hookfn *hook; /*处理函数*/
struct module *owner;
u_int8_t pf;
unsigned int hooknum; /*hook的在协议栈中的调用位置*/
/* Hooks are ordered in ascending priority. */
int priority; /*同一调用位置的优先级*/
};
下面这个是HOOK在协议栈中的调用位置,大致是根据路由前后来制定的。
enum nf_inet_hooks {
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
};
同一个切入点比如NF_INET_PRE_ROUTING,提供了优先级,可以看到在同一个协议栈切入点,不同的表的优先级顺序。
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
二 HOOK主要使用对象是 IPTABLES, CONNTRACK
从枚举类型enum nf_ip_hook_priorities 可以看出这一点。
三 iptables向netfilter HOOK的注册
同一切入点,不同的表因为不同的优先级,所以区别开来。
同一个表,要向netfilter hook 注册多次,最多的可能下面几个地方都会注册。
NF_INET_PRE_ROUTING,
NF_INET_LOCAL_IN,
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
NF_INET_NUMHOOKS
这些点在处理上分别对应各个表的预定义链,这个解释会通过
unsigned int
ipt_do_table(struct sk_buff *skb,
unsigned int hook, /* enum nf_inet_hooks ,如值为NF_INET_PRE_ROUTING,
*/
const struct net_device *in,
const struct net_device *out,
struct xt_table *table)
来
四 iptables的组织结构
Iptables 由链组成,链内由零或多条规则组成。链分为:预定义链和自定义链。
预定义链
是netfilter HOOK调用后的切入点。预定义链中的规则可以跳到自定义链继续遍历规则,直到匹配。
自定义链
也可以再跳转到其他自定义链,但自定义链里面的规则不能跳转到预定义链,但是可以返回调用链。
不同表之间不能直接跳转,不同表之间可以自定义同名的链。
表这个概念存在的意义
规则,规则链足以描述策略
hook调用后直接跳向的是链,即使不存在表这个概念仍然也是行的通的。但是表的存在的确是有好处的。
表的存在提高了功能的抽象,同一功能的不同链归为一个表的域内,比如nat功能表,几个预定义链分布在不同HOOK点,NF_INET_PRE_ROUTING时候 做目的nat,NF_INET_POST_ROUTING, 时候做源nat。filter表主要用来做过滤用,mangle表主要用来设置一下标志用,当然这个并不是死板的。
表的存在可以减少预定义链的命名,使得易于记忆,不同的表里面都可以叫FORWARD链名
表的存在可以隔离自定义链,减少命名冲突。
规则,链,表在内核中的数据结构
Iptables 中链,规则不是明显的使用链表来组织的。可能因为规则总是顺序遍历的原因。
只有规则和表的结构,不存在链的结构。一个表直接由一块规则的内存区组成。
不用链表做优点:从用户空间传递到内核空间的时候,内核空间不需要再把传进来的数据用链表化,使用便宜量,这样也即节省了代码,又节省操作了时间。缺点不太利于阅读理解。用户空间在整合各个链表,以及自定义链表到内存区的时候,要不停的计算便宜量,一不小心容易出错,稍微比较麻烦点。
struct ipt_entry {
struct ipt_ip ip;
/* Mark with fields that we care about. */
unsigned int nfcache;
/* Size of ipt_entry + matches */
u_int16_t target_offset;
/* Size of ipt_entry + matches + target */
u_int16_t next_offset; /*我们只需要记住下一条的偏移量,相当于next指针*/
/* Back pointer */
unsigned int comefrom; /*返回调用链用*/
/* Packet and byte counters. */
struct xt_counters counters;
/* The matches (if any), then the target. */
unsigned char elems[0];
};
Table表结构
/* The table itself */
struct xt_table_info {
/* Size per table */
unsigned int size;
/* Number of entries: FIXME. --RR */
unsigned int number;
/* Initial number of entries. Needed for module usage count */
unsigned int initial_entries;
/* Entry points and underflows */
unsigned int hook_entry[NF_INET_NUMHOOKS]; /*预定义链在表中的偏移量*/
unsigned int underflow[NF_INET_NUMHOOKS]; /*从预定义链返回的偏移量*/
/* ipt_entry tables: one per CPU */
/* Note : this field MUST be the last one, see XT_TABLE_INFO_SZ */
void *entries[1];
};
思考:
对于预定义链:空链情况下也会占有一个ipt_entry, hook_entry[] underflow[]中的值是相等的,都指向该ipt_entry,该规则target必须是 drop或者accept,来表示默认处理方式。
对于自定义链:
头规则是
struct ipt_error {
struct ipt_entry entry;
struct ipt_error_target target;
};
struct ipt_error_target {
struct ipt_entry_target target; /*user.name 为“ERROR”*/
char errorname[IPT_FUNCTION_MAXNAMELEN]; /*这里存放表名称*/
};
阅读(1670) | 评论(0) | 转发(0) |