Chinaunix首页 | 论坛 | 博客

分类: LINUX

2014-03-02 15:30:46

用户态接收数据包流程

我们找到了一个函数调用链:recv → nfq_handle_packet → nfnl_handle_packet → __nfnl_handle_msg → ssh->cb[type].call() → __nfq_rcv_pkt → 用户定义的回调函数。

用户调用recv接收数据包,调用 nfq_handle_packet处理数据包。nfq_handle_packet直接调用了 nfnl_handle_packet函数。

while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {


nfq_handle_packet(h, buf, rv); /**nfq_create_queue里面的回调函数将会被调用*/


}

nfnl_handle_packet

该函数就是在buff中一遍遍的找到netlink message的头,并传递给__nfnl_handle_msg。注意,若__nfnl_handle_msg返回值小于0,那么函数直接返回。这也是我们判断用户注册的回调函数的返回值的标准

int nfnl_handle_packet(struct nfnl_handle *h, char *buf, int len)


{


while (len >= NLMSG_SPACE(0)) {


u_int32_t rlen;


struct nlmsghdr *nlh = (struct nlmsghdr *)buf;


if (nlh->nlmsg_len < sizeof(struct nlmsghdr)


|| len < nlh->nlmsg_len)


return -1;


rlen = NLMSG_ALIGN(nlh->nlmsg_len);


if (rlen > len)


rlen = len;


if (__nfnl_handle_msg(h, nlh, rlen) < 0)


return -1;


len -= rlen;

buf += rlen;

}


return 0;

}

__nfnl_handle_msg

static int __nfnl_handle_msg(struct nfnl_handle *h, struct nlmsghdr *nlh,

int len)

{

struct nfnl_subsys_handle *ssh;

u_int8_t type = NFNL_MSG_TYPE(nlh->nlmsg_type);

u_int8_t subsys_id = NFNL_SUBSYS_ID(nlh->nlmsg_type);


int err = 0;


if (subsys_id > NFNL_MAX_SUBSYS)

return -1;


ssh = &h->subsys[subsys_id];

//找到子系统的nfnl_subsys_hanle结构体.


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

return -1;


if (type >= ssh->cb_count)

return -1;


if (ssh->cb[type].attr_count) { //子系统的某一类消息,如果存在属性的话。就会创建一个struct nfattr

* []的数组。

struct nfattr *nfa[ssh->cb[type].attr_count];


err = nfnl_check_attributes(h, nlh, nfa); //将属性的头指针都放到nfa当中

if (err < 0)

return err;

if (ssh->cb[type].call)

return ssh->cb[type].call(nlh, nfa, ssh->cb[type].data);

//调用我们所谓的统筹调度函数

}


return 0;

}


             __nfq_rcv_pkt

static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data)

{

struct nfgenmsg *nfmsg = NLMSG_DATA(nlh);

struct nfq_handle *h = data; //pkt_cb中将data指向了nfq_handle.

u_int16_t queue_num = ntohs(nfmsg->res_id); //数据包所属的queue_num


/*nfq_handle中的nfq_q_handle *qh_list是一个链表结构,链表中元素的添加我们稍后讲,find_qh就是遍历链表中的每个元素,与之id进行比较。并返回链表中的节点.

struct nfq_q_handle *qh = find_qh(h, queue_num);

struct nfq_data nfqa;


if (!qh)

return -ENODEV;

if (!qh->cb)

return -ENODEV;

nfqa.data = nfa;


return qh->cb(qh, nfmsg, &nfqa, qh->data);

}

static struct nfnl_callback pkt_cb = {

.call = &__nfq_rcv_pkt,

.attr_count = NFQA_MAX,

};


此时用户注册的回调函数终于被调用了。

综上所述:nf_queue仍然使用的netlink机制与内核之间进行通信,不同的是nf_queue根据不同的queue_num,将数据包传递给不同的用户注册的回调函数。但是请注意,它们共用一个socket套接字。

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