概述

本文主要对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. zepto学习(一)之click事件和tap事件比较

    一.click 和 tap 比较 两者都会在点击时触发,但是在手机WEB端,click会有 200~300 ms,所以请用tap代替click作为点击事件. singleTap和doubleTap分别 ...

  2. 第一章、接口规范之Restful规范

    阅读目录 2.1 数据的安全保障 2.2 接口特征表现 2.3 多数据版本共存 2.4 数据即是资源 2.5 资源操作由请求方式决定 3.1 正常响应 3.2 重定向响应 3.3 客户端异常 3.4 ...

  3. Codeforces 845G Shortest Path Problem?

    http://codeforces.com/problemset/problem/845/G 从顶点1dfs全图,遇到环则增加一种备选方案,环上的环不需要走到前一个环上作为条件,因为走完第二个环可以从 ...

  4. AJAX配置csrf

    // 从COokie取CSRF TOKEN的值 function getCookie(name) { var cookieValue = null; if (document.cookie & ...

  5. Vue介绍:vue导读1

    一.什么是vue 二.如何在页面中使用vue 三.vue的挂载点 四.vue的基础指令 一.什么是vue 1.什么是vue vue.js十一个渐进式javascript框架 渐进式:vue从控制页面中 ...

  6. Django_02_创建模型

    一:ORM简介 ORM,全拼Object-Relation Mapping,中文意为对象-关系映射,是随着面向对象的软件开发方法发展而产生的. 面向对象的开发方法是当今企业级应用开发环境中的主流开发方 ...

  7. CentOS7.x忘记root密码如何破解

    在CentOS7.x中,有一个单用户模式.CentOS7.x进入单用户模式与CentOS6.x略有不同,要复杂一些. 如果我们忘记了root的密码,可以在单用户模式下重置密码. 注意:此操作必须在服务 ...

  8. windows下虚拟python环境

    Windows虚拟环境 cd %HOMEDRIVE%%HOMEPATH%\Desktop python3  -m  venv venv 环境变量修改脚本bat,把脚本放到%HOMEDRIVE%%HOM ...

  9. idou老师带教你学Istio 03: istio故障注入功能的介绍和使用

    故障注入测试 故障注入测试顾名思义就是当被测试应用部分组件或功能出现潜在故障时其本身的容错机制是否正常工作,以达到规避故障保证正常组件或功能的使用.Istio提供了HTTP故障注入功能,在http请求 ...

  10. (转)再过半小时,你就能明白kafka的工作原理了

    为什么需要消息队列 周末无聊刷着手机,某宝网APP突然蹦出来一条消息“为了回馈老客户,女朋友买一送一,活动仅限今天!”.买一送一还有这种好事,那我可不能错过!忍不住立马点了去.于是选了两个最新款,下单 ...