<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Linux netfilter Hacking HOWTO:4.适合于程序员的信息</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<style type="text/css">
<!--
.code {
font-family: "Courier New", "Courier", "mono";
font-size: 9pt;
text-decoration: none;
}
-->
</style>
</head>
<body>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><a href="netfilter-hacking-HOWTO-5.htm">下一篇</a> <a href="netfilter-hacking-HOWTO-3.htm">前一篇</a>
<a href="netfilter-hacking-HOWTO.htm#toc4">目录</a></td>
</tr>
</table>
<hr>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td><p><font size="5">4.<a href="netfilter-hacking-HOWTO.htm#toc4"><strong>适合于程序员的信息</strong></a></font><br>
我要让你和我分享一个秘密:我的宠物鼠完成了所有的代码。在我的宠物的极其重要的计划中,我只是一个频道,一个平台,如果你需要的话。所以,如果这里有什么bug的话,不要责怪我。去责怪那个聪明伶俐、毛茸茸的家伙吧。</p>
<p>4.1 <a name="ss41"></a><a href="netfilter-hacking-HOWTO.htm#toc41">理解ip_tables</a></p>
<p> iptables仅仅在内存中指定一个控制队列(因此命名为“iptables”),并且像信息包来自哪里这样的信息,每一个hook之间应该开始遍历。当一张表被注册后,通过利用getsockopt()和setsockopt(),用户空间可以读取和替换其中的内容。<br>
iptables 不向任何netfilter hook注册:它最要依赖其他的模块去完成这个工作并且在适当的时候注入信息包;一个模块必须分别注册netfilte
hook和ip_tables,并且提供一种当hook到达的时候来呼叫ip_tables的机制。<br>
<br>
<strong>ip_tables的数据结构</strong></p>
<p> 为方便起见,被用户空间用以描述的控制常常使用的是和内核中相同的数据结构,虽然其中一部分只被内核使用。<br>
每一个标准都由以下部分组成:<br>
1.一个“struct ipt_entry”。<br>
2.0个或更多的“struct ipt_entry_match”结构,每一个都可以添加一个不定量(0或更多的byte)的数据。<br>
3.一个“ipt_entry_target”结构,可以添加一个不定量(0或更多的byte)的数据。<br>
这种使用变量的特点给出了一个有弹性的极大的数据扩展性,正如我们即将看到的,尤其是每一个匹配(match)和目标(target)可以传输任意大小的数据。这样也制造了一些陷阱,不管怎样:我们要小心留意队列。我们这样做是通过确保能方便的指定“ipt_entry”、“ipt_entry_match”和“ipt_entry_target”大小,并且,使用IPT_ALIGN()宏能使得所有的数据被安排进机器分配的最大的队列当中。“struct
ipt_entry”具有以下这些成员:<br>
1 .一个“struct ipt_ip”,包含了用来匹配用的IP包头的规范。<br>
2 .一个“nf_cache”位域表明当前控制正在执行数据包中的哪一个部分。<br>
3 .一个“target_offset”成员指出从当前控制开始地址的偏移量,也就是ipt_entry_target结构的开始地址。这常常需要被正确的设置(用IPT_ALIGN宏)。<br>
4 .一个“next_offset”成员指出当前控制的总大小,包括匹配(match)和目标(target)。这也需要用IPT_ALIGN宏来正确的设置。<br>
5 .一个“comfrom”成员被内核用来跟踪信息包的传递。<br>
6 .一个“struct ipt_counters”成员,包含了信息包和该信息包的字节计数器,该计数器用来和这个信息包匹配。</p>
<p>因为这种控制数据结构的机警的特性,一些帮助程序被提供:</p>
<p><strong>ipt_get_target()</strong><br>
这个内联函数返回一个控制的对象的指针。</p>
<p><strong>IPT_MATCH_ITERATE()</strong><br>
<br>
该宏在特定的控制里为每一个匹配调用特定的函数。函数的第一个参数是“struct ipt_match_entry”,其它参数(即便要(if any))是提供给IPT_MATCH_ITERATE()宏的。函数必须要么返回一个零来继续循环,要么返回一个非零值来终止循环。</p>
<p><strong>IPT_ENTRY_ITERATE()</strong></p>
<p> 该函数获得一个指针给入口,指针内容是入口的表的总的大小,和一个用来调用的函数。函数的第一个参数是“struct ipt_entry”,其它参数(即便要(if
any))是提供给IPT_ENTRY_ITERATE()宏的。函数必须要么返回一个零来继续循环,要么返回一个非零值来终止循环。</p>
<p><strong>来自用户空间的ip_tables</strong></p>
<p>用户空间有四步工作:读取当前表的内容,获取信息(hook的地址和表的大小),替换表(和获取旧的计数器)和加入新的计数器。</p>
<p>这样就允许由用户模拟的任何的细微操作:这是由libiptc库完成的,它为程序理解“add/delete/replcae”意义提供了便利。</p>
<p>因为这些表要被传入内核空间,对那些有着不同用户空间和内核空间类型规则的机器来说,队列成了问题(有32-bit userland的Sparc64)。这些问题通过忽略这些平台上的“libiptc.h”的IPT_ALIGN定义来解决。</p>
<p><strong>ip_tables使用和传递</strong></p>
<p>通过特定的hook,内核在特定的区域之间传递信息。这种控制被进行分析,如果“struct ipt_ip”的元素匹配,每一个“struct
ipt_entry_match”在每一次循环都被检查(每一个匹配函数和被调用的匹配相关联),如果函数返回一个0值,该控制的循环结束。如果设置“hotdrop”参数为1,该信息包会立刻被抛弃(这常常用在一些可疑的信息包上,例如在tcp匹配函数)</p>
<p>如果直到循环结束,计数器还被增加,“struct ipt_entry_target”被检查:如果是一个标准的对象,“verdict”成员被检查(负数意味着是一个确认的信息包,正数意味着是一个跳转的偏移量),如果检查结果是正数并且这个偏移量并不是指向下一个控制,那么“back”变量会被设置,并且上一个的“back”值会被放置在其“comefrom”成员里。</p>
<p>对于那些非标准的对象,目标函数被调用:该函数返回一个确认值(非标准对象不能跳转,因为这样会终止静态的循环侦测代码)。该值可以是IPT_CONTINUE,直到该循环进行到下一个控制。</p>
<p>4.2 <a name="ss42"></a><a href="netfilter-hacking-HOWTO.htm#toc42">扩展ip_tables</a></p>
<p>因为我很懒,不过ip_tables还算可扩展的。这基本上是一个用工作来欺骗他人的鬼话而已,那就是关于Open Source的一切,(cf.
Free Software,像RMS曾要说的一样,是关于自由的,当我写这些的时候我正是他的交谈者之一)。</p>
<p>扩展ip_tables潜在的关系到两个部分:扩展内核,通过写一个新的模块,或者扩展用户空间的iptables程序,通过写一个新的共享库。</p>
<p><strong>内核</strong></p>
<p>编写一个内核模块本身是很简单的,因为你可以参照现成的例子。有一点你要注意的是你的代码必须是可再入的:那可能是一个来自用户空间的信息包,而另一个可能是来自一个中断。实际上在SMP里,那可以是在2.3.4和其以上的一个经由CPU的涉及中断的信息包。</p>
<p>你所需要了解的相关的一些函数是:</p>
<p><strong>init_module()</strong><br>
<br>
这是模块的载入口。它返回一个负值表示错误,或者,如果在在netfilter注册成功则返回0。</p>
<p><strong>cleanup_module()</strong></p>
<p> 这是模块的退出函数。它将会在netfilter注销自己。</p>
<p><strong>ipt_register_match()</strong></p>
<p> 这是用来注册一个新的匹配类型。你要把“struct ipt_match”赋值给它,其常常声明为静态(file-scope)变量。</p>
<p><strong>ipt_register_target()</strong></p>
<p> 这是用来注册一个新的类型。你要把“struct ipt_target”赋值给它,其常常声明为静态(file-scope)变量。</p>
<p><strong>ipt_unregister_target</strong></p>
<p> 用来注销对象。</p>
<p><strong>ipt_unregister_match</strong></p>
<p> 用来注销匹配。</p>
<p>一个警告,关于在你的匹配或对象中的额外的空间中做这类灵活的处理(例如提供一个计数器)。在SMP机器上,对于每一个CPU都通过用memcpy复制了一份完整的表:如果你想保存重要的信息,你应该看看在“limit”匹配中使用的方法。</p>
<p>新的匹配函数</p>
<p> 新的匹配函数常常被当成一个独立的模块来写。用这些模块具有可扩展性,虽然不是必须的。一个方法是用netfilter框架的“nf_register_sockopt”函数让用户直接和你的模块对话。另一个方法是输出标记来让别的模块注册它们自己,netfilter和ip_tables都是这么做。</p>
<p>你的新的匹配函数的重心是struct ipt_match,它传递给“ipt_register_match()”,这个结构有下面这些成员:</p>
<p><strong>list</strong></p>
<p> 这个成员常设置为空“{NULL,NULL}”。</p>
<p><strong>name</strong></p>
<p> 这个成员是匹配函数的名称,因此是被用户来指定。为了自动加载工作,这个名称必须和模块的名称相符合(如,如果name是“mac”,模块必须是“ipt_mac.o”)。</p>
<p><strong>match</strong><br>
<br>
这个成员是一个指向匹配函数的指针,它获取skb,一个输入输出设备指针(其中有可能有NULL,主要依赖于hook),一个在控制中被影响的(在用户空间中被准备好的结构)指�
评论3