Chinaunix首页 | 论坛 | 博客

分类: LINUX

2014-03-02 15:34:43

 nf_queuenfnetlink.c nfnetlink_queue.c 是要分析的主要文件。我们从netlink_kernel_create函数开始分析,

nfnetlink.c文件中定义。

static int __net_init nfnetlink_net_init(struct net *net)

{

struct sock *nfnl;


nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,

nfnetlink_rcv, NULL, THIS_MODULE); //负责接受用户下发的策略或命令

if (!nfnl)

return -ENOMEM;

net->nfnl_stash = nfnl;

RCU_INIT_POINTER(net->nfnl, nfnl);

return 0;

}

netlink_kernel_create函数的参数分析可知,用户下发的策略,统一交给nfnetlink_rcv处理


该函数也是负责一遍一遍的遍历数据包的内容,找到一个个的netfilter netlink message, 判断消息的合法性,不合法则交给netlink_ack函数, 合法则交给回调函数cb

int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,

struct nlmsghdr *))

{

struct nlmsghdr *nlh;

int err;

while (skb->len >= nlmsg_total_size(0)) { //skb的载荷必须大于nlmsghdr的长度

int msglen;


nlh = nlmsg_hdr(skb);

err = 0;


if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)

return 0;


/* Only requests are handled by the kernel,如果下发的策略不包含NLM_F_REQUEST */

if (!(nlh->nlmsg_flags & NLM_F_REQUEST))

goto ack;


/* Skip control messages nlh->nlmsg_typeNF_queue中它的值得不可能小于10*/

if (nlh->nlmsg_type < NLMSG_MIN_TYPE)

goto ack;


err = cb(skb, nlh);

if (err == -EINTR)

goto skip;


ack:

if (nlh->nlmsg_flags & NLM_F_ACK || err)

netlink_ack(skb, nlh, err);


skip:

msglen = NLMSG_ALIGN(nlh->nlmsg_len);

if (msglen > skb->len)

msglen = skb->len;

skb_pull(skb, msglen); //这里指的是分析下一个nlmsg(在一个数据包内)

}

return 0;

}

EXPORT_SYMBOL(netlink_rcv_skb);


接下来看一下,netlink_ack函数如何执行。它就是发送一个error的消息。


****************************************************************************************

发送的错误消息并没有按照nfattr + 结构的形式。消息类型为NLMSG_ERROR,且nl_type的高八位是0,表明一个错误消息。这不是我们关心的重点,到此为止。

void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)

{

struct sk_buff *skb;

struct nlmsghdr *rep;

struct nlmsgerr *errmsg;

size_t payload = sizeof(*errmsg);


/* error messages get the original request appened */

if (err)

payload += nlmsg_len(nlh); //错误的消息是struct nlmsgerr + message_len(no header)


skb = nlmsg_new(payload, GFP_KERNEL); //创建了skb_buf结构体

if (!skb) { //一般创建也都会成功吧?

struct sock *sk;

sk = netlink_lookup(sock_net(in_skb->sk),

in_skb->sk->sk_protocol,

NETLINK_CB(in_skb).pid);

if (sk) {

sk->sk_err = ENOBUFS;

sk->sk_error_report(sk);

sock_put(sk);

}

return;

}


rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq,

NLMSG_ERROR, payload, 0);

errmsg = nlmsg_data(rep);

errmsg->error = err;

memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh));

netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);

}


真真正正地处理一个Netlink Message的地方。

/* Process one complete nfnetlink message. */

static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)

{

struct net *net = sock_net(skb->sk); //类似socket

const struct nfnl_callback *nc;

const struct nfnetlink_subsystem *ss;

int type, err;


if (security_netlink_recv(skb, CAP_NET_ADMIN))

return -EPERM;


/* All the messages must at least contain nfgenmsg */

if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg)))

return 0;


type = nlh->nlmsg_type;

replay:

rcu_read_lock();

ss = nfnetlink_get_subsys(type); //nl_type的高8位是子系统的下标,在static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT]中就可寻找到nfnetlink_subsystem

if (!ss) {

{

rcu_read_unlock();

return -EINVAL;

}

}

*************************************************************************************************

nfnetlink_subsystem

struct nfnetlink_subsystem {

const char *name;

__u8 subsys_id; /* nfnetlink subsystem ID */

__u8 cb_count; /* number of callbacks */

const struct nfnl_callback *cb; /* callback for individual types */

}; 跟用户态的netlink_subsystem_handle类似

************************************************************************************************

nc = nfnetlink_find_client(type, ss); //这次再根据nl_type的低八位,获得消息的类型.

if (!nc) {

rcu_read_unlock();

return -EINVAL;

}

*********************************************************************************************

struct nfattr结构的指针全部手机起来,到cda中。

{

int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));

u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type);

struct nlattr *cda[ss->cb[cb_id].attr_count + 1];


struct nlattr *attr = (void *)nlh + min_len;

int attrlen = nlh->nlmsg_len - min_len;


err = nla_parse(cda, ss->cb[cb_id].attr_count,

attr, attrlen, ss->cb[cb_id].policy); //是不是也要进行nfattr指针的收集啊?是

if (err < 0)

return err;

***********************************************************************************************

处理数据包


if (nc->call_rcu) { //如果处理这类型的消息的回调函数存在

err = nc->call_rcu(net->nfnl, skb, nlh,

(const struct nlattr **)cda); //调用回调函数.

rcu_read_unlock();

} else { //异常情况,先不考虑.

rcu_read_unlock();

nfnl_lock();

if (rcu_dereference_protected(

subsys_table[NFNL_SUBSYS_ID(type)],

lockdep_is_held(&nfnl_mutex)) != ss ||

nfnetlink_find_client(type, ss) != nc)

err = -EAGAIN;

else

err = nc->call(net->nfnl, skb, nlh,

(const struct nlattr **)cda);

nfnl_unlock();

}

if (err == -EAGAIN)

goto replay;

return err;

}

}


通过以上的分析,可知subsys_table是与nfnl_handle中的struct nfnl_subsys_handle subsys[NFNL_MAX_SUBSYS+1]有许多相似之处的。都存在u_int8_t cb_count; /*cb指向的内存中,结构体nfnl_callback个数*/ struct nfnl_callback *cb; /* 暂时不清楚回调函数的作用 */ 的成员变量。看来,回调函数之间是一一对应的关系

阅读(4161) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~