一定要清楚自己在干什么,每行代码在干什么,这样写的代码才能做到心中有数。
之前看到ip_set_hash_ip.chash_ip4_uadt和hash_ip4_kadt函数,就一直很好奇这两个函数是干什么呢?
下面我来带你一步步剖析这两个函数
一、kadt和uadt回调函数的注册
首先看下struct ip_set_type_variant结构体变量
**kadt回调函数的注册在以下三个位置**
1.ip_set_bitmap_gen.h的.kadt = mtype_kadt,
static const struct ip_set_type_variant mtype = {
.kadt = mtype_kadt,
.uadt = mtype_uadt,
.adt = {
[IPSET_ADD] = mtype_add,
[IPSET_DEL] = mtype_del,
[IPSET_TEST] = mtype_test,
},
......
};
2.ip_set_hash_gen.h的.kadt = mtype_kadt,
static const struct ip_set_type_variant mtype_variant = {
.kadt = mtype_kadt,
.uadt = mtype_uadt,
.adt = {
[IPSET_ADD] = mtype_add,
[IPSET_DEL] = mtype_del,
[IPSET_TEST] = mtype_test,
},
......
};
正好对应set模块的三种数据类型bitmap,hash。
其中
.kadt = mtype_kadt,
.uadt = mtype_uadt,
搜索mtype_kadt,是一个宏定义,将MTYPE宏定义和_kadt拼接起来
#define CONCAT(a, b) a##b
#define TOKEN(a, b) CONCAT(a, b)
#define mtype_kadt TOKEN(MTYPE, _kadt)
MTYPE有不同的定义,对于hash:ip集合,为hash_ip4
#define MTYPE hash_ip4
那么mtype_kadt宏经过拼装以后得到的是hash_ip4_kadt,即我之前一直疑惑的hash_ip4_kadt函数
ipset的每种类型都需要实现一个xxx_kadt的函数,不信的同学可以打开源代码自己看哦。
二、kadt回调函数的调用位置
调用位置有三处,分别对应于ip_set_test,ip_set_add,ip_set_del函数
1.ip_set_test函数
ret = set->variant->kadt(set, skb, par, IPSET_TEST, opt);
2.ip_set_add函数
ret = set->variant->kadt(set, skb, par, IPSET_ADD, opt);
3.ip_set_del函数
ret = set->variant->kadt(set, skb, par, IPSET_DEL, opt);
以hash_ip4_kadt为例子进行讲解
hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
const struct xt_action_param *par,
enum ipset_adt adt, struct ip_set_adt_opt *opt)
{
const struct hash_ip *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip4_elem e = {};
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h);
__be32 ip;
ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
ip &= ip_set_netmask(h->netmask);
if (ip == 0)
return -EINVAL;
e.ip = ip;
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
在kadt回调函数中会调用adtfn函数
ipset_adtfn adtfn = set->variant->adt[adt];
因此其最终调用还是注册的adt回调函数
在ip_set_hash_gen.h文件中有mtype_add这个函数
那系统是在何时调用ip_set_add呢?
在target回调函数中
这点很好理解了,target回调函数可以说是执行一条指令的最后一步,一切都准备就绪,在target回调函数中调用添加、删除、修改等操作,也是有情可原的。
总结起来流程图如下所示
三、uadt回调函数的调用位置
uadt的调用位置有两个
1.call_ad函数
2.IPSET_CBFN(ip_set_utest, struct net *net, struct sock *ctnl,struct sk_buff *skb,const struct nlmsghdr *nlh,
const struct nlattr * const attr[],struct netlink_ext_ack *extack)
其中IPSET_CBFN宏的功能是创建并实现一个以第一个参数为函数名称的函数,即ip_set_utest函数
搜索ip_set_utest函数,得到结果
其中在IPSET_CMD_TEST项对应的call回调函数用ip_set_utest来赋值,那么call何时调用呢?我们猜测是当IPSET_CMD_TEST请求命令到来时调用call,这些call是内核模块调用的。
暂时没有找到call回调函数是谁来调用的