如图所示,netfilter和用户空间进行通信使用的是两个socket的系统调用,把用户空间的地址传给内核,内核使用copy_from_user和copy_to_user来进行数据的传递。
我们先来看一下系统调用setsockopt和getsock_opt的定义。
#include
intgetsockopt(intsockfd,intlevel,intoptname,void*optval,socklen_t*optlen);
intsetsockopt(intsockfd,intlevel,intoptname,constvoid*optval,socklen_toptlen);
sockfd指向一个打开的套接口描述符,level一般是标识协议。optname是命令字,optval指向一个地址,通过它来传递/存储数据。optlen是要传递数据的长度.
基于socksetopt和sockgetopt系统调用的机制,Netfilte提供了一个基本框架,允许不同协议的防火墙来自己实现自己和用户空间的通信函数。
structnf_sockopt_ops
{
structlist_headlist;
u_int8_tpf;/*最小和最大set命令字*/
intset_optmin;
intset_optmax;/*处理socksetopt系统调用的函数,由防火墙自己实现*/
int(*set)(structsock*sk,intoptval,void__user*user,unsignedintlen);
int(*compat_set)(structsock*sk,intoptval,void__user*user,unsignedintlen);/*最小和最大get命令字*/
intget_optmin;
intget_optmax;/*处理sockgetopt系统调用的函数,由防火墙自己实现*/
int(*get)(structsock*sk,intoptval,void__user*user,int*len);
int(*compat_get)(structsock*sk,intoptval,
void__user*user,int*len);
/*Usethemodulestructtolockset/getcodeinplace*/
structmodule*owner;
};
Netfilter中每个协议自己初始化一个自己的nf_sockopt_ops实例,把它注册到netfilter管理的链表中。用户空间发起和内核通信时,Netfilter根据各个协议的命令字范围来找到相应的
nf_sockopt_ops实例,调用实例中对应的set/get处理函数,来进行数据的处理。
staticLIST_HEAD(nf_sockopts);
intnf_register_sockopt(structnf_sockopt_ops*reg)
structnf_sockopt_ops*ops;
intret=0;
if(mutex_lock_interruptible(&nf_sockopt_mutex)!=0)
return-EINTR;/*检查要注册的nf_sockopt_ops的命令字范围是否和已注册的重复*/
list_for_each_entry(ops,&nf_sockopts,list){
if(ops->pf==reg->pf
&&(overlap(ops->set_optmin,ops->set_optmax,
reg->set_optmin,reg->set_optmax)
||overlap(ops->get_optmin,ops->get_optmax,reg->get_optmin,reg->get_optmax))){
NFDEBUG("nf_sockoverlap:%u-%u/%u-%uv%u-%u/%u-%u\n",
ops->set_optmin,ops->set_optmax,
ops->get_optmin,ops->get_optmax,
reg->set_optmin,reg->set_optmax,
reg->get_optmin,reg->get_optmax);
ret=-EBUSY;
gotoout;
}
/*把nf_sockopt_ops实例加入到netfilter管理的全局链表上*/
list_add(->list,&nf_sockopts);
out:
mutex_unlock(&nf_sockopt_mutex);
returnret;
staticstructnf_sockopt_ops*nf_sockopt_find(structsock*sk,u_int8_tpf,intval,intget)