热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linuxnetfilteriptable_filter

内核中将filter模块被组织成了一个独立的模块,每个这样独立的模块中都有个类似的init()初始化函数;首先来看一下filter模块是如何将自己的钩子

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

filter 模块钩子点:

/* 在LOCAL_IN,FORWARD, LOCAL_OUT钩子点工作 */
#define FILTER_VALID_HOOKS ((1 <(1 < \(1 <

 

static const struct xt_table packet_filter &#61; {.name &#61; "filter",.valid_hooks &#61; FILTER_VALID_HOOKS,.me &#61; THIS_MODULE,.af &#61; NFPROTO_IPV4,.priority &#61; NF_IP_PRI_FILTER,.table_init &#61; iptable_filter_table_init,
};

 

 

ilist

struct ipt_standard {struct ipt_entry entry;struct xt_standard_target target;
};
struct ipt_error {struct ipt_entry entry;struct xt_error_target target;
};
void *ipt_alloc_initial_table(const struct xt_table *info)
{unsigned
int hook_mask &#61; info->valid_hooks; //LOCAL_IN、FORWARD、LOCAL_OUT unsigned int nhooks &#61; hweight32(hook_mask); //这里得到3&#xff0c;上面hookmask对应三个hook点。unsigned int bytes &#61; 0, hooknum &#61; 0, i &#61; 0;
//看到函数的最后&#xff0c;知道返回值是tbl&#xff0c;而这里的结构体内嵌的三个结构体是tbl的组成&#xff0c;三个结构体的数据结构拓扑图如图11.1.3。struct {struct ipt_replace repl; struct ipt_standard entries[nhooks]; struct ipt_error term; } *tbl &#61; kzalloc(sizeof(*tbl), GFP_KERNEL); if (tbl &#61;&#61; NULL) return NULL; strncpy(tbl->repl.name, info->name, sizeof(tbl->repl.name)); tbl->term &#61; (struct ipt_error)IPT_ERROR_INIT; tbl->repl.valid_hooks &#61; hook_mask; tbl->repl.num_entries &#61; nhooks &#43; 1; tbl->repl.size &#61; nhooks * sizeof(struct ipt_standard) &#43; sizeof(struct ipt_error); for (; hook_mask !&#61; 0; hook_mask >>&#61; 1, &#43;&#43;hooknum) {if (!(hook_mask & 1)) continue; tbl->repl.hook_entry[hooknum] &#61; bytes; tbl->repl.underflow[hooknum] &#61; bytes; tbl->entries[i&#43;&#43;] &#61; (struct ipt_standard) IPT_STANDARD_INIT(NF_ACCEPT); bytes &#43;&#61; sizeof(struct ipt_standard); }return tbl;
}
/*filter模块初始化时先调用ipt_register_table向Netfilter完成filter过滤表的注册&#xff0c;然后调用ipt_register_hooks完成自己钩子函数的注册
*/

initial_table.repl&#61; { "filter", FILTER_VALID_HOOKS, 4,sizeof(struct ipt_standard) * 3 &#43; sizeof(struct ipt_error),{ [NF_IP_LOCAL_IN] &#61; 0,[NF_IP_FORWARD] &#61; sizeof(struct ipt_standard),[NF_IP_LOCAL_OUT] &#61; sizeof(struct ipt_standard) * 2},{ [NF_IP_LOCAL_IN] &#61; 0,[NF_IP_FORWARD] &#61; sizeof(struct ipt_standard),[NF_IP_LOCAL_OUT] &#61; sizeof(struct ipt_standard) * 2},0, NULL, { }
}&#xff1b;


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

---最后一个柔性数组struct ipt_entry  entries[0]中保存了默认的那四条规则

 

test

/*
简而言之ipt_register_table()所做的事情就是从模板initial_table变量的repl成员里取出初始化数据&#xff0c;然后申请一块内存并用repl里的值来初始化它&#xff0c;
之后将这块内存的首地址赋给packet_filter表的private成员&#xff0c;最后将packet_filter挂载到xt[2].tables的双向链表中。
*/
////iptable netfilter表注册添加到该链表中 iptable_filter.ko里面用结构xt_table,该表现源从packet_filter来的 见xt_register_table
//table头部:net->xt.tables[table->af],所有table的头部链表
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 &#61; {0};void *loc_cpu_entry;struct xt_table *new_table;newinfo &#61; xt_alloc_table_info(repl->size);//malloc for xt_table filter size为sizeof(struct ipt_standard) * 3 &#43; sizeof(struct ipt_error),if (!newinfo)return -ENOMEM;loc_cpu_entry &#61; newinfo->entries;//将表中的规则入口地址赋值给loc_cpu_entrymemcpy(loc_cpu_entry, repl->entries, repl->size);//拷贝repl里面的entries规则到xt_table_info表里面的entries里面/*translate_table函数将由newinfo所表示的table的各个规则进行边界检查&#xff0c;然后对于newinfo所指的xt_talbe_info结构中的hook_entries和underflows赋予正确的值&#xff0c;最后将表项向其他cpu拷贝*/ret &#61; translate_table(net, newinfo, loc_cpu_entry, repl);if (ret !&#61; 0)goto out_free;
/*
packet_filter中没对其private成员进行初始化&#xff0c;那么这个工作自然而然的就留给了xt_register_table()函数来完成&#xff0c;它也定义在x_tables.c文件中&#xff0c;它主要完成两件事&#xff1a;1&#xff09;、将由newinfo参数所存储的表里面关于规则的基本信息结构体xt_table_info{}变量赋给由table参数所表示的packet_filter{}的private成员变量&#xff1b;2&#xff09;、根据packet_filter的协议号af&#xff0c;将filter表挂到变量xt中tables成员变量所表示的双向链表里。
*/new_table &#61; xt_register_table(net, table, &bootstrap, newinfo);if (IS_ERR(new_table)) {ret &#61; 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 &#61; nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));if (ret !&#61; 0) {__ipt_unregister_table(net, new_table);*res &#61; NULL;}return ret;out_free:xt_free_table_info(newinfo);return ret;
}

 

 

 

Filter回调函数

 在上述ipt_register_table 实现中会调用nf_register_net_hooks  注册钩子回调函数

Netfilter中默认表filter在建立时则在NF_IP_LOCAL_IN&#xff0c;NF_IP_FORWARD&#xff0c;NF_IP_LOCAL_OUT钩子点注册了钩子函数iptable_filter_hook&#xff0c;其核心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头)&#xff0c;在使用raw socket */if (state->hook &#61;&#61; 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);
}

 

可知其回调函数核心函数为&#xff1a;

/* Returns one of the generic firewall policies, like NF_ACCEPT.
包过滤子功能&#xff1a;包过滤一共定义了四个hook函数&#xff0c;这四个hook函数本质最后都调用了ipt_do_table()函数。
实际上是直接调用ipt_do_table(ip_tables.c)函数接下来就是根据table里面的entry来处理数据包了一个table就是一组防火墙规则的集合而一个entry就是一条规则&#xff0c;每个entry由一系列的matches和一个target组成一旦数据包匹配了该某个entry的所有matches&#xff0c;就用target来处理它
Match又分为两部份&#xff0c;一部份为一些基本的元素&#xff0c;如来源/目的地址&#xff0c;进/出网口&#xff0c;协议等&#xff0c;对应了struct ipt_ip&#xff0c;
我们常常将其称为标准的match&#xff0c;另一部份match则以插件的形式存在&#xff0c;是动态可选择&#xff0c;也允许第三方开发的&#xff0c;
常常称为扩展的match&#xff0c;如字符串匹配&#xff0c;p2p匹配等。同样&#xff0c;规则的target也是可扩展的。这样&#xff0c;一条规则占用的空间&#xff0c;
可以分为&#xff1a;struct ipt_ip&#43;n*match&#43;n*target&#xff0c;&#xff08;n表示了其个数&#xff0c;这里的match指的是可扩展的match部份&#xff09;。
*/
unsigned
int
ipt_do_table(
struct sk_buff *skb,const struct nf_hook_state *state,struct xt_table *table)
{unsigned
int hook &#61; 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 &#61; 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 &#61; 0;ip &#61; ip_hdr(skb);indev &#61; state->in ? state->in->name : nulldevname;outdev &#61; 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&#39;t know, ie. tcp syn flag or ports). If the* rule is also a fragment-specific rule, non-fragments won&#39;t* match it. */acpar.fragoff &#61; ntohs(ip->frag_off) & IP_OFFSET;acpar.thoff &#61; ip_hdrlen(skb);acpar.hotdrop &#61; false;acpar.net &#61; state->net;acpar.in &#61; state->in;acpar.out &#61; state->out;acpar.family &#61; NFPROTO_IPV4;acpar.hooknum &#61; hook;IP_NF_ASSERT(table->valid_hooks & (1 << hook));local_bh_disable();addend &#61; xt_write_recseq_begin();private &#61; table->private;cpu &#61; smp_processor_id();/** Ensure we load private-> members after we&#39;ve fetched the base* pointer.*/smp_read_barrier_depends();table_base &#61; private->entries;jumpstack &#61; (struct ipt_entry **)private->jumpstack[cpu];/* Switch to alternate jumpstack if we&#39;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 &#43;&#61; private->stacksize * __this_cpu_read(nf_skb_duplicated);e &#61; 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);/*匹配IP包&#xff0c;成功则继续匹配下去&#xff0c;否则跳到下一个规则 ip_packet_match匹配标准match, 也就是ip报文中的一些基本的元素&#xff0c;如来源/目的地址&#xff0c;进/出网口&#xff0c;协议等&#xff0c;因为要匹配的内容是固定的&#xff0c;所以具体的函数实现也是固定的。而IPT_MATCH_ITERATE &#xff08;应该猜到实际是调用第二个参数do_match函数&#xff09;匹配扩展的match&#xff0c;如字符串匹配&#xff0c;p2p匹配等&#xff0c;因为要匹配的内容不确定&#xff0c;所以函数的实现也是不一样的&#xff0c;所以do_match的实现就和具体的match模块有关了。 这里的&e->ip就是上面的ipt_ip结构*/if (!ip_packet_match(ip, indev, outdev,&e->ip, acpar.fragoff)) {//遍历匹配match
no_match:e &#61; ipt_next_entry(e);continue;}xt_ematch_foreach(ematch, e) {acpar.match &#61; ematch->u.kernel.match;acpar.matchinfo &#61; ematch->data;if (!acpar.match->match(skb, &acpar))goto no_match;}counter &#61; xt_get_this_cpu_counter(&e->counters);ADD_COUNTER(*counter, skb->len, 1);
/* ipt_get_target获取当前target&#xff0c;t是一个ipt_entry_target结构&#xff0c;这个函数就是简单的返回e&#43;e->target_offset每个entry只有一个target&#xff0c;所以不需要像match一样遍历&#xff0c;直接指针指过去了*/t &#61; 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
/* 这里都还是和扩展的match的匹配很像&#xff0c;但是下面一句有句注释&#xff1a;Standard target? 判断当前target是否标准的target&#xff1f;而判断的条件是u.kernel.target->target&#xff0c;就是ipt_target结构里的target函数是否为空&#xff0c;
而下面还出现了ipt_standard_target结构和verdict变量&#xff0c;好吧&#xff0c;先停下&#xff0c;看看ipt_standard_target结构再说
ipt_standard_target的定义&#xff1a;
struct ipt_standard_target
{struct ipt_entry_target target;int verdict;
};
也就比ipt_entry_target多了一个verdict&#xff08;判断&#xff09;&#xff0c;请看前面的nf_hook_slow&#xff08;&#xff09;函数&#xff0c;里面也有verdict变量&#xff0c;
用来保存hook函数的返回值&#xff0c;常见的有这些
#define NF_DROP 0
#define NF_ACCEPT 1
#define NF_STOLEN 2
#define NF_QUEUE 3
#define NF_REPEAT 4
#define RETURN IPT_RETURN
#define IPT_RETURN (-NF_MAX_VERDICT - 1)
#define NF_MAX_VERDICT NF_REPEAT
我们知道chain&#xff08;链&#xff09;是某个检查点上检查的规则的集合。除了默认的chain外&#xff0c;用户还可以创建新的chain。在iptables中&#xff0c;
同一个chain里的规则是连续存放的。默认的chain的最后一条规则的target是chain的policy。用户创建的chain的最后一条
规则的target的调用返回值是NF_RETURN&#xff0c;遍历过程将返回原来的chain。规则中的target也可以指定跳转到某个用户创建的chain上&#xff0c;
这时它的target是ipt_stardard_target&#xff0c;并且这个target的verdict值大于0。如果在用户创建的chain上没有找到匹配的规则&#xff0c;
遍历过程将返回到原来chain的下一条规则上。事实上&#xff0c;target也是分标准的和扩展的&#xff0c;但前面说了&#xff0c;毕竟一个是条件&#xff0c;一个是动作&#xff0c;
target的标准和扩展的关系和match还是不太一样的&#xff0c;不能一概而论&#xff0c;而且在标准的target里还可以根据verdict的值再
划分为内建的动作或者跳转到自定义链简单的说&#xff0c;标准target就是内核内建的一些处理动作或其延伸
扩展的当然就是完全由用户定义的处理动作
*/if (!t->u.kernel.target->target) {int v;v &#61; ((struct xt_standard_target *)t)->verdict;/*v小于0&#xff0c;动作是默认内建的动作&#xff0c;也可能是自定义链已经结束而返回return标志*/if (v <0) { /*如果v大于0,记录是跳转偏移量,小于0,是标准target*//* Pop from stack? */if (v !&#61; XT_RETURN) {verdict &#61; (unsigned int)(-v) - 1;break;}/* e和back分别是当前表的当前Hook的规则的起始偏移量和上限偏移量&#xff0c;即entry的头和尾&#xff0c;e&#61;back */if (stackidx &#61;&#61; 0) {e &#61; get_entry(table_base,private->underflow[hook]);} else {e &#61; jumpstack[--stackidx];e &#61; ipt_next_entry(e);}continue;}/* v大于等于0&#xff0c;处理用户自定义链&#xff0c;如果当前链后还有规则&#xff0c;而要跳到自定义链去执行&#xff0c;那么需要保存一个back点&#xff0c;以指示程序在匹配完自定义链后&#xff0c;应当继续匹配的规则位置&#xff0c;自然地&#xff0c; back点应该为当前规则的下一条规则&#xff08;如果存在的话&#xff09;
至于为什么下一条规则的地址是table_base&#43;v, 就要去看具体的规则是如何添加的了
*/if (table_base &#43; v !&#61; ipt_next_entry(e) &&!(e->ip.flags & IPT_F_GOTO))jumpstack[stackidx&#43;&#43;] &#61; e;e &#61; get_entry(table_base, v); /*根据verdict的偏移量找到跳转的rule*/continue;}acpar.target &#61; t->u.kernel.target;acpar.targinfo &#61; t->data; /*如果是扩展target&#xff0c;就执行扩展targe的target处理函数*/verdict &#61; t->u.kernel.target->target(skb, &acpar);/* Target might have changed stuff. */ip &#61; ip_hdr(skb);if (verdict &#61;&#61; XT_CONTINUE)e &#61; ipt_next_entry(e);else/* Verdict */break;} while (!acpar.hotdrop);xt_write_recseq_end(addend);local_bh_enable();if (acpar.hotdrop)return NF_DROP;else return verdict;
}

 

filter回调函数 和 match、target之间的关系&#xff1a;

 

转:https://www.cnblogs.com/codestack/p/10850663.html



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
author-avatar
重报礼坊官肀网
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有