概述

本文主要对filter表的初始化流程,以及钩子函数的规则match流程的源码进行分析;

源码分析

所在钩子点:

 /* 在LOCAL_IN,FORWARD, LOCAL_OUT钩子点工作 */
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
( << NF_INET_FORWARD) | \
( << NF_INET_LOCAL_OUT))

filter表信息:

 /* filter表的信息 */
static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS, /* filter工作的钩子点 */
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_FILTER,
.table_init = iptable_filter_table_init,
};

初始化:

 static int __net_init iptable_filter_table_init(struct net *net)
{
struct ipt_replace *repl;
int err; /* filter表已经被初始化了,返回 */
if (net->ipv4.iptable_filter)
return ; /* 分配初始化表,用于下面的表注册 */
repl = ipt_alloc_initial_table(&packet_filter);
if (repl == NULL)
return -ENOMEM;
/* Entry 1 is the FORWARD hook */
/* 入口1是否为FORWARD钩子点时的verdict值设置 */
((struct ipt_standard *)repl->entries)[].target.verdict =
forward ? -NF_ACCEPT - : -NF_DROP - ; /* 注册filter表,注册后,ipv4.iptable_filter保存了注册后的新表 */
err = ipt_register_table(net, &packet_filter, repl, filter_ops,
&net->ipv4.iptable_filter); /* 释放初始化表 */
kfree(repl);
return err;
}

分配用于初始化的table结构,其中的xt_alloc_initial_table是以宏的形式存在的;

 void *ipt_alloc_initial_table(const struct xt_table *info)
{
return xt_alloc_initial_table(ipt, IPT);
}

为了看起来方便,这里对函数进行了宏替换;

 void * ipt_alloc_initial_table(const struct xt_table *info) {
/* 钩子点掩码 */
unsigned int hook_mask = info->valid_hooks;
/* 钩子点数量 */
unsigned int nhooks = hweight32(hook_mask);
unsigned int bytes = , hooknum = , i = ;
/* 此次构造的表结构 */
struct {
struct ipt_replace repl;
struct ipt_standard entries[];
} *tbl; struct ipt_error *term; /* 算出entries的偏移 */
size_t term_offset = (offsetof(iptof(*tbl), entries[nhooks]) +
__alignof__(*term) - ) & ~(__alignof__(*term) - );
/* 分配内存 */
tbl = kzalloc(term_offset + sizeof(*term), GFP_KERNEL);
if (tbl == NULL)
return NULL;
/* 找到error部分 */
term = (struct ipt_error *)&(((char *)tbl)[term_offset]);
/* 拷贝表名 */
strncpy(tbl->repl.name, info->name, sizeof(tbl->repl.name));
/* 初始化error */
*term = (struct ipt_error)IPT_ERROR_INIT;
/* 初始化钩子点,数量(包括error),占用内存大小 */
tbl->repl.valid_hooks = hook_mask;
tbl->repl.num_entries = nhooks + ;
tbl->repl.size = nhooks * sizeof(struct ipt_standard) +
sizeof(struct ipt_error);
/* 对每个偏移进行初始化 */
for (; hook_mask != ; hook_mask >>= , ++hooknum) {
if (!(hook_mask & ))
continue;
tbl->repl.hook_entry[hooknum] = bytes;
tbl->repl.underflow[hooknum] = bytes;
tbl->entries[i++] = (struct ipt_standard)
IPT_STANDARD_INIT(NF_ACCEPT);
bytes += sizeof(struct ipt_standard);
}
/* 返回表 */
return tbl;
}

ipt_register_table完成表注册流程,其中包括了分配table_info结构,并且与table->private进行关联,table中规则的合法性检查,以及调用nf_register_net_hooks进行钩子函数的注册;

 /* 表注册 */
int ipt_register_table(struct net *net, const struct xt_table *table,
const struct ipt_replace *repl,
const struct nf_hook_ops *ops, struct xt_table **res)
{
int ret;
struct xt_table_info *newinfo;
struct xt_table_info bootstrap = {};
void *loc_cpu_entry;
struct xt_table *new_table; /* 分配table_info结构 */
newinfo = xt_alloc_table_info(repl->size);
if (!newinfo)
return -ENOMEM; /* 拷贝entries到table_info */
loc_cpu_entry = newinfo->entries;
memcpy(loc_cpu_entry, repl->entries, repl->size); /* 合法性检查 */
ret = translate_table(net, newinfo, loc_cpu_entry, repl);
if (ret != )
goto out_free; /* 建立新表,关联private到newinfo */
new_table = xt_register_table(net, table, &bootstrap, newinfo);
if (IS_ERR(new_table)) {
ret = PTR_ERR(new_table);
goto out_free;
} /* set res now, will see skbs right after nf_register_net_hooks */
/* 设置返回值指向新表 */
WRITE_ONCE(*res, new_table); /* 注册钩子函数 */
ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
if (ret != ) {
__ipt_unregister_table(net, new_table);
*res = NULL;
} return ret; out_free:
xt_free_table_info(newinfo);
return ret;
}

xt_register_table建立新表,将xt_table_info与表进行关联,并将表加入到net->xt.tables[table->af]链表;

 struct xt_table *xt_register_table(struct net *net,
const struct xt_table *input_table,
struct xt_table_info *bootstrap,
struct xt_table_info *newinfo)
{
int ret;
struct xt_table_info *private;
struct xt_table *t, *table; /* Don't add one object to multiple lists. */
/* 建立新表 */
table = kmemdup(input_table, sizeof(struct xt_table), GFP_KERNEL);
if (!table) {
ret = -ENOMEM;
goto out;
} mutex_lock(&xt[table->af].mutex);
/* Don't autoload: we'd eat our tail... */
/* 验证是否已经存在相同名字的表 */
list_for_each_entry(t, &net->xt.tables[table->af], list) {
if (strcmp(t->name, table->name) == ) {
ret = -EEXIST;
goto unlock;
}
} /* Simplifies replace_table code. */
table->private = bootstrap; /* 设置newinfo到table的privates */
if (!xt_replace_table(table, , newinfo, &ret))
goto unlock; private = table->private;
pr_debug("table->private->number = %u\n", private->number); /* save number of initial entries */
private->initial_entries = private->number; /* 将表加入到xt.tables中 */
list_add(&table->list, &net->xt.tables[table->af]);
mutex_unlock(&xt[table->af].mutex); /* 返回新表 */
return table; unlock:
mutex_unlock(&xt[table->af].mutex);
kfree(table);
out:
return ERR_PTR(ret);
}

钩子函数iptable_filter_hook,该函数主要调用ipt_do_table函数进行规则的匹配;

 static unsigned int
iptable_filter_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
/* LOCAL_OUT && (数据长度不足ip头 || 实际ip头部长度不足最小ip头),在使用raw socket */
if (state->hook == NF_INET_LOCAL_OUT &&
(skb->len < sizeof(struct iphdr) ||
ip_hdrlen(skb) < sizeof(struct iphdr)))
/* root is playing with raw sockets. */
return NF_ACCEPT; /* 核心规则匹配流程 */
return ipt_do_table(skb, state, state->net->ipv4.iptable_filter);
}

ipt_do_table是核心的规则匹配流程,其中包括了标准match,扩展match,标准target,扩展target的相关处理;

 /* 遍历钩子链上的所有规则,进行标准匹配和扩展匹配,执行其target操作 */
unsigned int
ipt_do_table(struct sk_buff *skb,
const struct nf_hook_state *state,
struct xt_table *table)
{
unsigned int hook = state->hook;
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
const struct iphdr *ip;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
const char *indev, *outdev;
const void *table_base;
struct ipt_entry *e, **jumpstack;
unsigned int stackidx, cpu;
const struct xt_table_info *private;
struct xt_action_param acpar;
unsigned int addend; /* Initialization */
stackidx = ;
ip = ip_hdr(skb);
indev = state->in ? state->in->name : nulldevname;
outdev = state->out ? state->out->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
* if it was a normal packet. All other fragments are treated
* normally, except that they will NEVER match rules that ask
* things we don't know, ie. tcp syn flag or ports). If the
* rule is also a fragment-specific rule, non-fragments won't
* match it. */
acpar.fragoff = ntohs(ip->frag_off) & IP_OFFSET;
acpar.thoff = ip_hdrlen(skb);
acpar.hotdrop = false;
acpar.state = state; IP_NF_ASSERT(table->valid_hooks & ( << hook));
local_bh_disable();
addend = xt_write_recseq_begin();
private = table->private;
cpu = smp_processor_id();
/*
* Ensure we load private-> members after we've fetched the base
* pointer.
*/
smp_read_barrier_depends();
/* 首个规则地址 */
table_base = private->entries;
jumpstack = (struct ipt_entry **)private->jumpstack[cpu]; /* Switch to alternate jumpstack if we're being invoked via TEE.
* TEE issues XT_CONTINUE verdict on original skb so we must not
* clobber the jumpstack.
*
* For recursion via REJECT or SYNPROXY the stack will be clobbered
* but it is no problem since absolute verdict is issued by these.
*/
if (static_key_false(&xt_tee_enabled))
jumpstack += private->stacksize * __this_cpu_read(nf_skb_duplicated); /* 获取对应链上的首个匹配规则 */
e = get_entry(table_base, private->hook_entry[hook]); do {
const struct xt_entry_target *t;
const struct xt_entry_match *ematch;
struct xt_counters *counter; IP_NF_ASSERT(e);
/* 标准match */
if (!ip_packet_match(ip, indev, outdev,
&e->ip, acpar.fragoff)) {
no_match:
/* 未匹配成功,继续下一个规则 */
e = ipt_next_entry(e);
continue;
} /* 扩展match */
xt_ematch_foreach(ematch, e) {
acpar.match = ematch->u.kernel.match;
acpar.matchinfo = ematch->data;
/* 只要有返回不匹配的,则说明匹配当前规则失败 */
if (!acpar.match->match(skb, &acpar))
goto no_match;
} counter = xt_get_this_cpu_counter(&e->counters);
ADD_COUNTER(*counter, skb->len, ); /* 标准match和扩展match都成功 */ /* 获取target */
t = ipt_get_target(e);
IP_NF_ASSERT(t->u.kernel.target); #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */
if (unlikely(skb->nf_trace))
trace_packet(state->net, skb, hook, state->in,
state->out, table->name, private, e);
#endif
/* Standard target? */
/* 标准target */
if (!t->u.kernel.target->target) {
int v; v = ((struct xt_standard_target *)t)->verdict;
/* 不会跳转到用户自定义规则 */
if (v < ) {
/* Pop from stack? */
/* 不是XT_RETURN,则跳出处理结果 */
if (v != XT_RETURN) {
verdict = (unsigned int)(-v) - ;
break;
} /* XT_RETURN则继续匹配下一条规则 */
if (stackidx == ) {
e = get_entry(table_base,
private->underflow[hook]);
} else {
e = jumpstack[--stackidx];
e = ipt_next_entry(e);
}
continue;
} /* 记录跳转规则,以便返回时获取下一跳规则进行后续匹配 */
if (table_base + v != ipt_next_entry(e) &&
!(e->ip.flags & IPT_F_GOTO))
jumpstack[stackidx++] = e; /* 获取自定义规则 */
e = get_entry(table_base, v);
continue;
} /* 扩展target,执行target回调 */ acpar.target = t->u.kernel.target;
acpar.targinfo = t->data; verdict = t->u.kernel.target->target(skb, &acpar);
/* Target might have changed stuff. */
ip = ip_hdr(skb); /* 需要继续匹配 */
if (verdict == XT_CONTINUE)
e = ipt_next_entry(e);
/* 跳出处理匹配结果 */
else
/* Verdict */
break;
/* 无hotdrop,继续匹配 */
} while (!acpar.hotdrop); xt_write_recseq_end(addend);
local_bh_enable(); /* drop标记 */
if (acpar.hotdrop)
return NF_DROP;
/* 返回匹配结果 */
else return verdict;
}

Netfilter 之 iptable_filter的更多相关文章

  1. 【Linux】iptables的内核模块问题大坑!

    系统环境 CentOS 6.5 今天本来可以平静的度过一天,正品味着下午茶的美好,突然接到防火墙iptables的报警. 进入到服务器中,执行下面的命令查看,结果报错 /etc/init.d/ipta ...

  2. linux netfilter ----iptable_filter

    内核中将filter模块被组织成了一个独立的模块,每个这样独立的模块中都有个类似的init()初始化函数:首先来看一下filter模块是如何将自己的钩子函数注册到netfilter所管辖的几个hook ...

  3. netfilter分析

    转自:http://blog.sina.com.cn/s/blog_a31ff26901013n07.html 一.概述 1. Netfilter/IPTables框架简介 Netfilter/IPT ...

  4. (转)Netfilter分析

    看到一篇讲Netfilter框架的,如果有一点基础了的话对于捋清整个框架很好帮助,转下来细细阅读. 转自http://aichundi.blog.163.com/blog/static/7013846 ...

  5. Netfilter深度解剖

         在网络上发现这个Netfilter写的很好的系列文章,为了便于后期寻找,特此标注下博客地址,感谢大大神提供.     ---------------------------分割线开始---- ...

  6. netfilter的笔记3--那些内置的表

    通过netfilter的笔记2的例子,我们知道了怎么使用netfilter的框架,对于内核的设计原则来说,策略和机制分离,所以提供了iptables来供用户配置防火墙策略. 那么,怎么使用iptabl ...

  7. Linux内核下包过滤框架——iptables&netfilter

    iptables & netfilter 1.简介 netfilter/iptables(下文中简称为iptables)组成Linux内核下的包过滤防火墙,完成封包过滤.封包重定向和网络地址转 ...

  8. (一)洞悉linux下的Netfilter&iptables:什么是Netfilter?

    转自:http://blog.chinaunix.net/uid-23069658-id-3160506.html 本人研究linux的防火墙系统也有一段时间了,由于近来涉及到的工作比较纷杂,久而久之 ...

  9. netfilter的钩子——数据包在内核态得捕获、修改和转发

    转发:http://blog.csdn.net/stonesharp/article/details/27091391 数据包在内核态得捕获.修改和转发(基于 netfilter)    忙活了好几天 ...

随机推荐

  1. VBA开发项目分享-1

    这个项目的目的是使用VBA制作一个股票筛选器,股票的指标数据存放在多个工作表,输入多个指标的查询条件,可以从相应的工作表里查询出符合条件的股票数据并返回.项目涉及的VBA知识结构有字典.数组.OLED ...

  2. SSISDB8:查看SSISDB记录Package执行的消息

    在执行Package时,SSISDB都会创建唯一的OperationID 和 ExecutionID,标识对package执行的操作和执行实例(Execution Instance),并记录opera ...

  3. 【Git】三、版本回退&撤消修改&文件删除

    提要 //查看git操作日志 $ git log //单行格式查看操作日志 $ git log --pretty=oneline //还原操作到上一次版本,有几个^就上几次 $ git reset - ...

  4. 第一章、前端之html

    目录 第一章.前端之html 一. html介绍 第一章.前端之html 一. html介绍 web服务本质 import socket sk = socket.socket() sk.bind((& ...

  5. Delphi VCL类结构

  6. Dart 面向对象 类 方法

    Dart是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类. 面向对象编程(OOP)的三个基本特征是:封装.继承.多态 封装:封装是对象和类概念的主要特性. ...

  7. GOLANG文件拷贝

    GOLANG文件拷贝 在Golang中,使用系统自带函数io.Copy() 如: srcFile := "C:/Users/Wisdom/Desktop/Wisdompic.png" ...

  8. STM32F407 CAN发送注意事项

    STM32使用的baseCAN,使用过程中发现一些注意的事项,特此记录. 现象: CAN发送程序,在1ms以上间隔调用时,一切正常. 当连续调用CAN发送程序4次或更多时,表现为丢数据,仅能发送一条或 ...

  9. NMI是什么

    NMI是什么 2016/02/28 vmunix NMI(non-maskable interrupt),就是不可屏蔽的中断.根据Intel的Software Developer手册Volume 3, ...

  10. SPOJ 10707 COT2 - Count on a tree II

    思路 树上莫队的题目 每次更新(u1,u2)和(v1,v2)(不包括lca)的路径,最后单独统计LCA即可 代码 #include <cstdio> #include <cstrin ...