实例分析Iptables基本原理(Part 2 - 参数解析)
本文源码分析基于Iptables1.4.7. 本文档版权归hereitis所有,可以自由拷贝/转载,转载时请保持文档的完整性并且注明来源,禁止用于任何商业用途。
hereitis.cu@gmail.com
- Iptables命令行参数
根据我的理解,Iptables命令行参数分为两类,仍然以在该文第一部分中的命令为例:
iptables -t nat -A PREROUTING -p tcp -d 172.20.9.80 -i eth0 --sport 1024:65535 --dport 21 -j DNAT --to-destination 192.168.1.100:21- 独立参数:像-t, -A, -p, -d, -i, -j这样的参数都属于独立参数,可以直接被解析为具有明确意义的参数。
- 非独立参数:如--sport, --dport这样的参数依赖于-p tcp/udp,只有指定了tcp/udp这样的协议,--sport, --dport才有意义,当-p icmp时这些端口选项就没有意义了。类似地,--to-destination依赖于-j DNAT。在下文中,称-p tcp/udp,-j DNAT这样的参数为--sport,--dport,--to-destination这些参数的宿主参数。
参数解析主要利用getopt_long()来进行解析。独立参数有相应的case:来进行处理,而对于非独立参数在被解析时,会
按需加载相应的match/target,同时将该match/target的非独立参数都merge到global options里。当第一次遇到一个非独立参数时,如第一次遇到--sport,则在default:分支来进行解析:
这时将会加载tcp_match,同时tcp_match中所有非独立参数都将全部merge到global options里, 回退optind,重新对该非独立参数进行一次解析。这一次的解析才真正地对该参数进行处理,第一次仅仅是加载相关match, 并将其中的全部非独立参数merge到global options.
这个过程在下面的源码分析中会详细解释。
- 源码分析
源码分析主要是根据上述Iptables示例命令来进行解析,并且假设该条命令是当前系统中第一条Iptables命令,之前所有的相关规则都是空的,主要是便于分析。命令解析在iptables.c中的do_command()函数中。
- -t nat
case 't':
if (invert)
// 不支持-t !NAT xtables_error(PARAMETER_PROBLEM,
"unexpected ! flag before --table");
*table = optarg; break;
该选项指定了该条规则是用于nat table。
- -A PREROUTING
case 'A':
add_command(&command, CMD_APPEND, CMD_NONE,
invert);
chain = optarg; break;
该选项指定了该条规则的chain。
- -p tcp
- 选项处理
case 'p':
xtables_check_inverse(optarg, &invert, &optind, argc, argv); // 分析是不是-p !tcp并设置invert标志
set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
invert);
/* Canonicalize into lower case */
for (protocol = optarg; *protocol; protocol++)
*protocol = tolower(*protocol);
protocol = optarg;
fw.ip.proto = xtables_parse_protocol(protocol); //协议号查找,如“tcp”将被转换成6
if (fw.ip.proto == 0
&& (fw.ip.invflags & IPT_INV_PROTO))
xtables_error(PARAMETER_PROBLEM,
"rule would never match protocol");
break;
- 协议转换及查找函数xtables_parse_protocol(xtables.c)
const struct xtables_pprot xtables_chain_protos[] = {
{"tcp", IPPROTO_TCP},
{"sctp", IPPROTO_SCTP},
{"udp", IPPROTO_UDP},
{"udplite", IPPROTO_UDPLITE},
{"icmp", IPPROTO_ICMP},
{"icmpv6", IPPROTO_ICMPV6},
{"ipv6-icmp", IPPROTO_ICMPV6},
{"esp", IPPROTO_ESP},
{"ah", IPPROTO_AH},
{"ipv6-mh", IPPROTO_MH},
{"mh", IPPROTO_MH},
{"all", 0},
{NULL},
};
u_int16_t
xtables_parse_protocol(const char *s)
{
unsigned int proto;
if (!xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX)) {
struct protoent *pent;
/* first deal with the special case of 'all' to prevent
* people from being able to redefine 'all' in nsswitch
* and/or provoke expensive [not working] ldap/nis/...
* lookups */
if (!strcmp(s, "all"))
return 0;
if ((pent = getprotobyname(s))) // 根据/etc/protocols查找相应的协议号
proto = pent->p_proto;
else {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) { //根据预先定义的协议数组来查找
if (xtables_chain_protos[i].name == NULL)
continue;
if (strcmp(s, xtables_chain_protos[i].name) == 0) {
proto = xtables_chain_protos[i].num;
break;
}
}
if (i == ARRAY_SIZE(xtables_chain_protos))
xt_params->exit_err(PARAMETER_PROBLEM,
"unknown protocol `%s' specified",
s);
}
}
return proto;
}
case 'd':
xtables_check_inverse(optarg, &invert, &optind, argc, argv);
set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
invert);
dhostnetworkmask = optarg;
break;
该选项指定了目的网络的信息。
- -i eth0
- 选项处理
case 'i':
xtables_check_inverse(optarg, &invert, &optind, argc, argv);
set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
invert);
xtables_parse_interface(optarg,
fw.ip.iniface,
fw.ip.iniface_mask);
break;
- 接口参数解析xtables_parse_interface(xtables.c)
void xtables_parse_interface(const char *arg, char *vianame,
unsigned char *mask)
{
unsigned int vialen = strlen(arg);
unsigned int i;
memset(mask, 0, IFNAMSIZ);
memset(vianame, 0, IFNAMSIZ);
if (vialen + 1 > IFNAMSIZ)
xt_params->exit_err(PARAMETER_PROBLEM,
"interface name `%s' must be shorter than IFNAMSIZ"
" (%i)", arg, IFNAMSIZ-1);
strcpy(vianame, arg);
if (vialen == 0)
memset(mask, 0, IFNAMSIZ);
else if (vianame[vialen - 1] == '+') {
memset(mask, 0xFF, vialen - 1);
memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
/* Don't remove `+' here! -HW */
} else {
/* Include nul-terminator in match */
memset(mask, 0xFF, vialen + 1);
memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
for (i = 0; vianame[i]; i++) {
if (vianame[i] == '/' ||
vianame[i] == ' ') {
fprintf(stderr,
"Warning: weird character in interface"
" `%s' ('/' and ' ' are not allowed by the kernel).\n",
vianame);
break;
}
}
}
}
需要解释的是接口名字最后含一个'+'的情况。eth+将表示所有的Ethernet网卡,注意上述函数中对于mask的设置。
- --sport 1024:65535
- 选项处理
default:
if (target == NULL || target->parse == NULL ||
!target->parse(c - target->option_offset,
argv, invert,
&target->tflags,
&fw, &target->t)) {
for (matchp = matches; matchp; matchp = matchp->next) { //第一条Iptables命令,所以目前matches为空
if (matchp->completed ||
matchp->match->parse == NULL)
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 you listen carefully, you can
actually hear this code suck. */
/* some explanations (after four different bugs
* in 3 different releases): If we encounter a
* parameter, that has not been parsed yet,
* it's not an option of an explicitly loaded
* match or a target. However, we support
* implicit loading of the protocol match
* extension. '-p tcp' means 'l4 proto 6' and
* at the same time 'load tcp protocol match on
* demand if we specify --dport'.
*
* To make this work, we need to make sure:
* - the parameter has not been parsed by
* a match (m above)
* - a protocol has been specified
* - the protocol extension has not been
* loaded yet, or is loaded and unused
* [think of iptables-restore!]
* - the protocol extension can be successively
* loaded
*/
if (m == NULL
&& protocol
&& (!find_proto(protocol, XTF_DONT_LOAD,
options&OPT_NUMERIC, NULL)
|| (find_proto(protocol, XTF_DONT_LOAD,
options&OPT_NUMERIC, NULL)
&& (proto_used == 0))
)
&& (m = find_proto(protocol, XTF_TRY_LOAD,
options&OPT_NUMERIC, &matches))) {//该函数将加载tcp_match
/* Try loading protocol */
size_t size;
proto_used = 1;
size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+ m->size;
m->m = xtables_calloc(1, size);
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
xtables_set_revision(m->m->u.user.name,
m->revision);
if (m->init != NULL)
m->init(m->m);
opts = xtables_merge_options(opts, //这个函数将会把tcp_match相关的所有参数merge到global options
m->extra_opts,
&m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM,
"can't alloc memory!");
optind--; //回退,重新解析--sport
continue;
}
if (!m) {
if (c == '?') {
if (optopt) {
xtables_error(
PARAMETER_PROBLEM,
"option `%s' "
"requires an "
"argument",
argv[optind-1]);
} else {
xtables_error(
PARAMETER_PROBLEM,
"unknown option "
"`%s'",
argv[optind-1]);
}
}
xtables_error(PARAMETER_PROBLEM,
"Unknown arg `%s'", optarg);
}
}
程序中的注释非常重要,解释了什么情况下会按需加载,什么条件下按需加载会工作。
- find_proto(iptables.c)
static struct xtables_match *
find_proto(const char *pname, enum xtables_tryload tryload,
int nolookup, struct xtables_rule_match **matches) //参数: ("tcp", XTF_TRY_LOAD, options&OPT_NUMERIC, &matches)
{
unsigned int proto;
if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) {
const char *protoname = proto_to_name(proto, nolookup);
if (protoname) // "tcp"
return xtables_find_match(protoname, tryload, matches);
} else
return xtables_find_match(pname, tryload, matches);
return NULL;
}
根据相应的协议类型(tcp),寻找相应的match(tcp_match),按需加载。
- xtables_find_match(xtables.c)
struct xtables_match *
xtables_find_match(const char *name, enum xtables_tryload tryload,
struct xtables_rule_match **matches)
{
struct xtables_match *ptr;
const char *icmp6 = "icmp6";
/* This is ugly as hell. Nonetheless, there is no way of changing
* this without hurting backwards compatibility */
if ( (strcmp(name,"icmpv6") == 0) ||
(strcmp(name,"ipv6-icmp") == 0) ||
(strcmp(name,"icmp6") == 0) )
name = icmp6;
// Part 1: 这部分用于查找某一match是否已经在全局match list中,从而来判断match是否已经注册或者 进行一些兼容性检查
for (ptr = xtables_matches; ptr; ptr = ptr->next) { // global match list
if (strcmp(name, ptr->name) == 0) {
struct xtables_match *clone;
/* First match of this type: */
if (ptr->m == NULL)
break;
/* Second and subsequent clones */
clone = xtables_malloc(sizeof(struct xtables_match));
memcpy(clone, ptr, sizeof(struct xtables_match));
clone->mflags = 0;
/* This is a clone: */
clone->next = clone;
ptr = clone;
break;
}
}
//Part 2: 加载match相应的.so
#ifndef NO_SHARED_LIBS
if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
ptr = load_extension(xtables_libdir, afinfo->libprefix,
name, false); //加载相应match的.so文件, libxt_tcp.so
if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
xt_params->exit_err(PARAMETER_PROBLEM,
"Couldn't load match `%s':%s\n",
name, dlerror());
}
#else
if (ptr && !ptr->loaded) {
if (tryload != XTF_DONT_LOAD)
ptr->loaded = 1;
else
ptr = NULL;
}
if(!ptr && (tryload == XTF_LOAD_MUST_SUCCEED)) {
xt_params->exit_err(PARAMETER_PROBLEM,
"Couldn't find match `%s'\n", name);
}
#endif
//Part 3: 将Part 2 中加载的match插入到match list中
if (ptr && matches) {
struct xtables_rule_match **i;
struct xtables_rule_match *newentry;
newentry = xtables_malloc(sizeof(struct xtables_rule_match));
for (i = matches; *i; i = &(*i)->next) {
if (strcmp(name, (*i)->match->name) == 0)
(*i)->completed = true; //match已经注册完成
}
newentry->match = ptr;
newentry->completed = false;
newentry->next = NULL;
*i = newentry; //将该match插入到match list中
}
return ptr;
}
- load_exention
- load_exention(xtables.c)
static void *load_extension(const char *search_path, const char *prefix,
const char *name, bool is_target)
{
const char *dir = search_path, *next;
void *ptr = NULL;
struct stat sb;
char path[256];
do {
next = strchr(dir, ':');
if (next == NULL)
next = dir + strlen(dir);
snprintf(path, sizeof(path), "%.*s/libxt_%s.so",
(unsigned int)(next - dir), dir, name); //libxt_tcp.so
if (dlopen(path, RTLD_NOW) != NULL) { //libxt_tcp.c 中的_init函数将会被调用
/* Found library. If it didn't register itself,
maybe they specified target as match. */
if (is_target)
ptr = xtables_find_target(name, XTF_DONT_LOAD);
else
ptr = xtables_find_match(name,
XTF_DONT_LOAD, NULL); //通过在全局match list中查找来判断match是否加载成功
} else if (stat(path, &sb) == 0) {
fprintf(stderr, "%s: %s\n", path, dlerror());
}
if (ptr != NULL)
return ptr;
snprintf(path, sizeof(path), "%.*s/%s%s.so",
(unsigned int)(next - dir), dir, prefix, name);
if (dlopen(path, RTLD_NOW) != NULL) {
if (is_target)
ptr = xtables_find_target(name, XTF_DONT_LOAD);
else
ptr = xtables_find_match(name,
XTF_DONT_LOAD, NULL);
} else if (stat(path, &sb) == 0) {
fprintf(stderr, "%s: %s\n", path, dlerror());
}
if (ptr != NULL)
return ptr;
dir = next + 1;
} while (*next != '\0');
return NULL;
}
- _init(extentions/libxt_tcp.c)
void
_init(void)
{
xtables_register_match(&tcp_match);
}
static struct xtables_match
tcp_match = {
.family = NFPROTO_UNSPEC,
.name = "tcp",
.version = XTABLES_VERSION,
.size = XT_ALIGN(sizeof(struct xt_tcp)),
.userspacesize = XT_ALIGN(sizeof(struct xt_tcp)),
.help = tcp_help,
.init = tcp_init,
.parse = tcp_parse,
.print = tcp_print,
.save = tcp_save,
.extra_opts = tcp_opts,
};
- xtables_register_match(xtables.c)
void xtables_register_match(struct xtables_match *me)
{
struct xtables_match **i, *old;
if (me->version == NULL) {
fprintf(stderr, "%s: match %s<%u> is missing a version\n",
xt_params->program_name, me->name, me->revision);
exit(1);
}
if (strcmp(me->version, XTABLES_VERSION) != 0) {
fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
"but \"%s\" is required.\n",
xt_params->program_name, me->name,
me->version, XTABLES_VERSION);
exit(1);
}
/* Revision field stole a char from name. */
if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) {
fprintf(stderr, "%s: target `%s' has invalid name\n",
xt_params->program_name, me->name);
exit(1);
}
if (me->family >= NPROTO) {
fprintf(stderr,
"%s: BUG: match %s has invalid protocol family\n",
xt_params->program_name, me->name);
exit(1);
}
/* ignore not interested match */
if (me->family != afinfo->family && me->family != AF_UNSPEC)
return;
old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL); //查找match是否已经注册,以及相关兼容性
if (old) {
if (old->revision == me->revision &&
old->family == me->family) {
fprintf(stderr,
"%s: match `%s' already registered.\n",
xt_params->program_name, me->name);
exit(1);
}
/* Now we have two (or more) options, check compatibility. */
if (compatible_match_revision(old->name, old->revision)
&& old->revision > me->revision)
return;
/* See if new match can be used. */
if (!compatible_match_revision(me->name, me->revision))
return;
/* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
if (old->revision == me->revision && me->family == AF_UNSPEC)
return;
/* Delete old one. */
for (i = &xtables_matches; *i!=old; i = &(*i)->next);
*i = old->next;
}
if (me->size != XT_ALIGN(me->size)) {
fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
xt_params->program_name, me->name,
(unsigned int)me->size);
exit(1);
}
/* Append to list. */
for (i = &xtables_matches; *i; i = &(*i)->next); //将match插入到全局match list中
me->next = NULL;
*i = me;
me->m = NULL;
me->mflags = 0;
}
/* Try loading protocol */
size_t size;
proto_used = 1;
size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+ m->size;
m->m = xtables_calloc(1, size); //为match分配空间
m->m->u.match_size = size;
strcpy(m->m->u.user.name, m->name);
xtables_set_revision(m->m->u.user.name,
m->revision);
if (m->init != NULL)
m->init(m->m);
opts = xtables_merge_options(opts, //这个函数将会把tcp_match相关的所有参数merge到global options
m->extra_opts, //tcp_match->extra_opts
&m->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM,
"can't alloc memory!");
optind--; //回退,重新解析--sport
continue;
- tcp-match->init(extentions/libxt_tcp.c)
static void
tcp_init(struct xt_entry_match *m)
{
struct xt_tcp *tcpinfo = (struct xt_tcp *)m->data;
tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF; //源端口,目的端口的初始化
}
- xtables_merge_options(xtables.c)
static const struct option
tcp_opts[] = {
//tcp_match相关的非独立参数 { "source-port", 1, NULL, '1' },
{ "sport", 1, NULL, '1' }, /* synonym */
{ "destination-port", 1, NULL, '2' },
{ "dport", 1, NULL, '2' }, /* synonym */
{ "syn", 0, NULL, '3' },
{ "tcp-flags", 1, NULL, '4' },
{ "tcp-option", 1, NULL, '5' },
{ .name = NULL }
};
struct option *
xtables_merge_options(struct option *oldopts,
const struct option *newopts,
unsigned int *option_offset)
{
unsigned int num_old, num_new, i;
struct option *merge;
if (newopts == NULL)
return oldopts;
for (num_old = 0; oldopts[num_old].name; num_old++) ;
for (num_new = 0; newopts[num_new].name; num_new++) ;
xt_params->option_offset += 256;
*option_offset = xt_params->option_offset;
merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
if (merge == NULL)
return NULL;
memcpy(merge, oldopts, num_old * sizeof(struct option));
xtables_free_opts(0); /* Release any old options merged */
for (i = 0; i < num_new; i++) {
merge[num_old + i] = newopts[i];
merge[num_old + i].val += *option_offset;
}
memset(merge + num_old + num_new, 0, sizeof(struct option));
return merge;
}
- --sport 1024:65535(第二次解析)
if (target == NULL || target->parse == NULL ||
!target->parse(c - target->option_offset,
argv, invert,
&target->tflags,
&fw, &target->t)) {
for (matchp = matches; matchp; matchp = matchp->next) {
if (matchp->completed ||
matchp->match->parse == NULL)
continue;
if (matchp->match->parse(c - matchp->match->option_offset,
argv, invert,
&matchp->match->mflags,
&fw,
&matchp->match->m)) //调用tcp_match->parse
break;
}
......
忽略的部分本次处理中将不会执行,因为match已经成功加载。
- tcp_parse(extensions/libxt_tcp.c)
static int
tcp_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_match **match)
{
struct xt_tcp *tcpinfo = (struct xt_tcp *)(*match)->data;
switch (c) {
case '1': // --source-port
if (*flags & TCP_SRC_PORTS)
xtables_error(PARAMETER_PROBLEM,
"Only one `--source-port' allowed");
xtables_check_inverse(optarg, &invert, &optind, 0, argv);
parse_tcp_ports(optarg, tcpinfo->spts); //分析--source-port 参数值,将分析结果存储到match源端口中
if (invert)
tcpinfo->invflags |= XT_TCP_INV_SRCPT;
*flags |= TCP_SRC_PORTS;
break;
case '2':
if (*flags & TCP_DST_PORTS)
xtables_error(PARAMETER_PROBLEM,
"Only one `--destination-port' allowed");
xtables_check_inverse(optarg, &invert, &optind, 0, argv);
parse_tcp_ports(optarg, tcpinfo->dpts);
if (invert)
tcpinfo->invflags |= XT_TCP_INV_DSTPT;
*flags |= TCP_DST_PORTS;
break;
case '3':
if (*flags & TCP_FLAGS)
xtables_error(PARAMETER_PROBLEM,
"Only one of `--syn' or `--tcp-flags' "
" allowed");
parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert);
*flags |= TCP_FLAGS;
break;
case '4':
if (*flags & TCP_FLAGS)
xtables_error(PARAMETER_PROBLEM,
"Only one of `--syn' or `--tcp-flags' "
" allowed");
xtables_check_inverse(optarg, &invert, &optind, 0, argv);
if (!argv[optind]
|| argv[optind][0] == '-' || argv[optind][0] == '!')
xtables_error(PARAMETER_PROBLEM,
"--tcp-flags requires two args.");
parse_tcp_flags(tcpinfo, optarg, argv[optind],
invert);
optind++;
*flags |= TCP_FLAGS;
break;
case '5':
if (*flags & TCP_OPTION)
xtables_error(PARAMETER_PROBLEM,
"Only one `--tcp-option' allowed");
xtables_check_inverse(optarg, &invert, &optind, 0, argv);
parse_tcp_option(optarg, &tcpinfo->option);
if (invert)
tcpinfo->invflags |= XT_TCP_INV_OPTION;
*flags |= TCP_OPTION;
break;
default:
return 0;
}
return 1;
}
- parce_tcp_ports(extension/libxt_tcp.c)
parse_tcp_ports(const char *portstring, u_int16_t *ports)
{
char *buffer;
char *cp;
buffer = strdup(portstring);
if ((cp = strchr(buffer, ':')) == NULL)
ports[0] = ports[1] = xtables_parse_port(buffer, "tcp");
else { // 1024:65535
*cp = '\0';
cp++;
ports[0] = buffer[0] ? xtables_parse_port(buffer, "tcp") : 0;
ports[1] = cp[0] ? xtables_parse_port(cp, "tcp") : 0xFFFF;
if (ports[0] > ports[1])
xtables_error(PARAMETER_PROBLEM,
"invalid portrange (min > max)");
}
free(buffer);
}
- --dport 21
解析过程类似于上述--sport的第二次解析,因为tcp_match已经成功加载。 case 'j':
set_option(&options, OPT_JUMP, &fw.ip.invflags,
invert);
jumpto = parse_target(optarg);
/* TRY_LOAD (may be chain name) */
target = xtables_find_target(jumpto, XTF_TRY_LOAD); // 加载target: dnat_tg_reg
if (target) {
size_t size;
size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+ target->size;
target->t = xtables_calloc(1, size); //为target分配内存
target->t->u.target_size = size;
strcpy(target->t->u.user.name, jumpto);
xtables_set_revision(target->t->u.user.name,
target->revision);
if (target->init != NULL)
target->init(target->t);
opts = xtables_merge_options(opts,
target->extra_opts,
&target->option_offset);
if (opts == NULL)
xtables_error(OTHER_PROBLEM,
"can't alloc memory!");
}
break;
- xtables_find_target(xtables.c)
xtables_find_target(const char *name, enum xtables_tryload tryload)
{
struct xtables_target *ptr;
/* Standard target? */
if (strcmp(name, "") == 0
|| strcmp(name, XTC_LABEL_ACCEPT) == 0
|| strcmp(name, XTC_LABEL_DROP) == 0
|| strcmp(name, XTC_LABEL_QUEUE) == 0
|| strcmp(name, XTC_LABEL_RETURN) == 0)
name = "standard";
// Part 1: 查询global target list,看看target是否已经注册
for (ptr = xtables_targets; ptr; ptr = ptr->next) {
if (strcmp(name, ptr->name) == 0)
break;
}
//Part 2: 如果target没有注册,则按需加载相应的target
#ifndef NO_SHARED_LIBS
if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
ptr = load_extension(xtables_libdir, afinfo->libprefix, //libipt_DNAT.so
name, true);
if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
xt_params->exit_err(PARAMETER_PROBLEM,
"Couldn't load target `%s':%s\n",
name, dlerror());
}
#else
if (ptr && !ptr->loaded) {
if (tryload != XTF_DONT_LOAD)
ptr->loaded = 1;
else
ptr = NULL;
}
if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED) {
xt_params->exit_err(PARAMETER_PROBLEM,
"Couldn't find target `%s'\n", name);
}
#endif
if (ptr)
ptr->used = 1;
return ptr;
}
load_extension()在前面已经分析过了,接下来直接分析target的_init()。
- _init(extentions/libipt_DNAT.c)
static struct xtables_target dnat_tg_reg = {
.name = "DNAT",
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
.userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
.help = DNAT_help,
.parse = DNAT_parse,
.final_check = DNAT_check,
.print = DNAT_print,
.save = DNAT_save,
.extra_opts = DNAT_opts,
};
void _init(void)
{
xtables_register_target(&dnat_tg_reg);
}
- xtables_register_target(xtables.c)
void xtables_register_target(struct xtables_target *me)
{
struct xtables_target *old;
if (me->version == NULL) {
fprintf(stderr, "%s: target %s<%u> is missing a version\n",
xt_params->program_name, me->name, me->revision);
exit(1);
}
if (strcmp(me->version, XTABLES_VERSION) != 0) {
fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
"but \"%s\" is required.\n",
xt_params->program_name, me->name,
me->version, XTABLES_VERSION);
exit(1);
}
/* Revision field stole a char from name. */
if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) {
fprintf(stderr, "%s: target `%s' has invalid name\n",
xt_params->program_name, me->name);
exit(1);
}
if (me->family >= NPROTO) {
fprintf(stderr,
"%s: BUG: target %s has invalid protocol family\n",
xt_params->program_name, me->name);
exit(1);
}
/* ignore not interested target */
if (me->family != afinfo->family && me->family != AF_UNSPEC)
return;
old = xtables_find_target(me->name, XTF_DURING_LOAD);
if (old) {
struct xtables_target **i;
if (old->revision == me->revision &&
old->family == me->family) {
fprintf(stderr,
"%s: target `%s' already registered.\n",
xt_params->program_name, me->name);
exit(1);
}
/* Now we have two (or more) options, check compatibility. */
if (compatible_target_revision(old->name, old->revision)
&& old->revision > me->revision)
return;
/* See if new target can be used. */
if (!compatible_target_revision(me->name, me->revision))
return;
/* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
if (old->revision == me->revision && me->family == AF_UNSPEC)
return;
/* Delete old one. */
for (i = &xtables_targets; *i!=old; i = &(*i)->next);
*i = old->next;
}
if (me->size != XT_ALIGN(me->size)) {
fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
xt_params->program_name, me->name,
(unsigned int)me->size);
exit(1);
}
/* Prepend to list. */
me->next = xtables_targets;
xtables_targets = me;
me->t = NULL;
me->tflags = 0;
}
- --to-destination 192.168.1.100:21
- 选项处理
default:
if (target == NULL || target->parse == NULL ||
!target->parse(c - target->option_offset, //调用target的parse
argv, invert,
&target->tflags,
&fw, &target->t)) {
for (matchp = matches; matchp; matchp = matchp->next) {
if (matchp->completed ||
matchp->match->parse == NULL)
continue;
if (matchp->match->parse(c - matchp->match->option_offset,
argv, invert,
&matchp->match->mflags,
&fw,
&matchp->match->m))
break;
}
- DNAT_parse(extensions/libipt_DNAT.c)
static int DNAT_parse(int c, char **argv, int invert, unsigned int *flags,
const void *e, struct xt_entry_target **target)
{
const struct ipt_entry *entry = e;
struct ipt_natinfo *info = (void *)*target;
int portok;
if (entry->ip.proto == IPPROTO_TCP
|| entry->ip.proto == IPPROTO_UDP
|| entry->ip.proto == IPPROTO_SCTP
|| entry->ip.proto == IPPROTO_DCCP
|| entry->ip.proto == IPPROTO_ICMP)
portok = 1;
else
portok = 0;
switch (c) {
case '1': //--to-destination
if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
xtables_error(PARAMETER_PROBLEM,
"Unexpected `!' after --to-destination");
if (*flags & IPT_DNAT_OPT_DEST) {
if (!kernel_version)
get_kernel_version();
if (kernel_version > LINUX_VERSION(2, 6, 10))
xtables_error(PARAMETER_PROBLEM,
"Multiple --to-destination not supported");
}
*target = parse_to(optarg, portok, info);
/* WTF do we need this for?? */
if (*flags & IPT_DNAT_OPT_RANDOM)
info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
*flags |= IPT_DNAT_OPT_DEST;
return 1;
case '2':
if (*flags & IPT_DNAT_OPT_DEST) {
info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
*flags |= IPT_DNAT_OPT_RANDOM;
} else
*flags |= IPT_DNAT_OPT_RANDOM;
return 1;
case '3':
info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
return 1;
default:
return 0;
}
}
- parse_to(extensions/libipt_DNAT.c)
/* Ranges expected in network order. */
static struct xt_entry_target *
parse_to(char *arg, int portok, struct ipt_natinfo *info)
{
struct nf_nat_range range;
char *colon, *dash, *error;
const struct in_addr *ip;
memset(&range, 0, sizeof(range));
colon = strchr(arg, ':');
if (colon) {
int port;
if (!portok)
xtables_error(PARAMETER_PROBLEM,
"Need TCP, UDP, SCTP or DCCP with port specification");
range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
port = atoi(colon+1);
if (port <= 0 || port > 65535)
xtables_error(PARAMETER_PROBLEM,
"Port `%s' not valid\n", colon+1);
error = strchr(colon+1, ':');
if (error)
xtables_error(PARAMETER_PROBLEM,
"Invalid port:port syntax - use dash\n");
dash = strchr(colon, '-');
if (!dash) {
range.min.tcp.port
= range.max.tcp.port
= htons(port);
} else {
int maxport;
maxport = atoi(dash + 1);
if (maxport <= 0 || maxport > 65535)
xtables_error(PARAMETER_PROBLEM,
"Port `%s' not valid\n", dash+1);
if (maxport < port)
/* People are stupid. */
xtables_error(PARAMETER_PROBLEM,
"Port range `%s' funky\n", colon+1);
range.min.tcp.port = htons(port);
range.max.tcp.port = htons(maxport);
}
/* Starts with a colon? No IP info...*/
if (colon == arg)
return &(append_range(info, &range)->t);
*colon = '\0';
}
range.flags |= IP_NAT_RANGE_MAP_IPS;
dash = strchr(arg, '-');
if (colon && dash && dash > colon)
dash = NULL;
if (dash)
*dash = '\0';
ip = xtables_numeric_to_ipaddr(arg);
if (!ip)
xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
arg);
range.min_ip = ip->s_addr;
if (dash) {
ip = xtables_numeric_to_ipaddr(dash+1);
if (!ip)
xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
dash+1);
range.max_ip = ip->s_addr;
} else
range.max_ip = range.min_ip;
return &(append_range(info, &range)->t);
}
这样将把DNAT相关info构造出来并且保存在target中。
到此,所有的参数解析已经完成,相应的match,target也已经按需加载成功,相关的结构以及变量信息也已经成功构建,下一步的动作将是把这些信息写入内核中。