iptc_init(): 根据指定的表名获取内核中该表的自身信息和表中所有规则
fcntl(fd, F_SETFD, FD_CLOEXEC)
fcntl(int fd, int cmd, long arg): 用来操作文件描述符一些特性。fcntl不仅可以施加建议性锁,还可以施加强制锁。同时,fcntl还可以对文件某一记录进行上锁,也就是记录锁。
F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定
=========用户空间==========
对于ipv4和ipv6都被宏定义为TC_INIT
// libip4tc.c
#define TC_INIT iptc_init
// libip6tc.c
#define TC_INIT ip6tc_init
struct xtc_handle *
TC_INIT(const char *tablename)
1、创建sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW),与内核进行通信
2、将tablename设置到STRUCT_GETINFO info的name中,通过getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, sizeof(info)),获取STRUCT_GETINFO信息,对于结构体用户空间、内核空间如何使用,结构体注释已经明确说明了
// libiptc/libip4tc.c
#define STRUCT_GETINFO struct ipt_getinfo
// libiptc/libip6tc.c
#define STRUCT_GETINFO struct ip6t_getinfo
// include/linuxe/netfileter_ipv4/ip_tables.h(来自内核)
/* The argument to IPT_SO_GET_INFO */
struct ipt_getinfo {
/* Which table: caller fills this in. */
char name[XT_TABLE_MAXNAMELEN];
/* kernel fills these in */
/* Which hook entry points are valid: bitmask */
unsigned int valid_hooks;
/* Hook entry points: one per netfilter hook. */
unsigned int hook_entry[NF_INET_NUMHOOKS];
/* Underflow points */
unsigned int underflow[NF_INET_NUMHOOKS];
/* Number of entries */
unsigned int num_entries;
/* size of entries */
unsigned int size;
};
<<<<<<<<<<(1)
// libiptc/libiptc.c
struct xtc_handle {
int sockfd;
int chaned;
struct list_head chains;
struct chain_head *chain_iterator_cur;
struct rule_head *rule_iterator_cur;
unsigned int num_chains; /* Number of user defined chains */
struct chain_head **chain_index; /* array for fast chain list access */
unsigned int chain_index_sz; /* size of chain index array */
int sorted_offsets;
STRUCT_GETINFO info;
STRUCT_GET_ENTRIES *entries;
};
3、根据获取的STRUCT_GETINFO中的size信息,进行struct xtc_handle的分配{alloc_handle(STRUCT_GETINFO *info)},然后将xtc_handle中的sockfd、info、 entries->size进行赋值
4、再通过getsockopt获取entries的信息{getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, H->entries, &tmp)}
// libiptc/libip4tc.c
#define STRUCT_GET_ENTRIES struct ipt_get_entries
// libiptc/libip6tc.c
#define STRUCT_GET_ENTRIES struct ip6t_get_entries
// include/linux/netfilter_ipv4/ip_tables.h
/* The argument to IPT_SO_GET_ENTRIES. */
struct ipt_get_entries {
/* Which table: user fills this in. */
char name[XT_TABLE_MAXNAMELEN];
/* User fills this in: total entry size. */
unsigned int size;
/* The entries. */
struct ipt_entry entrytable[0];
};
=========内核空间==========
1、内核模块通过函数do_ipt_get_ctl得到IPT_SO_GET_INFO命令,通过get_info获取信息
2、get_info首先从user参数中获取tablename: copy_from_user(name, user, sizeof(name));【根据用户空间的步骤2得知,用户空间传入的:q参数类型为STRUCT_GETINFO,但是这个位置获取的为字符串name,因此STRUCT_GETINFO第一个参数必须是name,并且一样大小的name空间】
3、根据name从内核空间中查找到xt_table t = try_then_request_module(xt_find_table_lock(net, AF_INET, name), "iptable_%s", name);
4、从获取的table中,从中拷贝出valid_hooks/hook_entry/under_flow/num_entries/size/name放置到新构建的ipt_getinfo info结构体中
5、调用copy_to_user到用户空间{copy_to_user(user, info, *len)}, 并对表进行解锁(获取表时进行了lock)
<<<<<<<<<<(1)
内核定义的几个数据结构用于与用户空间交互
1、IPT_SO_GET_INFO
>>>>>>>>ipv4的定义<<<<<<<<
// 在用户空间(iptables)中被重定义为
#define STRUCT_GETINFO struct ipt_getinfo
// 在内核空间中定义为:
/* The argument to IPT_SO_GET_INFO */
struct ipt_getinfo{
/* Which table: caller fills this in. */
char name[XT_TABLE_MAXNAMELEN];
/* Kernel fills these in. */
/* Which hook entry points are valid: bitmask */
unsigned int valid_hooks;
/* Hook entry points: one per-netfilter hooks. */
unsigned int hook_entry[NF_INET_NUMHOOKS];
/* Underflow points */
unsigned int underflow[NF_INET_NUMHOOKS];
/* Number of entries */
unsigned int num_entries;
/* Size of entries*/
unsigned int size;
};
>>>>>>>>ipv6的定义与ipv4类同<<<<<<<<
2、IPT_SO_GET_ENTRIES
// 在用户空间(iptables)被重定义为
#define STRUCT_GET_ENTRIES struct ipt_get_entries
// 在内核空间中的定义
/* The argument to IPT_SO_GET_ENTRIES. */
struct ipt_get_entries {
/* Which table: user fills this in. */
char name[XT_TABLE_MAXNAMELEN];
/* User fills this in: total entry size. */
unsigned int size;
/* The entries */
struct ipt_entry entrytable[0];
};