Netfilter 之 连接跟踪的helper
注册helper
nf_conntrack_ftp_init是连接跟踪ftp模块的初始化函数,可以看到其调用了nf_conntrack_helpers_register来注册helper;
static int __init nf_conntrack_ftp_init(void)
{
int i, ret = ; NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master)); ftp_buffer = kmalloc(, GFP_KERNEL);
if (!ftp_buffer)
return -ENOMEM; if (ports_c == )
ports[ports_c++] = FTP_PORT; /* FIXME should be configurable whether IPv4 and IPv6 FTP connections
are tracked or not - YK */
/* 初始化helper */
for (i = ; i < ports_c; i++) {
nf_ct_helper_init(&ftp[ * i], AF_INET, IPPROTO_TCP, "ftp",
FTP_PORT, ports[i], ports[i], &ftp_exp_policy,
, help, nf_ct_ftp_from_nlattr, THIS_MODULE);
nf_ct_helper_init(&ftp[ * i + ], AF_INET6, IPPROTO_TCP, "ftp",
FTP_PORT, ports[i], ports[i], &ftp_exp_policy,
, help, nf_ct_ftp_from_nlattr, THIS_MODULE);
} /* 注册helper */
ret = nf_conntrack_helpers_register(ftp, ports_c * );
if (ret < ) {
pr_err("failed to register helpers\n");
kfree(ftp_buffer);
return ret;
} return ;
}
模块通过调用nf_conntrack_helper_register函数将helper添加到对应的hash中;函数首先会对是否有相同匹配的helper进行检查,不存在才会存放到hash中;
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
unsigned int h = helper_hash(&me->tuple);
struct nf_conntrack_helper *cur;
int ret = , i; BUG_ON(me->expect_policy == NULL);
BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES);
BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - ); /* 允许的最大期望连接超额 */
if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT)
return -EINVAL; mutex_lock(&nf_ct_helper_mutex);
/* 遍历helper hash,查找是否已存在,条件(名称相同 &&(三层协议为指定||三层协议相同)&&四层协议相同 */
for (i = ; i < nf_ct_helper_hsize; i++) {
hlist_for_each_entry(cur, &nf_ct_helper_hash[i], hnode) {
if (!strcmp(cur->name, me->name) && /* 名称相同 */
(cur->tuple.src.l3num == NFPROTO_UNSPEC || /* 三层协议未指定 */
cur->tuple.src.l3num == me->tuple.src.l3num) && /* 三层协议相同 */
cur->tuple.dst.protonum == me->tuple.dst.protonum) { /* 四层协议相同 */
ret = -EEXIST;
goto out;
}
}
} /* avoid unpredictable behaviour for auto_assign_helper */
/* 不是userspace,遍历hash,进行tuple和掩码的比较,查看是否是已存在 */
if (!(me->flags & NF_CT_HELPER_F_USERSPACE)) {
hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) {
if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple,
&mask)) {
ret = -EEXIST;
goto out;
}
}
} /* 将helper加入到helper hash */
refcount_set(&me->refcnt, );
hlist_add_head_rcu(&me->hnode, &nf_ct_helper_hash[h]);
nf_ct_helper_count++;
out:
mutex_unlock(&nf_ct_helper_mutex);
return ret;
}
关联helper到conntrack
在有新数据包进入的时候,如果没有对应连接跟踪,需要调用init_conntrack新建一个连接跟踪,其中会设置连接跟踪的helper;
static int
resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
struct sk_buff *skb,
unsigned int dataoff,
u_int16_t l3num,
u_int8_t protonum,
struct nf_conntrack_l3proto *l3proto,
struct nf_conntrack_l4proto *l4proto)
{
const struct nf_conntrack_zone *zone;
struct nf_conntrack_tuple tuple;
struct nf_conntrack_tuple_hash *h;
enum ip_conntrack_info ctinfo;
struct nf_conntrack_zone tmp;
struct nf_conn *ct;
u32 hash; /* 将源目的地址端口协议方向等字段设置到tuple */
if (!nf_ct_get_tuple(skb, skb_network_offset(skb),
dataoff, l3num, protonum, net, &tuple, l3proto,
l4proto)) {
pr_debug("Can't get tuple\n");
return ;
} /* look for tuple match */
/* 从hash中查找tuple */
zone = nf_ct_zone_tmpl(tmpl, skb, &tmp);
hash = hash_conntrack_raw(&tuple, net);
h = __nf_conntrack_find_get(net, zone, &tuple, hash); /* 未找到该tuple */
if (!h) {
/* 创建一个节点 */
h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
skb, dataoff, hash);
if (!h)
return ;
if (IS_ERR(h))
return PTR_ERR(h);
} /* 获取到nf_conn */
ct = nf_ct_tuplehash_to_ctrack(h); /* It exists; we have (non-exclusive) reference. */
/* 应答方向,已建立连接应答 */
if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) {
ctinfo = IP_CT_ESTABLISHED_REPLY;
}
/* 原始方向 */
else {
/* Once we've had two way comms, always ESTABLISHED. */
/* 已经见过应答了,那么是已连接状态 */
if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
pr_debug("normal packet for %p\n", ct);
ctinfo = IP_CT_ESTABLISHED;
}
/* 有期望连接标记,则设置关联字段 */
else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) {
pr_debug("related packet for %p\n", ct);
ctinfo = IP_CT_RELATED;
}
/* 其他情况,新连接 */
else {
pr_debug("new packet for %p\n", ct);
ctinfo = IP_CT_NEW;
}
} /* skb关联nf_conn */
nf_ct_set(skb, ct, ctinfo);
return ;
}
init_conntrack函数调用__nf_ct_try_assign_helper来对helper进行赋值;
static noinline struct nf_conntrack_tuple_hash *
init_conntrack(struct net *net, struct nf_conn *tmpl,
const struct nf_conntrack_tuple *tuple,
struct nf_conntrack_l3proto *l3proto,
struct nf_conntrack_l4proto *l4proto,
struct sk_buff *skb,
unsigned int dataoff, u32 hash)
{
/*...*/ /* 尝试设置helper */
if (!exp)
__nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC); /*...*/ return &ct->tuplehash[IP_CT_DIR_ORIGINAL];
}
__nf_ct_try_assign_helper函数完成对helper的设置,其中在helper为空的时候调用nf_ct_lookup_helper查找helper;
int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
gfp_t flags)
{
struct nf_conntrack_helper *helper = NULL;
struct nf_conn_help *help;
struct net *net = nf_ct_net(ct); /* We already got a helper explicitly attached. The function
* nf_conntrack_alter_reply - in case NAT is in use - asks for looking
* the helper up again. Since now the user is in full control of
* making consistent helper configurations, skip this automatic
* re-lookup, otherwise we'll lose the helper.
*/
if (test_bit(IPS_HELPER_BIT, &ct->status))
return ; /* 原关联存在,记录helper为原关联的helper */
if (tmpl != NULL) {
help = nfct_help(tmpl);
if (help != NULL) {
helper = help->helper;
set_bit(IPS_HELPER_BIT, &ct->status);
}
} /* 新连接跟踪的help */
help = nfct_help(ct); /* helper为空 */
if (helper == NULL) {
/* 根据tuple和mask查找helper */
helper = nf_ct_lookup_helper(ct, net); /* 没找到,赋值为NULL */
if (helper == NULL) {
if (help)
RCU_INIT_POINTER(help->helper, NULL);
return ;
}
} /* help为空 */
if (help == NULL) {
/* 为连接跟踪添加help扩展 */
help = nf_ct_helper_ext_add(ct, helper, flags);
if (help == NULL)
return -ENOMEM;
}
/* 扩展不为空 */
else {
/* We only allow helper re-assignment of the same sort since
* we cannot reallocate the helper extension area.
*/
struct nf_conntrack_helper *tmp = rcu_dereference(help->helper); /* 已有的help和新的helper所属的help不是同一个扩展help */
if (tmp && tmp->help != helper->help) {
RCU_INIT_POINTER(help->helper, NULL);
return ;
}
} /* 设置helper */
rcu_assign_pointer(help->helper, helper); return ;
}
static struct nf_conntrack_helper *
nf_ct_lookup_helper(struct nf_conn *ct, struct net *net)
{
if (!net->ct.sysctl_auto_assign_helper) {
if (net->ct.auto_assign_helper_warned)
return NULL;
if (!__nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple))
return NULL;
pr_info("nf_conntrack: default automatic helper assignment "
"has been turned off for security reasons and CT-based "
" firewall rule not found. Use the iptables CT target "
"to attach helpers instead.\n");
net->ct.auto_assign_helper_warned = ;
return NULL;
} /* 根据tuple查找 */
return __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
}
__nf_ct_helper_find会遍历第一部分讲到的保存已注册helper的hash表,通过tuple和mask来查找对应helper;
static struct nf_conntrack_helper *
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{
struct nf_conntrack_helper *helper;
struct nf_conntrack_tuple_mask mask = { .src.u.all = htons(0xFFFF) };
unsigned int h; if (!nf_ct_helper_count)
return NULL; h = helper_hash(tuple); /* 遍历对应hash,根据tuple和mask查找helper */
hlist_for_each_entry_rcu(helper, &nf_ct_helper_hash[h], hnode) {
if (nf_ct_tuple_src_mask_cmp(tuple, &helper->tuple, &mask))
return helper;
}
return NULL;
}
对比过程,在三层地址,四层端口,协议均相同的情况,认为找到helper;
static inline bool
nf_ct_tuple_src_mask_cmp(const struct nf_conntrack_tuple *t1,
const struct nf_conntrack_tuple *t2,
const struct nf_conntrack_tuple_mask *mask)
{
int count; /* 判断三层地址是否相同 */
for (count = ; count < NF_CT_TUPLE_L3SIZE; count++) {
if ((t1->src.u3.all[count] ^ t2->src.u3.all[count]) &
mask->src.u3.all[count])
return false;
} /* 判断四层端口是否相同 */
if ((t1->src.u.all ^ t2->src.u.all) & mask->src.u.all)
return false; /* 判断协议是否相同 */
if (t1->src.l3num != t2->src.l3num ||
t1->dst.protonum != t2->dst.protonum)
return false; /* 地址+端口+协议都相同,已存在,返回true */
return true;
}
调用helper
在连接跟踪的ipv4_helper钩子函数中,会查找连接跟踪的对应的helper,并执行helper的help函数完成扩展功能;
static unsigned int ipv4_helper(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
const struct nf_conn_help *help;
const struct nf_conntrack_helper *helper; /* This is where we call the helper: as the packet goes out. */
/* 获取skb关联的nf_conn */
ct = nf_ct_get(skb, &ctinfo);
/* 未关联,或者是 已建立连接的关联连接的响应 */
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
return NF_ACCEPT; /* 获取help扩展 */
help = nfct_help(ct); /* 没有扩展 */
if (!help)
return NF_ACCEPT; /* rcu_read_lock()ed by nf_hook_thresh */
/* 获得helper */
helper = rcu_dereference(help->helper);
if (!helper)
return NF_ACCEPT; /* 执行扩展的help函数 */
return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
ct, ctinfo);
}
Netfilter 之 连接跟踪的helper的更多相关文章
- Netfilter之连接跟踪实现机制初步分析
Netfilter之连接跟踪实现机制初步分析 原文: http://blog.chinaunix.net/uid-22227409-id-2656910.html 什么是连接跟踪 连接跟踪(CONNT ...
- Netfilter 之 连接跟踪相关数据结构
Netfilter通过连接跟踪来记录和跟踪连接的状态,为状态防火墙和NAT提供基础支持: 钩子点与钩子函数 下图为钩子点和钩子函数的关系图(点击图片查看原图),其中ipv4_conntrack_def ...
- Netfilter 之 连接跟踪钩子函数分析
ipv4_conntrack_defrag ipv4_conntrack_defrag对输入包进行检查,如果是分片包,则调用nf_ct_ipv4_gather_frags函数进行重组: static ...
- Netfilter 之 连接跟踪初始化
基础参数初始化 nf_conntrack_init_start函数完成连接跟踪基础参数的初始化,包括了hash,slab,扩展项,GC任务等: int nf_conntrack_init_start( ...
- Netfilter&iptables:如何理解连接跟踪机制?
如何理解Netfilter中的连接跟踪机制? 本篇我打算以一个问句开头,因为在知识探索的道路上只有多问然后充分调动起思考的机器才能让自己走得更远.连接跟踪定义很简单:用来记录和跟踪连接的状态. 问:为 ...
- linux内核netfilter连接跟踪的hash算法
linux内核netfilter连接跟踪的hash算法 linux内核中的netfilter是一款强大的基于状态的防火墙,具有连接跟踪(conntrack)的实现.conntrack是netfilte ...
- linux nf_conntrack 连接跟踪机制 3-hook
conntrack hook函数分析 enum nf_ip_hook_priorities { NF_IP_PRI_FIRST = INT_MIN, NF_IP_PRI_CONNTRACK_DEFRA ...
- linux nf_conntrack 连接跟踪机制 2
连接跟踪初始化 基础参数的初始化:nf_conntrack_standalone_init 会调用nf_conntrack_init_start 完成连接跟踪基础参数的初始化, hash slab 扩 ...
- linux nf_conntrack 连接跟踪机制
PRE_ROUTING和LOCAL_OUT点可以看作是整个netfilter的入口,而POST_ROUTING和LOCAL_IN可以看作是其出口; 报文到本地:PRE_ROUTING----LOCAL ...
随机推荐
- JS基础_函数的返回值
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- 一个SAP顾问的回忆:我过去很胖!
去年也是这个时候,SAP成都研究院体育界大神邓阳,曾经赏脸在Jerry这个公众号上赐文一篇,介绍了他和围绕在他身边的一群小伙伴们的体育故事:SAP成都研究院的体育故事 而今天文章的主角则是SAP成都研 ...
- Flutter——Container组件(容器组件)
名称 功能 alignment topCenter:顶部居中对齐 topLeft:顶部左对齐 topRight:顶部右对齐 center:水平垂直居中对齐 centerLeft:垂直居中水平居左对齐 ...
- 【异常】azkaban.executor.ExecutorManagerException: No active executors found
1 azkaban启动异常 没有找到活动的executors,需在MySQL数据库里设置端口为12321的executors表的active为1 update azkaban.executors ...
- 2-1 bash基本特性
bash基本特性 bash基本介绍 bash是shell的一种,shell是计算机与用户交互的主要接口,狭义上的shell指的是CLI(command line interface命令行接口),用户输 ...
- Hadoop_15_MapRduce_案例1_Wordcount 单词统计
1.Wordcount示例编写: MapReduce采用”分而治之”的思想,把对大规模数据集的操作,分发给一个主节点管理下的各个分节点共同完成,然后通过整合各 个节点的中间结果,得到最终结果.简单地说 ...
- C# 之 .net core -- 创建项目
一.新建一个Web 的 应用程序 二.选择项目的基本信息(.net coer 2.2 和带有试图控制器的程序) 这个是类似以MVC的模式,也可以用其他的,总之需要什么选什么 三. 然后既可以看到这样一 ...
- 02—EF初次体验
新建个表,我用的是sql server2014,我会把文件发上来,如果是低版本的,可以执行语句,数据库就自己创建吧. USE [testdb] GO CREATE TABLE [dbo].[Produ ...
- 4-windows启用账户锁定计数器
1.打开本地策略编辑器 命令:gpedit.msc 2.找到账户锁定策略 3.右键属性,设置登录无效次数 注:这个策略修改完后,不需要重新服务器就能生效
- Mapreduce案例之Pi值估算
题目: 这个程序的原理是这样的.假如有一个边长为1的正方形.以正方形的一个端点为圆心,以1为半径,画一个圆弧,于是在正方形内就有了一个直角扇形.在正方形里随机生成若干的点,则有些点是在扇形内,有些点是 ...