iptables 分析
2012-08-14 22:59
218 查看
http://blog.chinaunix.net/uid-24207747-id-2622900.html
iptables 分析(一) (2010-11-10 12:17)
分类:
iptables
****************
v1.4.1
****************
iptables 是用户空间中用于管理包过滤及NAT 等的工具应用程序。它设置防火墙的过滤规则,并将规则添加到内核空间的特定信息包过滤表内的链中,通过netfilter框架的hook
函数完成对数据包的过滤工作。它还可以设置nat 表的规则实现IP 地址及端口转换,设置mangle 表的规则改变IP 头部信息实现对IP 包更高级的控制.
基本语法:
iptables -t table -Operation chain matching-criteria
-j target
iptables 命令中各参数说明如下: iptables 命令使用选项-t table 选择规则表。
规则表包含处理特定类型信息包的规则和链的信息。规则表选项有:filter、nat 和 mangle.默认是filter。
1)filter(过滤器) 表用于一般的信息包过滤,它包含INPUT、OUTPUT 和 FORWARD 规则链.
2)nat(地址转换) 表用于要转发的信息包,它包含PREROUTING、OUTPUT 和POSTROUTING 规则链。
3)mangle(包处理) 规则表包含PREROUTING 和 OUTPUT 链。每条规则链上有许多规则,通过规则表和hook 点规则链可以找到规则。
-Operation:对规则链的某行规则添加,删除,修改参数(-A:D:C)
chain
:规则链名,有INPUT,OUTPUT,FORWARD.
matching-criteria:匹配,可选项match 部分指定信息包与规则匹配特征,如源和目的地址、协议.
target:动作目标,(-j target)如规则匹配后,执行的相关动作由目标指定,目标基本动作有: ACCEPT 接收该数据包。
DROP 丢弃该数据包。
REJECT 不仅丢弃数据包,还返回给发送者一个可配置的错误信息。
RETURN 让与该规则匹配的信息包停止遍历包含该规则的链,返回到前面调用的链。
如果链是如INPUT 之类的主链,则使用该链的缺省策略处理信息包
要分析源码先学会iptabes工具如何使用,看一下它命令help手册,对要分析源码有帮助.执行iptable -h 有:
进入main函数之前,先看分析几个重要的数据结构,这对看理解源码很有帮助:
哎,有些结构体必需结构netfiter来看,还找些资料,结构体注解就到这吧,正式进入源码吧,哈哈.
主函数二步工作:1.do_command接受用户输入的命令规则,根据输入的表名,将规则存到结构iptc_handle 2.iptc_commit通过系统调用setsockopt将规则设置到Linux
的netfilter 模块的规则表中.
进入do_command,看下一篇。
iptables 分析(二) (2010-11-10 14:04)
分类:
iptables
接下来还是根据一条规则来分析源码,这样比较直观.
命令: iptables -t filter -A INPUT -j ACCEPT
意思:是在filter 表的INPUT 链里追加一条规则(作为最后一条规则)
匹配所有访问本机IP 的数据包,匹配到的接收.
接受用户输入规则,getopt_long函数用来分析命令行选项,-t,-A,-j调用对应开关语句,getopt_long原型:
int
getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
=====================================
optstring:短选项字符串,如ho:v::a,表示有效选项是-h,-o与-v并且第二个参数-o后面需要一个参数,v后接参数不能空格.
longopts: struct option结构
struct option {
const char *name; //长选项的名字
int has_arg; //长选项参数值个数
int val; //长选项对应的短选项字符
};
longindex: 如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。
=======================================
-t 后面必有一参数,表名其为filter,把它保存到*table.
-A 后面必有一参数,链名保存在chain,add_command函数用来添加CMD_APPEND命令宏保存到command,以备后使这个命令宏来执行这个追加动作.代码:
-j 后面必有一参数,set_option函数首先将j对应的宏OPT_JUMP添加到options,OPT_JUMP在代码中就代表用户的j命令了:
获取-j后面所跟参数,也就是其目标名,解析目标名判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况,返回正确的字符串保存到jumpto:
字符串目标名正确,从全局的目标链表xptables_targets 中查找这个目标名,设置尝试加载目标标识:
iptables 分析(三) (2010-11-10 17:36)
分类:
iptables
接上篇文章,find_target查到目标并加载成功,返回一个xtables_target型对象保存在target,分配空间,拷贝目标,初始化target对象。
接下来看个比较重要的函数iptc_init来从内核获取表的规则信息,tables就是是-t保存的表名
进入iptc_init:
iptables与内核的交互,都是使用setsockopt函数(设置与某个套接字关联的选 项)来实现的,创建socket,通过getsockopt函数是获取与对应的套接字关联的选项,其标志位是SO_GET_INFO,获取保存在STRUCT_GETINFO类型info里,STRUCT_GETINFO结构也就是#define
STRUCT_GETINFO struct ipt_getinfo,最后getsockopt返回规则信息是ipt_getinfo型而TC_INIT函数返回一个具体的规则表信息iptc_handle型.
未完...
iptables 分析(一) (2010-11-10 12:17)
分类:
iptables
****************
v1.4.1
****************
iptables 是用户空间中用于管理包过滤及NAT 等的工具应用程序。它设置防火墙的过滤规则,并将规则添加到内核空间的特定信息包过滤表内的链中,通过netfilter框架的hook
函数完成对数据包的过滤工作。它还可以设置nat 表的规则实现IP 地址及端口转换,设置mangle 表的规则改变IP 头部信息实现对IP 包更高级的控制.
基本语法:
iptables -t table -Operation chain matching-criteria
-j target
iptables 命令中各参数说明如下: iptables 命令使用选项-t table 选择规则表。
规则表包含处理特定类型信息包的规则和链的信息。规则表选项有:filter、nat 和 mangle.默认是filter。
1)filter(过滤器) 表用于一般的信息包过滤,它包含INPUT、OUTPUT 和 FORWARD 规则链.
2)nat(地址转换) 表用于要转发的信息包,它包含PREROUTING、OUTPUT 和POSTROUTING 规则链。
3)mangle(包处理) 规则表包含PREROUTING 和 OUTPUT 链。每条规则链上有许多规则,通过规则表和hook 点规则链可以找到规则。
-Operation:对规则链的某行规则添加,删除,修改参数(-A:D:C)
chain
:规则链名,有INPUT,OUTPUT,FORWARD.
matching-criteria:匹配,可选项match 部分指定信息包与规则匹配特征,如源和目的地址、协议.
target:动作目标,(-j target)如规则匹配后,执行的相关动作由目标指定,目标基本动作有: ACCEPT 接收该数据包。
DROP 丢弃该数据包。
REJECT 不仅丢弃数据包,还返回给发送者一个可配置的错误信息。
RETURN 让与该规则匹配的信息包停止遍历包含该规则的链,返回到前面调用的链。
如果链是如INPUT 之类的主链,则使用该链的缺省策略处理信息包
要分析源码先学会iptabes工具如何使用,看一下它命令help手册,对要分析源码有帮助.执行iptable -h 有:
iptables v1.4.1.1 Usage: iptables -[AD] chain rule-specification [options] iptables -[RI] chain rulenum rule-specification [options] //用iptables - RI 通过规则的顺序指定 iptables -D chain rulenum [options] //删除指定规则 iptables -[LS] [chain [rulenum]] [options] //用iptables -LFZ 链名 [选项] iptables -[FZ] [chain] [options] iptables -[NX] chain //用 -NX 指定链 iptables -E old-chain-name new-chain-name //-E 用新的链名取代旧的链名 iptables -P chain target [options] //指定链的默认目标 iptables -h (print this help information) //帮助 //这些选项指定执行明确的动作:若指令行下没有其他规定,该行只能指定一个选项.对于长格式的命令和选项名,所用字母长度只要保证iptables能从其他选项中区分出该指令就行了 Commands: Either long or short options are allowed. --append -A chain Append to chain //在所选择的链末添加一条或更多规则 --delete -D chain Delete matching rule from chain //从所选链中删除一条或更多规则 --delete -D chain rulenum //从所选链中删除一条或更多规则。这条命令可以有两种方法:可以把被删除规则指定为链中的序号(第一条序号为1),或者指定为要匹配的规则 Delete rule rulenum (1 = first) from chain --insert -I chain [rulenum] //根据给出的规则序号向所选链中插入一条或更多规则。所以,如果规则序号为1,规则会被插入链的头部。这也是不指定规则序号时的默认方式。 Insert in chain as rulenum (default 1=first) --replace -R chain rulenum //从选中的链中取代一条规则,如果源(地址)或者/与 目的(地址)被转换为多地址,该命令会失败。规则序号从1开始 Replace rule rulenum (1 = first) in chain --list -L [chain [rulenum]] //显示所选链的所有规则。如果没有选择链,所有链将被显示。也可以和z选项一起使用,这时链会被自动列出和归零。精确输出受其它所给参数影响 List the rules in a chain or all chains --list-rules -S [chain [rulenum]] Print the rules in a chain or all chains --flush -F [chain] Delete all rules in chain or all chains //清空所选链。这等于把所有规则一个个的删除 --zero -Z [chain] Zero counters in chain or all chains //把所有链的包及字节的计数器清空。它可以和 -L配合使用,在清空前察看计数器,请参见前文。 --new -N chain Create a new user-defined chain //根据给出的名称建立一个新的用户定义链。这必须保证没有同名的链存在 --delete-chain //删除指定的用户自定义链。这个链必须没有被引用,如果被引用,在删除之前你必须删除或者替换与之有关的规则。如果没有给出参数,这条命令将试着删除每个非内建的链。 -X [chain] Delete a user-defined chain //清除mangle表中,所有规则链中的规则 --policy -P chain target //设置链的目标规则 Change policy on chain to target --rename-chain -E old-chain new-chain //根据用户给出的名字对指定链进行重命名,这仅仅是修饰,对整个表的结构没有影响。TARGETS参数给出一个合法的目标。只有非用户自定义链可以使用规则,而且内建链和用户自定义链都不能是规则的目标 Change chain name, (moving any references) //这些可被iptables识别的选项可以区分不同的种类 Options: --proto -p [!] proto protocol: by number or name, eg. 'tcp'//规则或者包检查(待检查包)的协议。指定协议可以是tcp、udp、icmp中的一个或者全部,也可以是数值,代表这些协议中的某一个。当然也可以使用在/etc/protocols中定义的协议名。在协议名前加上"!"表示相反的规则。数字0相当于所有all。Protocol all会匹配所有协议,而且这是缺省时的选项。在和check命令结合时,all可以不被使用。 --source -s [!] address[/mask] //指定源地址,可以是主机名、网络名和清楚的IP地址。mask说明可以是网络掩码或清楚的数字,在网络掩码的左边指定网络掩码左边"1"的个数,因此,mask值为24等于255.255.255.0。在指定地址前加上"!"说明指定了相反的地址段。标志 --src 是这个选项的简写。 source specification --destination -d [!] address[/mask] //指定目标地址,要获取详细说明请参见 -s标志的说明 destination specification --in-interface -i [!] input name[+] network interface name ([+] for wildcard) --jump -j target //目标跳转,指定规则的目标;也就是说,如果包匹配应当做什么。目标可以是用户自定义链(不是这条规则所在的),某个会立即决定包的命运的专用内建目标,或者一个扩展(参见下面的EXTENSIONS)。如果规则的这个选项被忽略,那么匹配的过程不会对包产生影响,不过规则的计数器会增加。 target for rule (may load target extension) --goto -g chain jump to chain with no return --match -m match extended match (may load extension) --numeric -n numeric output of addresses and ports --out-interface -o [!] output name[+] //输出接口[名称],这是包经由该接口送出的可选的出口名称,包通过该口输出(在链FORWARD、OUTPUT和POSTROUTING中送出的包)。当在接口名前使用"!"说明后,指的是相反的名称。如果接口名后面加上"+",则所有以此接口名开头的接口都会被匹配。如果这个选项被忽略,会假设为"+",那么将匹配所有任意接口。 network interface name ([+] for wildcard) --table -t table table to manipulate (default: 'filter') //指定表名 --verbose -v verbose mode --line-numbers print line numbers when listing --exact -x expand numbers (display exact values) [!] --fragment -f match second or further fragments only //这意味着在分片的包中,规则只询问第二及以后的片。自那以后由于无法判断这种把包的源端口或目标端口(或者是ICMP类型的),这类包将不能匹配任何指定对他们进行匹配的规则。如果"!"说明用在了"-f"标志之前,表示相反的意思。 --modprobe=<command> try to insert modules using this command --set-counters PKTS BYTES set the counter during insert/append [!] --version -V print package version. |
iptables 工具还把规则表存储在结构iptc_handle 的变量中,然后,再与内核交互得到或设置规 则表信息。结构iptc_handle 指向一个具体的规则表,表信息从内核中的filter、nat 等表提取. 结构如下: typedef struct iptc_handle *iptc_handle_t; #define STRUCT_TC_HANDLE struct iptc_handle STRUCT_TC_HANDLE { int changed; //是否有变化 struct list_head chains; //规则链 struct chain_head *chain_iterator_cur; //当前规则链 struct rule_head *rule_iterator_cur; //当前规则 unsigned int num_chains; //用户定义的链数 struct chain_head **chain_index; /* array for fast chain list access*/ unsigned int chain_index_sz; /* size of chain index array */ STRUCT_GETINFO info; //对应内核结构ipt_getinfo,包含hook 相关信息 STRUCT_GET_ENTRIES *entries; //规则链的规则条目,表对应内核中结构 }; //存储规则 struct rule_head { struct list_head list; struct chain_head *chain; struct counter_map counter_map; unsigned int index; /* index (needed for counter_map) */ unsigned int offset; /* offset in rule blob */ enum iptcc_rule_type type; struct chain_head *jump; /* jump target, if IPTCC_R_JUMP */ unsigned int size; /* size of entry data */ STRUCT_ENTRY entry[0]; }; //存储规则链 struct chain_head { struct list_head list; char name[TABLE_MAXNAMELEN]; unsigned int hooknum; /* hook number+1 if builtin */ unsigned int references; /* how many jumps reference us */ int verdict; /* verdict if builtin */ STRUCT_COUNTERS counters; /* per-chain counters */ struct counter_map counter_map; unsigned int num_rules; /* number of rules in list */ struct list_head rules; /* list of rules */ unsigned int index; /* index (needed for jump resolval) */ unsigned int head_offset; /* offset in rule blob */ unsigned int foot_index; /* index (needed for counter_map) */ unsigned int foot_offset; /* offset in rule blob */ }; ---------------------------------------------------------------------------------------- struct iptables_match *iptables_matches = NULL; struct iptables_target *iptables_targets = NULL; 1.iptables_match 存储了扩展匹配的操作函数指针,如:扩展匹配模块的初始化、选项分析、检查及帮助等函数,它还包括了结构ipt_entry_match 成员指针,而ipt_entry_match 存储了扩展匹配的条目名及内核使用的匹配操作指针等.ipt_entry_match 将用户空间与内核空间的匹配结构连接起来 2.iptables_target 存储了扩展目标与选项-j 相关的操作函数指针,如:初始化、选项分析、检查及帮助等函数。它在还包括了结构ipt_entry_target 成员,而ipt_entry_target 存储了扩展目标的条目名及内核空间使用的目标操作指针等信息.同上 struct iptables_match { struct iptables_match *next; ipt_chainlabel name; //链名 u_int8_t revision; //匹配模块的版本序号,缺省值为0 const char *version; //版本字符串 size_t size; //匹配的数据大小 size_t userspacesize; //匹配的数据大小,用于在用户空间进行比较 void (*help)(void); //打印使用信息 void (*init)(struct ipt_entry_match *m, unsigned int *nfcache); //初始化匹配 int (*parse)(int c, char **argv, int invert, unsigned int *flags,//分析命令行选项,如果分析完一个选项,返回true const struct ipt_entry *entry, unsigned int *nfcache, struct ipt_entry_match **match); void (*final_check)(unsigned int flags); //对匹配最终的检查,检查出不正确就退出 void (*print)(const struct ipt_ip *ip, const struct ipt_entry_match *match, intumeric); //打印出匹配信息,放置NULL 在字符串末尾。 void (*save)(const struct ipt_ip *ip, const struct ipt_entry_match *match); //存储分析出的匹配信息到标准输出stdout const struct option *extra_opts; //指向附加命令行选项链表 unsigned int option_offset; struct ipt_entry_match *m; //内核中匹配的操作函数及信息 unsigned int mflags; }; struct ipt_entry_match { union { struct { u_int16_t match_size; //匹配的大小 char name[IPT_FUNCTION_MAXNAMELEN-1]; //用户空间使用的匹配名 u_int8_t revision; //版本序号 } user; //用户空间匹配条目信息 struct { u_int16_t match_size; //匹配大小 struct ipt_match *match; //内核空间使用的匹配结构 } kernel; //内核空间使用的匹配结构信息 u_int16_t match_size; //匹配的总大小 } u; unsigned char data[0]; }; 结构 xt_match 定义了扩展匹配的内核模块的操作函数集,其列出如下: #define ipt_match xt_match struct xt_match { struct list_head list; const char name[XT_FUNCTION_MAXNAMELEN-1]; //匹配名 u_int8_t revision; //匹配的实现函数 int (*match)(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, unsigned int protoff, int *hotdrop); //当用户插入一个条目到这个类型时调用这个函数,返回true 或false int (*checkentry)(const char *tablename, const void *ip, void *matchinfo, unsigned int matchinfosize, unsigned int hook_mask); void (*destroy)(void *matchinfo, unsigned int matchinfosize); //当这个类型的条目被删除时调用 struct modules *me; //如果是内核模块,设置到指针THIS_MODULE,否则为NULL }; struct option { const char *name; //长选项的名字 int has_arg; //长选项参数值个数 int *flag; //NULL 时,getopt_long()返回val,否则返回0 int val; //长选项对应的短选项字符 }; ---------------------------------------------------------------------------------------- |
int main(int argc, char *argv[]) { int ret; char *table = "filter"; //默认表filter iptc_handle_t handle = NULL; //用来储存表的所有规则 program_name = "iptables"; program_version = XTABLES_VERSION; lib_dir = getenv("XTABLES_LIBDIR"); //得到环境变量 if (lib_dir == NULL) { lib_dir = getenv("IPTABLES_LIB_DIR"); if (lib_dir != NULL) fprintf(stderr, "IPTABLES_LIB_DIR is deprecated\n"); } if (lib_dir == NULL) lib_dir = XTABLES_LIBDIR; #ifdef NO_SHARED_LIBS init_extensions(); //设置版本号 #endif ret = do_command(argc, argv, &table, &handle); //分析命令选项,将规则存入handle if (ret) ret = iptc_commit(&handle); //通过系统调用setsockopt 将规则设置到Linux 的netfilter 模块的规则表中 if (!ret) { fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); if (errno == EAGAIN) { exit(RESOURCE_PROBLEM); } } exit(!ret); } |
的netfilter 模块的规则表中.
进入do_command,看下一篇。
iptables 分析(二) (2010-11-10 14:04)
分类:
iptables
//负责整个用户输入的命令处理 int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) { //初始化变量 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 xtables_match *m;//扩展匹配操作函数指针 struct iptables_rule_match *matches = NULL; struct iptables_rule_match *matchp; struct xtables_target *target = NULL; struct xtables_target *t;//扩展目标操作函数指针 const char *jumpto = ""; char *protocol = NULL; int proto_used = 0; unsigned long long cnt; memset(&fw, 0, sizeof(fw)); /*extern char *optarg; //选项的参数指针 extern int optind; //下一次调用getopt时,从optind存储的位置处重新开始检查选择项. extern int opterr; //当为0时,不向stderr输出错误信息. */ optind = 0; /* 清除标识mflags,以免do_command 被第二次调用,为了安全,清除了所有匹 配的链表*/ for (m = xtables_matches; m; m = m->next) m->mflags = 0; for (t = xtables_targets; t; t = t->next) { t->tflags = 0; t->used = 0; } /* Suppress error messages: we may add new options if we demand-load a protocol. */ opterr = 0; //禁止错误信息显示 //分析命令行选项,返回短选项字母,错误返回-1 while ((c = getopt_long(argc, argv, "-A:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:", //例ho:v::a,表示有效选项是-h,-o与-v并且第二个参数-o后面需要一个参数,v后接参数不能空格. opts, NULL)) != -1) { switch (c) { case 'A'://追加一条规则到规则链,如:iptables -A INPUT -j ACCEPT add_command(&command, CMD_APPEND, CMD_NONE, invert); //该函数用来添加命令宏 chain = optarg; //得到-A 后参数(链名) break; case 'D': //删除规则链中某条规则 add_command(&command, CMD_DELETE, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') { rulenum = parse_rulenumber(argv[optind++]); //把数字字符串转为数字 command = CMD_DELETE_NUM; } break; case 'R': add_command(&command, CMD_REPLACE, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); else exit_error(PARAMETER_PROBLEM, "-%c requires a rule number", cmd2char(CMD_REPLACE)); break; case 'I': add_command(&command, CMD_INSERT, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); else rulenum = 1; break; 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++]; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); break; case 'S': add_command(&command, CMD_LIST_RULES, CMD_ZERO, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') rulenum = parse_rulenumber(argv[optind++]); break; case 'F': add_command(&command, CMD_FLUSH, CMD_NONE, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'Z': add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'N': if (optarg && (*optarg == '-' || *optarg == '!')) exit_error(PARAMETER_PROBLEM, "chain name not allowed to start " "with `%c'\n", *optarg); if (find_target(optarg, TRY_LOAD)) exit_error(PARAMETER_PROBLEM, "chain name may not clash " "with target name\n"); add_command(&command, CMD_NEW_CHAIN, CMD_NONE, invert); chain = optarg; break; case 'X': add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, invert); if (optarg) chain = optarg; else if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') chain = argv[optind++]; break; case 'E': add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, invert); chain = optarg; if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') newname = argv[optind++]; else exit_error(PARAMETER_PROBLEM, "-%c requires old-chain-name and " "new-chain-name", cmd2char(CMD_RENAME_CHAIN)); break; case 'P': //为规则链(INPUT、OUTPUT 和FORWARD)定义一默认策略 add_command(&command, CMD_SET_POLICY, CMD_NONE, invert); chain = optarg; //链名 if (optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') policy = argv[optind++];//得到策略,如iptables -P INPUT ACCEPT else exit_error(PARAMETER_PROBLEM, "-%c requires a chain and a policy", cmd2char(CMD_SET_POLICY)); break; case 'h': if (!optarg) optarg = argv[optind]; /* iptables -p icmp -h */ if (!matches && protocol) find_match(protocol, TRY_LOAD, &matches); exit_printhelp(matches); case 'p': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_PROTOCOL, &fw.ip.invflags, invert); /* Canonicalize into lower case */ for (protocol = argv[optind-1]; *protocol; protocol++) *protocol = tolower(*protocol); protocol = argv[optind-1]; fw.ip.proto = parse_protocol(protocol); if (fw.ip.proto == 0 && (fw.ip.invflags & IPT_INV_PROTO)) exit_error(PARAMETER_PROBLEM, "rule would never match protocol"); break; case 's': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_SOURCE, &fw.ip.invflags, invert); shostnetworkmask = argv[optind-1]; break; case 'd': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_DESTINATION, &fw.ip.invflags, invert); dhostnetworkmask = argv[optind-1]; break; #ifdef IPT_F_GOTO case 'g': set_option(&options, OPT_JUMP, &fw.ip.invflags, invert); fw.ip.flags |= IPT_F_GOTO; jumpto = parse_target(optarg); break; #endif case 'j': // -j ACCEPT //将j 选项对应的宏定义位OPT_JUMP 加到options 上,invert 表示非逻辑操作 set_option(&options, OPT_JUMP, &fw.ip.invflags, invert); jumpto = parse_target(optarg); //解析目标名是否正确 target = find_target(jumpto, TRY_LOAD); //正确,从全局的目标链表xptables_targets 中查找这个目标,设置尝试加载目标标识 if (target) { //成功找到 size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + target->size; target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); //保存目标名到xptables_targets set_revision(target->t->u.user.name, target->revision); //设置版本 if (target->init != NULL) target->init(target->t); //初始化xptables_targets opts = merge_options(opts, target->extra_opts, &target->option_offset);//将target 的参数选项与旧的参数选项连接在一起由opts 返回,这样下一个循环可以分析target 的参数选项,一般在“default:”中进行分析 if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } break; case 'i': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags, invert); parse_interface(argv[optind-1], fw.ip.iniface, fw.ip.iniface_mask); break; case 'o': check_inverse(optarg, &invert, &optind, argc); set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags, invert); parse_interface(argv[optind-1], fw.ip.outiface, fw.ip.outiface_mask); break; case 'f': set_option(&options, OPT_FRAGMENT, &fw.ip.invflags, invert); fw.ip.flags |= IPT_F_FRAG; break; case 'v': if (!verbose) set_option(&options, OPT_VERBOSE, &fw.ip.invflags, invert); verbose++; break; case 'm': {//可扩展选项s size_t size; if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --match"); m = find_match(optarg, LOAD_MUST_SUCCEED, &matches);//从全局链表xptables_match 中查找匹配模块 size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; m->m = fw_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); set_revision(m->m->u.user.name, m->revision); if (m->init != NULL) m->init(m->m); //初始化 if (m != m->next) { /* Merge options for non-cloned matches */ opts = merge_options(opts, m->extra_opts, &m->option_offset);//将扩展匹配的选项加入到全局选项表opts,下一个循环就可以解析它的选项了 if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } } break; case 'n': set_option(&options, OPT_NUMERIC, &fw.ip.invflags, invert); break; case 't': if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --table"); *table = argv[optind-1]; //取t 后面的表名 break; case 'x': set_option(&options, OPT_EXPANDED, &fw.ip.invflags, invert); break; case 'V': if (invert) printf("Not %s ;-)\n", program_version); else printf("%s v%s\n", program_name, program_version); exit(0); case '0': set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags, invert); break; case 'M': modprobe_program = optarg; break; case 'c': set_option(&options, OPT_COUNTERS, &fw.ip.invflags, invert); pcnt = optarg; bcnt = strchr(pcnt + 1, ','); if (bcnt) bcnt++; if (!bcnt && optind < argc && argv[optind][0] != '-' && argv[optind][0] != '!') bcnt = argv[optind++]; if (!bcnt) exit_error(PARAMETER_PROBLEM, "-%c requires packet and byte counter", opt2char(OPT_COUNTERS)); if (sscanf(pcnt, "%llu", &cnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c packet counter not numeric", opt2char(OPT_COUNTERS)); fw.counters.pcnt = cnt; if (sscanf(bcnt, "%llu", &cnt) != 1) exit_error(PARAMETER_PROBLEM, "-%c byte counter not numeric", opt2char(OPT_COUNTERS)); fw.counters.bcnt = cnt; break; case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { if (invert) exit_error(PARAMETER_PROBLEM, "multiple consecutive ! not" " allowed"); invert = TRUE; optarg[0] = '\0'; continue; } fprintf(stderr, "Bad argument `%s'\n", optarg); exit_tryhelp(2); default://分析扩展目标和扩展匹配的选项 if (!target || !(target->parse(c - target->option_offset, //调用扩展目标的选项分析函数 argv, invert, &target->tflags, &fw, &target->t))) { for (matchp = matches; matchp; matchp = matchp->next) { if (matchp->completed) continue; if (matchp->match->parse(c - matchp->match->option_offset, argv, invert, &matchp->match->mflags, &fw, &matchp->match->m)) break; } m = matchp ? matchp->match : NULL; if (m == NULL //如果匹配不存在,通过协议查找扩展匹配,由扩展匹配分析选项 && protocol && (!find_proto(protocol, DONT_LOAD, options&OPT_NUMERIC, NULL) || (find_proto(protocol, DONT_LOAD, options&OPT_NUMERIC, NULL) && (proto_used == 0)) ) && (m = find_proto(protocol, TRY_LOAD, options&OPT_NUMERIC, &matches))) { //匹配成功 /* Try loading protocol */ size_t size; proto_used = 1; size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; m->m = fw_calloc(1, size); m->m->u.match_size = size; strcpy(m->m->u.user.name, m->name); set_revision(m->m->u.user.name, m->revision); if (m->init != NULL) m->init(m->m); opts = merge_options(opts, m->extra_opts, &m->option_offset); if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); optind--; continue; } if (!m) exit_error(PARAMETER_PROBLEM, "Unknown arg `%s'", argv[optind-1]); } } invert = FALSE; } //final_check成员函数的作用是作最终的标志检查,如果检测失则,则退出 for (matchp = matches; matchp; matchp = matchp->next) if (matchp->match->final_check != NULL)//使用扩展匹配的函数检查标识 matchp->match->final_check(matchp->match->mflags); if (target != NULL && target->final_check != NULL) target->final_check(target->tflags); /* Fix me: must put inverse options checking here --MN 接着对参数作一些必要的合法性检查 */ if (optind < argc) exit_error(PARAMETER_PROBLEM, "unknown arguments found on commandline"); if (!command) exit_error(PARAMETER_PROBLEM, "no command specified"); if (invert) exit_error(PARAMETER_PROBLEM, "nothing appropriate following !"); //如果没有设置来源/目的地址及掩码,则给予它们一个默认值 if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { if (!(options & OPT_DESTINATION)) //目的 dhostnetworkmask = "0.0.0.0/0"; if (!(options & OPT_SOURCE)) //源 shostnetworkmask = "0.0.0.0/0"; } /*对来源/目的地址及掩码进行拆分,它们总是以 addr/mask的形式来出现的,根据’/’前面的字符串取得地址值,根据’/’后面的掩码位数,求得正确的掩码值,值得注意的是,同时要处理主机地址和网络地址的情况*/ if (shostnetworkmask) ipparse_hostnetworkmask(shostnetworkmask, &saddrs, &fw.ip.smsk, &nsaddrs); if (dhostnetworkmask) ipparse_hostnetworkmask(dhostnetworkmask, &daddrs, &fw.ip.dmsk, &ndaddrs); /*然后检查来源/目的网络地址的合法性*/ if ((nsaddrs > 1 || ndaddrs > 1) && (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" " source or destination IP addresses"); if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) exit_error(PARAMETER_PROBLEM, "Replacement rule does not " "specify a unique address"); generic_opt_check(command, options); //检查命令选项有效 if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN) exit_error(PARAMETER_PROBLEM, "chain name `%s' too long (must be under %i chars)", chain, IPT_FUNCTION_MAXNAMELEN); /*handle,是一个指向了具体表,如filter、nat表的句柄,这里判断,如果handle为空,则调用iptc_init,根据table的名称,让handle指针指向相应的表的地址空间,也就是把对应表的所有信息从内核中取出来*/ if (!*handle) *handle = iptc_init(*table); //调用 iptc_init获取表的规则信息,调用list_entries函数显示规则 /* try to insmod the module if iptc_init failed */ if (!*handle && load_xtables_ko(modprobe_program, 0) != -1) *handle = iptc_init(*table); if (!*handle) exit_error(VERSION_PROBLEM, "can't initialize iptables table `%s': %s", *table, iptc_strerror(errno)); if (command == CMD_APPEND || command == CMD_DELETE || command == CMD_INSERT || command == CMD_REPLACE) { if (strcmp(chain, "PREROUTING") == 0 || strcmp(chain, "INPUT") == 0) { /* -o not valid with incoming packets. */ if (options & OPT_VIANAMEOUT) exit_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEOUT), chain); } if (strcmp(chain, "POSTROUTING") == 0 || strcmp(chain, "OUTPUT") == 0) { /* -i not valid with outgoing packets */ if (options & OPT_VIANAMEIN) exit_error(PARAMETER_PROBLEM, "Can't use -%c with %s\n", opt2char(OPT_VIANAMEIN), chain); } if (target && iptc_is_chain(jumpto, *handle)) { //jumpto链名 fprintf(stderr, "Warning: using chain %s, not extension\n", jumpto); if (target->t) free(target->t); target = NULL; } if (!target && (strlen(jumpto) == 0 || iptc_is_chain(jumpto, *handle))) { //如果没有指定目标或没有指定链名,使用标准的 size_t size; target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); //找到加载内核 size = sizeof(struct ipt_entry_target) + target->size; target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); if (!iptc_is_chain(jumpto, *handle)) set_revision(target->t->u.user.name, target->revision); if (target->init != NULL) target->init(target->t); } if (!target) {//扩展目标不存在 #ifdef IPT_F_GOTO if (fw.ip.flags & IPT_F_GOTO) exit_error(PARAMETER_PROBLEM, "goto '%s' is not a chain\n", jumpto); #endif find_target(jumpto, LOAD_MUST_SUCCEED); } else {//将扩展目标加入到规则条目中 e = generate_entry(&fw, matches, target->t); free(target->t); } } /* 根据命令标志,增加、显示规则*/ switch (command) { case CMD_APPEND: ret = append_entry(chain, e, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_DELETE: ret = delete_entry(chain, e, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, handle, matches); break; case CMD_DELETE_NUM: ret = iptc_delete_num_entry(chain, rulenum - 1, handle); break; case CMD_REPLACE: ret = replace_entry(chain, e, rulenum - 1, saddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_INSERT: ret = insert_entry(chain, e, rulenum - 1, nsaddrs, saddrs, ndaddrs, daddrs, options&OPT_VERBOSE, handle); break; case CMD_FLUSH: ret = flush_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_ZERO: ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_LIST: case CMD_LIST|CMD_ZERO: //list_entries是规则显示的主要处理函数 ret = list_entries(chain, rulenum, options&OPT_VERBOSE, options&OPT_NUMERIC, options&OPT_EXPANDED, options&OPT_LINENUMBERS, handle); if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_LIST_RULES: case CMD_LIST_RULES|CMD_ZERO: ret = list_rules(chain, rulenum, options&OPT_VERBOSE, handle); if (ret && (command & CMD_ZERO)) ret = zero_entries(chain, options&OPT_VERBOSE, handle); break; case CMD_NEW_CHAIN: ret = iptc_create_chain(chain, handle); break; case CMD_DELETE_CHAIN: ret = delete_chain(chain, options&OPT_VERBOSE, handle); break; case CMD_RENAME_CHAIN: ret = iptc_rename_chain(chain, newname, handle); break; case CMD_SET_POLICY: ret = iptc_set_policy(chain, policy, options&OPT_COUNTERS ? &fw.counters : NULL, handle); break; default: /* We should never reach this... */ exit_tryhelp(2); } if (verbose > 1) dump_entries(*handle); clear_rule_matches(&matches); if (e != NULL) { free(e); e = NULL; } free(saddrs); free(daddrs); free_opts(1); return ret; } |
命令: iptables -t filter -A INPUT -j ACCEPT
意思:是在filter 表的INPUT 链里追加一条规则(作为最后一条规则)
匹配所有访问本机IP 的数据包,匹配到的接收.
while ((c = getopt_long(argc, argv, "-A:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:", opts, NULL)) != -1) { switch (c) { .............. case 't': if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag before --table"); *table = argv[optind-1]; break; case 'A': add_command(&command, CMD_APPEND, CMD_NONE, invert); chain = optarg; break; case 'j': set_option(&options, OPT_JUMP, &fw.ip.invflags, invert); jumpto = parse_target(optarg); target = find_target(jumpto, TRY_LOAD); if (target) { //成功找到 size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + target->size; target->t = fw_calloc(1, size); target->t->u.target_size = size; strcpy(target->t->u.user.name, jumpto); set_revision(target->t->u.user.name, target->revision); if (target->init != NULL) target->init(target->t); opts = merge_options(opts, target->extra_opts, &target->option_offset); if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } break; ...................... |
int
getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);
=====================================
optstring:短选项字符串,如ho:v::a,表示有效选项是-h,-o与-v并且第二个参数-o后面需要一个参数,v后接参数不能空格.
longopts: struct option结构
struct option {
const char *name; //长选项的名字
int has_arg; //长选项参数值个数
int val; //长选项对应的短选项字符
};
longindex: 如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。
=======================================
-t 后面必有一参数,表名其为filter,把它保存到*table.
-A 后面必有一参数,链名保存在chain,add_command函数用来添加CMD_APPEND命令宏保存到command,以备后使这个命令宏来执行这个追加动作.代码:
static void add_command(unsigned int *cmd, const int newcmd, const int othercmds, int invert) { if (invert) exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); if (*cmd & (~othercmds)) exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); *cmd |= newcmd; } |
static void set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, int invert) { if (*options & option) exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", opt2char(option)); *options |= option;//添加宏 if (invert) { unsigned int i; for (i = 0; 1 << i != option; i++); if (!inverse_for_options[i]) exit_error(PARAMETER_PROBLEM, "cannot have ! before -%c", opt2char(option)); *invflg |= inverse_for_options[i]; } } |
static const char * parse_target(const char *targetname) //例如:ACCEPT { const char *ptr; if (strlen(targetname) < 1) exit_error(PARAMETER_PROBLEM, "Invalid target name (too short)"); if (strlen(targetname)+1 > sizeof(ipt_chainlabel)) exit_error(PARAMETER_PROBLEM, "Invalid target name `%s' (%u chars max)", targetname, (unsigned int)sizeof(ipt_chainlabel)-1); for (ptr = targetname; *ptr; ptr++) if (isspace(*ptr)) //检查参数*ptr是否为空格字符,也就是判断是否为空格('')、定位字符('\t')、CR('\r')、换行('\n')、垂直定位字符('\v')或翻页('\f')的情况 exit_error(PARAMETER_PROBLEM, "Invalid target name `%s'", targetname); return targetname; } |
struct xtables_target *find_target(const char *name, enum xt_tryload tryload) { struct xtables_target *ptr; /* Standard target? */ if (strcmp(name, "") == 0 || strcmp(name, XTC_LABEL_ACCEPT) == 0 //#define XTC_LABEL_ACCEPT "ACCEPT" || strcmp(name, XTC_LABEL_DROP) == 0 //#define XTC_LABEL_DROP "DROP" || strcmp(name, XTC_LABEL_QUEUE) == 0 //#define XTC_LABEL_QUEUE "QUEUE" || strcmp(name, XTC_LABEL_RETURN) == 0) //#define XTC_LABEL_RETURN "RETURN" name = "standard"; //** for (ptr = xtables_targets; ptr; ptr = ptr->next) { //从全局xtables_target 结构中查找目标名 if (strcmp(name, ptr->name) == 0)//直到找到 break; } #ifndef NO_SHARED_LIBS if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) { ptr = load_extension(lib_dir, afinfo.libprefix, name, true); //加载 if (ptr == NULL && tryload == LOAD_MUST_SUCCEED) exit_error(PARAMETER_PROBLEM, "Couldn't load target `%s':%s\n", name, dlerror()); } #else if (ptr && !ptr->loaded) { if (tryload != DONT_LOAD) ptr->loaded = 1; else ptr = NULL; } if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { exit_error(PARAMETER_PROBLEM, "Couldn't find target `%s'\n", name); } #endif if (ptr) ptr->used = 1; return ptr; } |
分类:
iptables
接上篇文章,find_target查到目标并加载成功,返回一个xtables_target型对象保存在target,分配空间,拷贝目标,初始化target对象。
if (target) { //成功 size_t size; size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + target->size; target->t = fw_calloc(1, size); target->t->u.target_size = size;//空间大小 strcpy(target->t->u.user.name, jumpto); //保存目标名到xptables_targets set_revision(target->t->u.user.name, target->revision); //设置版本 if (target->init != NULL) target->init(target->t); //初始化xptables_targets opts = merge_options(opts, target->extra_opts, &target->option_offset);//将target 的参数选项与旧的参数选项连接在一起由opts 返回,这样下一个循环可以分析target 的参数选项,一般在“default:”中进行分析 if (opts == NULL) exit_error(OTHER_PROBLEM, "can't alloc memory!"); } |
/*为了用户能够对内核态的规则进行操作,需要将内核态的规则信息读取到用户空间,对用户空间的规则进行修改后,再根据用户态的规则信息设置内核态的规则信息*/ if (!*handle) *handle = iptc_init(*table); //调用 iptc_init获取表的规则信息,调用list_entries函数显示规则 /* 加载此模块再获取 */ if (!*handle && load_xtables_ko(modprobe_program, 0) != -1) *handle = iptc_init(*table); if (!*handle) exit_error(VERSION_PROBLEM, "can't initialize iptables table `%s': %s", *table, iptc_strerror(errno)); |
iptc_handle_t iptc_init(const char *tablename); #define TC_INIT iptc_init //宏定义 TC_HANDLE_T TC_INIT(const char *tablename) //获取表信息 { TC_HANDLE_T h; STRUCT_GETINFO info; unsigned int tmp; socklen_t s; iptc_fn = TC_INIT; if (strlen(tablename) >= TABLE_MAXNAMELEN) { errno = EINVAL; return NULL; } /*为获取信息打开一个套接字接口*/ if (sockfd_use == 0) { sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW); if (sockfd < 0) return NULL; } sockfd_use++; retry: s = sizeof(info); strcpy(info.name, tablename); /*获取表基本信息,保存在info*/ if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) { if (--sockfd_use == 0) { close(sockfd); sockfd = -1; } return NULL; } DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n", info.valid_hooks, info.num_entries, info.size); if ((h = alloc_handle(info.name, info.size, info.num_entries)) == NULL) { if (--sockfd_use == 0) { close(sockfd); sockfd = -1; } return NULL; } /* Initialize current state */ h->info = info; h->entries->size = h->info.size; tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size; if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries, &tmp) < 0)//最后规则信息存入h->entries goto error; #ifdef IPTC_DEBUG2 { int fd = open("/tmp/libiptc-so_get_entries.blob", O_CREAT|O_WRONLY); if (fd >= 0) { write(fd, h->entries, tmp); close(fd); } } #endif if (parse_table(h) < 0) goto error; CHECK(h); return h; error: TC_FREE(&h); /* A different process changed the ruleset size, retry */ if (errno == EAGAIN) goto retry; return NULL; } |
STRUCT_GETINFO struct ipt_getinfo,最后getsockopt返回规则信息是ipt_getinfo型而TC_INIT函数返回一个具体的规则表信息iptc_handle型.
未完...
相关文章推荐
- 实用防火墙(Iptables)脚本分析
- iptables中ULOG和NFLOG实现分析
- iptables中ULOG和NFLOG实现分析【转】
- Openwrt iptables分析
- OpenStack neutron floatingips 与 iptables 深入分析
- Linux防火墙iptables中mark模块分析及编写
- 【Linux 驱动】Netfilter/Iptables (七) 内核协议栈skb封装分析(续六)
- Linux 为FTP 服务器添加iptables规则--案例分析
- iptables+squid综合案例分析
- iptables与layer7协议的分析与应用 推荐
- Linux-Netfilter&iptables实现机制的分析及应用
- Iptables 实例分析
- netfilter/iptables+squid综合案例分析
- Linux--netfilter/iptables+squid综合案例分析 推荐
- 分析kube-proxy的iptables规则
- 通过分析nginx log 后使用iptables来防ddos攻击
- iptables操作过程详解与实例分析
- 新建虚拟机的安全组规则分析--iptables
- 洞悉linux下的Netfilter&iptables:内核中的ip_tables分析
- 使用iptables默认拒绝所有导致sshd 响应慢原因分析及使用