- 论坛徽章:
- 36
|
二、命令行的核心处理
do_command()是处理Iptables命令的核心部分。
int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
(iptables.c)
1. 函数首先对一些结构、变量进行初始化。基本上涵盖了一条规则可能出现的大部分基本参数。其中,重要的结构体有struct ipt_entry fw, *e,这两个应该是存储一条防火墙规则的;struct iptables_match *m,struct iptables_target *target,*t;
分别用于存储match和target.
并将全局的match和target链表的对应flags和used为初始化为0.
2. 命令行解析。初始化完毕后,进入while循环,分析用户输入的命令,设置相关的标志变量,然后根据相应标志,调用对应的处理函数。这里是我们要进行详细分析的地方。
我们要分析的命令为:
iptabls –A INPUT –i eth0 –p tcp --syn –s 10.0.0.0/8 –d 10.1.28.184 –j ACCEPT
以下开始命令行解析:
(1) 处理 –A 选项
调用add_command函数,主要是对command变量进行逻辑处理。该函数的输入参数
newcmd = CMD_APPEND=0x0010,othercmds= CMD_NONE=0x0000,invert = 0;
输出参数command(初始值为0),
执行完该函数之后,
command = CMD_APPEND=0x0010,
chain = “INPUT”
然后程序break跳出switch语句,并将invert = FALSE,进行while的下一个循环,及处理下一个选项。
(2) 处理 –i 选项
调用check_inverse函数。该函数主要是检查-i对应的参数中是否使用了取反标志”!”,因此这里我们使用的是”eth0”,因此该函数直接返回FALSE。invert=0。
调用set_option函数是指对应的选项。该函数的输入参数option= OPT_VIANAMEOUT(0x0080), invert=0,输出参数options(初始化为0),fw.ip.invflags(初始化为0)。
经过该函数处理之后,
options = OPT_VIANAMEIN=0x0080;
fw.ip.invflags=0.
调用parse_interface函数进行网络接口的解析。该函数的参数输入参数argv[optind-1]=”eth0”, 输出参数fw.ip.iniface[IFNAMSIZ]={“”}, fw.ip.iniface_mask[IFNAMSIZ]={“”}, 其中IFNAMSIZ=15。
处理之后,
fw.ip.iniface[IFNAMSIZ]={“eth0”};
fw.ip.iniface_mask[IFNAMSIZ]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
fw.nfcache = NFC_IP_IF_IN = 0x0004;
(3) 处理 –p 选项
调用check_inverse函数,检查这里设置的协议参数是否使用了取反标志”!”。我们这里使用的是tcp,因此该函数直接返回FALSE。invert仍旧为0。
调用set_option函数是指对应的选项。可以参照(2)中该函数的处理方法。options 的初始值为0x0080,最终处理完毕之后
options |= OPT_PROTOCOL(0x0008) = 0x0088;
fw.ip.invflags=0
然后协议字串tcp全部转化为小写,protocol = “tcp”。调用parse_protocol函数将该协议字符串转换为TCP对应的协议号6,
fw.ip.proto = 6;
fw.nfcache |= NFC_IP_PROTO = 0x0004 | 0x0020 = 0x0024;
(4) 处理 --syn选项
由于该选项并不在全局变量opts里面,所以程序会进入switch语句的default分支执行。
进行该分支处理时一些变量的初始值:
target==NULL, m==NULL,
全局链表iptables_matches的used选项都为0
protocol = “tcp” ,options = 0x0088, proto_used = 0
根据以上参数的值,这里要执行的动作为加载该协议(m->init(), 即libipt_tcp.c中init()),并且将协议的一些选项扩展到全局变量opts里面。具体代码执行是进入该分支的if(m==NULL&&…)里面,具体执行的结果如下:
m指向全局match链表中tcp的match。(libipt_tcp.c)
proto_used = 1;
size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
m->m = fw_calloc(1, size); (m->m为内核总的match部分)
m->m->u.match_size = size;
m->m->u.user.name = “tcp”;
m->used = 1;
调用m->init(m->m, &fw.nfcache),执行init()(libipt_tcp.c)。仅是将match中的tcp的sport和dport的最大值置为0xFFFF. struct ipt_entry_match 中的data主要用于表示真正数据部分的开始,是通过fw_calloc为整个结构体以及data申请的内存,以后对数据的操作都可以通过data来索引。
init(struct ipt_entry_match *m, unsigned int *nfcache)
{
struct ipt_tcp *tcpinfo = (struct ipt_tcp *)m->data;
tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
}
merge_options将tcp的一些选项扩展到全局变量opts里面。这样就可以解析 --syn选项了。opts包含了tcp协议的选项参数。
并且全局变量global_option_offset += OPTION_OFFSET = 256;
m->option_offset= global_option_offset =256;
这里有点需要注意,就是新加到opts全局结构体中的部分,即tcp对应的option结构中所有的val成员都被赋值为:
merge[num_old + i].val += *option_offset; (见merge_options函数)
这里主要是为了区别和原先已有option中的val成员。而且在以后添加更多match模块的时候,都要做这样的动作。因此在命令行解析的时候,通过getopt_long得到的这些match中的命令行参数值的时候,需要先减去对应的m->option_offset,然后才能正确的parse.
然后两行代码: optind--;
continue;
是让程序进入下一个while循环,并再次处理--syn参数。因为以上的处理只是将tcp协议的match进行了初始化工作,并没有处理该参数。
因此,程序再次进入switch的default分支,不过这次是在if(!target&&…),由于对应tcp的match结构体的used被置1,即m->used =1; 因此程序要进行m->parse进行libipt_tcp中的parse()函数对命令行参数进行处理。该函数的第一个参数的使用方法上面已经解释过了。以下就是parse()函数对--syn的处理结果:
((struct ipt_tcp *)m->data)->flg_mask = 0x16(SYN,ACK,RST);
((struct ipt_tcp *)m->data)->flg_cmp = 0x02(SYN)
m->flags |= TCP_FLAGS = 0x04;
fw.nfcache |= NF_IP_TCP_FLAGS(0x0100) = 0x0024 | 0x0100=0x0124
至此,iptables对—syn选项的解析已经完成。
(5) 处理-s选项
同样还是check_inverse,set_option的处理,只有options变量改变;
options |= OPT_SOURCE(0x0002) = 0x0088 | 0x0002 = 0x008a;
shostnetworkmask= “10.0.0.0/24”;
fw.nfcache |= NFC_IP_SRC(0x0001)= 0x0124 | 0x0001 = 0x0125;
(6) 处理-d选项
同样还是check_inverse,set_option的处理,只有options变量改变;
options |= OPT_DESTINATION (0x0004) = 0x008a | 0x0004 = 0x008e;
dhostnetworkmask= “10.1.28.184”;
fw.nfcache |= NFC_IP_DST (0x0002)= 0x0125 | 0x0002 = 0x0127;
(7) 处理-j选项
首先还是set_option函数的处理:
options |= OPT_JUMP(0x0010) = 0x008e | 0x0010 = 0x009e;
然后jumpto =parse_target(“ACCEPT”),主要是检查一下该字符串是否合法;
接着target =find_target(jumpto, TRY_LOAD);该函数返回target为”standard”(包含了ACCEPT, DROP, QUEUE,RETURN等目标)的结构体指针。
并且: target->loaded = 1; target->used = 1
jumpto = “ACCEPT”
size = IPT_ALIGN(sizeof(struct ipt_entry_target))+ target->size;
target->t = fw_calloc(1, size); //分配内存给struct ipt_entry_target
target->t->u.target_size = size;
target->t->u.user.name = “ACCEPT”
这里的target->init指向了libipt_standard.c中的init(),该函数并未执行任何动作。
merge_options又是将该libipt_standard.c的 opts加入到全局的opts中。由于ACCEPT目标是标准的,这里实际上并未往全局的opts中添加任何内容。
仅修改了如下变量
global_option_offset += OPTION_OFFSET = 256+256=512;
target->option_offset= global_option_offset =512;
至此,命令行已经解析完毕,下面要接着对解析出来的各个参数进行进一步处理。
[ 本帖最后由 Godbach 于 2008-8-4 15:04 编辑 ] |
|