2018年(21)
分类: LINUX
2018-09-19 15:49:01
内核中采用了面向对象的设计思想,把一些共有的操作函数和属性提取出来包含到一个结构体内,和面向对象的类是一个概念。
数据结构定义,这里介绍几个关键字段
struct nf_conntrack_l3proto {
/* L3 Protocol Family number. ex) PF_INET */
u_int16_t l3proto;
/* Protocol name */
const char *name;
/*从报文的L3头部提取出conntrack 的元组信息*/
bool (*pkt_to_tuple)(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple);
/*conntrack 正反向连接的元组中L3信息的转换函数*/
bool (*invert_tuple)(struct nf_conntrack_tuple *inverse,
const struct nf_conntrack_tuple *orig);
/*获取L4协议类型的函数*/
int (*get_l4proto)(const struct sk_buff *skb, unsigned int nhoff,
unsigned int *dataoff, u_int8_t *protonum);
};
每个网络层的协议要是实现conntrack功能,需要自己实现上面定义的函数,并初始化一个struct nf_conntrack_l3proto实例,并注册到Netfilter管理的数组中。
Netfilter定义了一个全局的数组,来存放各个L3协议注册的nf_conntrack_l3proto实例。
struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly;
注册:
int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto)
{
int ret = 0;
。。。。。。//省略
mutex_lock(&nf_ct_proto_mutex);
/*根据协议类型为数组下标,在全局数组中查看是否重复注册*/
old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],lockdep_is_held(&nf_ct_proto_mutex));
if (old != &nf_conntrack_l3proto_generic) {
ret = -EBUSY;
goto out_unlock;
}
。。。。。。
/*注册到全局数组里*/
rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
out_unlock:
mutex_unlock(&nf_ct_proto_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_l3proto_register);
具体使用大家可以查看IPv4注册实例的代码,这里不介绍了。
struct nf_conntrack_l4proto {
/* L3 Protocol number. */
u_int16_t l3proto;
/* L4 Protocol number. */
u_int8_t l4proto;
/* 从报文的L4头部提取出conntrack 的元组信息 */
bool (*pkt_to_tuple)(const struct sk_buff *skb, unsigned int dataoff,
struct nf_conntrack_tuple *tuple);
/*conntrack 正反向连接的元组中L3信息的转换函数*/
bool (*invert_tuple)(struct nf_conntrack_tuple *inverse,
const struct nf_conntrack_tuple *orig);
/* Returns verdict for packet, or -1 for invalid. */
/*各个协议对报文的额外处理函数*/
int (*packet)(struct nf_conn *ct,
const struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info ctinfo,
u_int8_t pf,
unsigned int hooknum,
unsigned int *timeouts);
/* Called when a new connection for this protocol found;
* returns TRUE if it's OK. If so, packet() called next.
*判断是否根据该报文新建一个conntrack连接*/
bool (*new)(struct nf_conn *ct, const struct sk_buff *skb,
unsigned int dataoff, unsigned int *timeouts);
/*检查报文数据的正确性*/
int (*error)(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb,
unsigned int dataoff, enum ip_conntrack_info *ctinfo,
u_int8_t pf, unsigned int hooknum);
。。。。。。
/* Protocol name */
const char *name;
};
每个传输层的协议要是实现conntrack功能,需要自己实现上面定义的函数,并初始化一个struct nf_conntrack_l4proto实例,并注册到Netfilter管理的数组中。
Netfilter定义了一个全局的数组,来存放各个L4协议注册的nf_conntrack_l4proto实例。
static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly;
注册
int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto)
{
int ret = 0;
。。。。。。
mutex_lock(&nf_ct_proto_mutex);
if (!nf_ct_protos[l4proto->l3proto])
{
/* l3proto may be loaded latter. */
struct nf_conntrack_l4proto __rcu **proto_array;
int i;
/*对应L3下还没有创建L4所用的内存,申请一块*/
proto_array = kmalloc(MAX_NF_CT_PROTO *sizeof(struct nf_conntrack_l4proto *),GFP_KERNEL);
if (proto_array == NULL) {
ret = -ENOMEM;
goto out_unlock;
}
for (i = 0; i < MAX_NF_CT_PROTO; i++)
RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
/* Before making proto_array visible to lockless readers,
* we must make sure its content is committed to memory.
*/
smp_wmb();
nf_ct_protos[l4proto->l3proto] = proto_array;
}
else if (rcu_dereference_protected(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
lockdep_is_held(&nf_ct_proto_mutex)) != &nf_conntrack_l4proto_generic)
{
ret = -EBUSY;
goto out_unlock;
}
l4proto->nla_size = 0;
if (l4proto->nlattr_size)
l4proto->nla_size += l4proto->nlattr_size();
if (l4proto->nlattr_tuple_size)
l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
/*注册到Netfilter中*/
rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],l4proto);
out_unlock:
mutex_unlock(&nf_ct_proto_mutex);
return ret;
}
具体使用大家可以查看IPv4下udp,tcp注册实例的代码,这里不介绍了。
具体位置
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
static int __init nf_conntrack_l3proto_ipv4_init(void)
{
int ret = 0;
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp);
ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
}
(未完待续)