全部博文(140)
分类: LINUX
2014-03-02 15:34:43
nf_queue、nfnetlink.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_type在NF_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; /* 暂时不清楚回调函数的作用 */ 的成员变量。看来,回调函数之间是一一对应的关系。