这里只是个笔记,我会将进一步修改、整理,补充后的版本发在我的个人主页上:
http://www.skynet.org.cn/viewthread.php?tid=208&pid=262&page=1#pid262

应用软件源码分析发在这里不知道是否合适:D

最近看了一下iptables的实现,现在想陆续把看的心得贴出来,和大家讨论一下,一起学习……

一、规则的显示
选择先来说明规则的显示,因为他涉及到的东东简单,而且又全面,了解了规则的显示,对于其他操作的了解就显得容易了。

iptables version 1.2.7

iptables有两条线:ipv4 和ipv6,这里只分析v4的,因为v6偶暂时还用不着,没有去看。

iptables_standardone.c
主函数:
int main(int argc, char *argv[])
{
int ret;
char *table = "filter"; /*默认的表是filter*/
iptc_handle_t handle = NULL;

program_name = "iptables";
program_version = IPTABLES_VERSION;

#ifdef NO_SHARED_LIBS
init_extensions();
#endif

/*进入命令行处理函数*/
ret = do_command(argc, argv, &table, &handle);
if (ret)
ret = iptc_commit(&handle);

if (!ret)
fprintf(stderr, "iptables: %s\n",
iptc_strerror(errno));

exit(!ret);
}
table表示表的名称,就是iptables -t 后面跟的那个,默认是"filter"
iptc_handle_t handle = NULL; 这个东东很重要,现在初始化NULL,后面他被用来存储一个表的任何规则的快照。
program_name = "iptables";
program_version = IPTABLES_VERSION;
配置名称和版本。
#ifdef NO_SHARED_LIBS
init_extensions();
#endif
iptables很多东东,是用共享库*.so的形式(我们安装会,能够在诸如/lib/iptables下边看到),假如不采用共享库,则进行一个初始化操作。我们假设是采用共享库的,忽略他。

然后就进入核心处理模块:
do_command(argc, argv, &table, &handle);

do_command 函数是整个系统的核心,负责处理整个用户的输入命令。函数首先对一些结构、变量进行初始化,初始化完毕后,进入while循环,分析用户输入的命令,配置相关的标志变量,然后根据相应标志,调用对应的处理函数。

struct ipt_entry fw, *e = NULL;
int invert = 0;
unsigned int nsaddrs = 0, ndaddrs = 0;
struct in_addr *saddrs = NULL, *daddrs = NULL;

int c, verbose = 0;
const char *chain = NULL;
const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
const char *policy = NULL, *newname = NULL;
unsigned int rulenum = 0, options = 0, command = 0;
const char *pcnt = NULL, *bcnt = NULL;
int ret = 1;
struct iptables_match *m;
struct iptables_target *target = NULL;
struct iptables_target *t;
const char *jumpto = "";
char *protocol = NULL;
const char *modprobe = NULL;

/*初始化变量*/
memset(&fw, 0, sizeof(fw));

opts = original_opts;
global_option_offset = 0;

/* re-set optind to 0 in case do_command gets called
* a second time */
optind = 0;

/*初始化两个全局变量*/
/* clear mflags in case do_command gets called a second time
* (we clear the global list of all matches for security)*/
for (m = iptables_matches; m; m = m->next) {
m->mflags = 0;
m->used = 0;
}

for (t = iptables_targets; t; t = t->next) {
t->tflags = 0;
t->used = 0;
}
ps:开头一大堆的变量定义和初始化,能够在程式分析的时候看他们的作用,有两个全局结构变量很重要:iptables_matches和iptables_targets。现在来分析他们的作用会有一点困难,因为他们涉及到了太多方面的东东,这里,能够先把他们“想像成”用户空间用来读取内核规则的结构(当然,这有点错误)。

/*开始化析命令行*/
while ((c = getopt_long(argc, argv,
"-A:C:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
opts, NULL)) != -1)
{
}

这个while循环处理任何的用户输入,对应规则输出-L,有:
case 'L':
add_command(&command, CMD_LIST, CMD_ZERO,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
&& argv[optind][0] != '!')
chain = argv[optind ];
break;

add_command函数负责将命令标志变量command和令标志 CMD_LIST求&运算, CMD_ZERO只是个附加的判断标志而已,invert);然后,从命令行中取得要显示的链名(假如有的话)。

和此相关的更有用t参数指定了表名:
case 't':
if (invert)
exit_error(PARAMETER_PROBLEM,
"unexpected ! flag before --table");
*table = argv[optind-1];
break;
即,假如有’t’参数,则取’t’后跟的表名:*table = argv[optind-1],否则,他应该是主函数中默认的filter表。

命令处理完毕后,即进入执行模块:
/*因为程式定义了共享库的话,iptables_matches/iptables_target这两个结构运行至此是NULL,并且target也是NULL,对于规则显示而言,这一部份的处理现在没有实际意义,回过头再来看这一段更易理解。final_check成员函数的作用是作最终的标志检查,假如检测失则,则退出*/

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!