从ip_queue到nfnetlink_queue(上)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
1. 前言在2.4内核中出现了ip_queue,用于将数据包从内核空间传递到用户空间,其不足之处是只能有一个应用程序接收内核数据。到了2.6.14以后,新增了nfnetlink_queue,理论上可最大可支持65536个应用程序接口,而且可以兼容ip_queue。不过从内核到用户空间的通道还是只有一个,实际上netfilter对每个协议族也只有一个队列,这里说的65536个子队列的实现就象 802.1Q实现VLAN一样是在数据包中设置ID号来区分的,不同ID的包都通过相同的接口传输,只是在两端根据ID号进行了分类处理。2. 用户空间用户空间的支持库包括两个:libnfnetlink和libnetfilter_queue,后者需要前者支持,其源码可到netfilter网站上下载。2.1 数据结构2.1.1/*linux_nfnetlink.h */// 基本属性结构struct nfattr{// 长度 u_int16_t nfa_len;// leix u_int16_t nfa_type; /* we use 15 bits for the type, and the highest * bit to indicate whether the payload is nested */} __attribute__ ((packed));// nf基本消息结构struct nfgenmsg { u_int8_tnfgen_family;/* AF_xxx */ u_int8_tversion;/* nfnetlink version */ u_int16_t res_id;/* resource id */} __attribute__ ((packed));/* nfnl是netfilternetlink的缩写 */// nfnl回调结构struct nfnl_callback{ int (*call)(struct sock *nl, struct sk_buff *skb,struct nlmsghdr *nlh, struct nfattr *cda[], int *errp); u_int16_t attr_count; /* number of nfattr's */};// netfilter netlink子系统结构struct nfnetlink_subsystem{ const char *name; __u8 subsys_id;/* nfnetlink subsystem ID */ __u8 cb_count;/* number of callbacks */ struct nfnl_callback *cb; /* callback for individual types */};2.1.2/*libnfnetlink.h *//* nfnl是netfilternetlink的缩写 */// netfilter的netlink消息头结构struct nfnlhdr { struct nlmsghdr nlh; struct nfgenmsg nfmsg;};// netfilter的netlink回调函数struct nfnl_callback { int (*call)(struct nlmsghdr *nlh, struct nfattr *nfa[], void *data); void *data; u_int16_t attr_count;};2.1.3/* libnfnetlink.c */// netfilter netlink子系统的handle结构struct nfnl_subsys_handle { struct nfnl_handle*nfnlh; u_int32_tsubscriptions; u_int8_tsubsys_id; u_int8_tcb_count; struct nfnl_callback*cb; /* array of callbacks */};#defineNFNL_MAX_SUBSYS 16 /* enough for now */// netfilter netlink的handle结构struct nfnl_handle { int fd; // socket struct sockaddr_nl local;// 本地netlink socket信息 struct sockaddr_nl peer; // 对端的netlink socket信息 u_int32_tsubscriptions; u_int32_tseq; // 序号 u_int32_tdump; struct nlmsghdr*last_nlhdr; // 上一个消息 struct nfnl_subsys_handle subsys; // 各子系统的handle};/*linux_nfnetlink_queue.h *//* nfqnl是netfilter queue netlink的缩写 */// nfqueue_netlink消息类型enum nfqnl_msg_types { NFQNL_MSG_PACKET,/* packet from kernel to userspace */ NFQNL_MSG_VERDICT,/* verdict from userspace to kernel */ NFQNL_MSG_CONFIG,/* connect to a particular queue */ NFQNL_MSG_MAX};// nfqueue_netlink消息包头结构struct nfqnl_msg_packet_hdr { u_int32_t packet_id; /* unique ID of packet in queue */ u_int16_t hw_protocol; /* hw protocol (network order) */ u_int8_t hook;/* netfilter hook */} __attribute__ ((packed));// nfqueue_netlink消息包头的硬件地址信息struct nfqnl_msg_packet_hw { u_int16_t hw_addrlen; u_int16_t _pad; u_int8_t hw_addr;} __attribute__ ((packed));// nfqueue_netlink消息数据包时间戳,都是64位的struct nfqnl_msg_packet_timestamp { aligned_u64 sec; aligned_u64 usec;} __attribute__ ((packed));// nfqueue_netlink属性类型enum nfqnl_attr_type { NFQA_UNSPEC, NFQA_PACKET_HDR, NFQA_VERDICT_HDR,/* nfqnl_msg_verdict_hrd */ NFQA_MARK, /* u_int32_t nfmark */ NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ NFQA_IFINDEX_INDEV,/* u_int32_t ifindex */ NFQA_IFINDEX_OUTDEV,/* u_int32_t ifindex */ NFQA_IFINDEX_PHYSINDEV,/* u_int32_t ifindex */ NFQA_IFINDEX_PHYSOUTDEV, /* u_int32_t ifindex */ NFQA_HWADDR, /* nfqnl_msg_packet_hw */ NFQA_PAYLOAD, /* opaque data payload */ __NFQA_MAX};#define NFQA_MAX (__NFQA_MAX - 1)// nfqueue_netlink数据包的裁定结果头结构struct nfqnl_msg_verdict_hdr { u_int32_t verdict; u_int32_t id;} __attribute__ ((packed));// nfqueue_netlink消息的配置命令类型enum nfqnl_msg_config_cmds { NFQNL_CFG_CMD_NONE, NFQNL_CFG_CMD_BIND, // 和队列绑定 NFQNL_CFG_CMD_UNBIND, // 取消和队列的绑定 NFQNL_CFG_CMD_PF_BIND,// 和协议族绑定 NFQNL_CFG_CMD_PF_UNBIND, // 取消和协议族的绑定};// nfqueue_netlink消息的配置命令结构struct nfqnl_msg_config_cmd { u_int8_t command; /* nfqnl_msg_config_cmds */ u_int8_t _pad; u_int16_t pf;/* AF_xxx for PF_BIND */} __attribute__ ((packed));// nfqueue_netlink消息的配置模式类型enum nfqnl_config_mode { NFQNL_COPY_NONE,// 不拷贝 NFQNL_COPY_META,// 只拷贝头部信息 NFQNL_COPY_PACKET, // 拷贝整个数据包};// nfqueue_netlink消息的配置结构struct nfqnl_msg_config_params { u_int32_t copy_range; u_int8_t copy_mode; /* enum nfqnl_config_mode */} __attribute__ ((packed));// nfqueue_netlink属性类型enum nfqnl_attr_config { NFQA_CFG_UNSPEC, NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ NFQA_CFG_PARAMS,/* nfqnl_msg_config_params */ __NFQA_CFG_MAX};#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1)2.1.4/* libnetfilter_queue.c */// netfilter queue的handle结构struct nfq_handle{ struct nfnl_handle *nfnlh;// nf netlink handle struct nfnl_subsys_handle *nfnlssh; // 子系统的handle struct nfq_q_handle *qh_list; //};// netfilter queue的节点队列结构struct nfq_q_handle{// 下一个节点 struct nfq_q_handle *next;// 该队列的handle struct nfq_handle *h;// id号,每个queue有唯一ID,一共可支持65536个queue u_int16_t id;// typedef intnfq_callback(struct nfq_q_handle *gh, struct nfgenmsg *nfmsg,// struct nfq_data *nfad, void *data);// nf queue的回调函数 nfq_callback *cb;// nf queue的回调函数输入数据指针 void *data;};struct nfq_data { struct nfattr **data;};2.2 接口函数声明2.2.1 libnfnetlink实际上这些函数和宏都是被netfilter_queue的接口函数所包装,一般用户应用程序中不用直接调用这些函数或宏。/* libnfnetlink.h */extern int nfnl_fd(struct nfnl_handle *h);/* get a new library handle */extern struct nfnl_handle *nfnl_open(void);extern int nfnl_close(struct nfnl_handle *);extern struct nfnl_subsys_handle *nfnl_subsys_open(struct nfnl_handle *, u_int8_t, u_int8_t, unsigned int);extern void nfnl_subsys_close(struct nfnl_subsys_handle *);/* sending of data */extern int nfnl_send(struct nfnl_handle *, struct nlmsghdr *);extern int nfnl_sendmsg(const struct nfnl_handle *, const struct msghdr *msg, unsigned int flags);extern int nfnl_sendiov(const struct nfnl_handle *nfnlh, const struct iovec *iov, unsigned int num, unsigned int flags);extern void nfnl_fill_hdr(struct nfnl_subsys_handle *, struct nlmsghdr *, unsigned int, u_int8_t, u_int16_t, u_int16_t, u_int16_t);extern int nfnl_talk(struct nfnl_handle *, struct nlmsghdr *, pid_t, unsigned, struct nlmsghdr *, int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *), void *);/* simple challenge/response */extern int nfnl_listen(struct nfnl_handle *, int (*)(struct sockaddr_nl *, struct nlmsghdr *, void *), void *);/* receiving */extern ssize_t nfnl_recv(const struct nfnl_handle *h, unsigned char *buf, size_t len);extern int nfnl_callback_register(struct nfnl_subsys_handle *, u_int8_t type, struct nfnl_callback *cb);extern int nfnl_callback_unregister(struct nfnl_subsys_handle *, u_int8_t type);extern int nfnl_handle_packet(struct nfnl_handle *, char *buf, int len);/* parsing */extern struct nfattr *nfnl_parse_hdr(const struct nfnl_handle *nfnlh, const struct nlmsghdr *nlh, struct nfgenmsg **genmsg);extern int nfnl_check_attributes(const struct nfnl_handle *nfnlh, const struct nlmsghdr *nlh, struct nfattr *tb[]);extern struct nlmsghdr *nfnl_get_msg_first(struct nfnl_handle *h, const unsigned char *buf, size_t len);extern struct nlmsghdr *nfnl_get_msg_next(struct nfnl_handle *h, const unsigned char *buf, size_t len);#define nfnl_attr_present(tb, attr) \ (tb)#define nfnl_get_data(tb, attr, type) \ ({ type __ret = 0; \if (tb) \__ret = *(type *)NFA_DATA(tb);\__ret; \})#define nfnl_get_pointer_to_data(tb, attr, type) \ ({ type *__ret = NULL; \if (tb) \__ret = NFA_DATA(tb); \__ret; \})/* nfnl attribute handling functions */extern int nfnl_addattr_l(struct nlmsghdr *, int, int, void *, int);extern int nfnl_addattr16(struct nlmsghdr *, int, int, u_int16_t);extern int nfnl_addattr32(struct nlmsghdr *, int, int, u_int32_t);extern int nfnl_nfa_addattr_l(struct nfattr *, int, int, void *, int);extern int nfnl_nfa_addattr16(struct nfattr *, int, int, u_int16_t);extern int nfnl_nfa_addattr32(struct nfattr *, int, int, u_int32_t);extern int nfnl_parse_attr(struct nfattr **, int, struct nfattr *, int);#define nfnl_parse_nested(tb, max, nfa) \ nfnl_parse_attr((tb), (max), NFA_DATA((nfa)), NFA_PAYLOAD((nfa)))#define nfnl_nest(nlh, bufsize, type) \({ struct nfattr *__start = NLMSG_TAIL(nlh);\ nfnl_addattr_l(nlh, bufsize, (NFNL_NFA_NEST | type), NULL, 0);\ __start; })#define nfnl_nest_end(nlh, tail) \({ (tail)->nfa_len = (void *) NLMSG_TAIL(nlh) - (void *) tail; })extern void nfnl_build_nfa_iovec(struct iovec *iov, struct nfattr *nfa, u_int16_t type, u_int32_t len, unsigned char *val);extern unsigned int nfnl_rcvbufsiz(struct nfnl_handle *h, unsigned int size);extern void nfnl_dump_packet(struct nlmsghdr *, int, char *);2.2.2 libnetfilter_queue/* libnetfilter_queue.h */// 打开一个nfqueue的handle,返回NULL表示失败extern struct nfq_handle *nfq_open(void);// 打开nf netlink handle对应的nfqueueextern struct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh);// 关闭nfqueueextern int nfq_close(struct nfq_handle *h);// 对nfqueue绑定协议族extern int nfq_bind_pf(struct nfq_handle *h, u_int16_t pf);// 对nfqueue取消协议族绑定extern int nfq_unbind_pf(struct nfq_handle *h, u_int16_t pf);// 建立具体的queue的handle,由num指定queue的序号, 返回NULL失败extern struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, u_int16_t num, nfq_callback *cb, void *data);// 释放队列extern int nfq_destroy_queue(struct nfq_q_handle *qh);// 处理队列的数据包extern int nfq_handle_packet(struct nfq_handle *h, char *buf, int len);// 设置queue handle的数据拷贝模式extern int nfq_set_mode(struct nfq_q_handle *qh, u_int8_t mode, unsigned int len);// 设置数据包的判定结果, id用于指定具体的包, buf和data_len用于传递修改后的数据extern int nfq_set_verdict(struct nfq_q_handle *qh, u_int32_t id, u_int32_t verdict, u_int32_t data_len, unsigned char *buf);// 设置数据包的mark值extern int nfq_set_verdict_mark(struct nfq_q_handle *qh, u_int32_t id, u_int32_t verdict, u_int32_t mark, u_int32_t datalen, unsigned char *buf);/* message parsing function */// 从缓冲区原始数据中返回消息头结构extern struct nfqnl_msg_packet_hdr * nfq_get_msg_packet_hdr(struct nfq_data *nfad);// 获取数据的mark信息extern u_int32_t nfq_get_nfmark(struct nfq_data *nfad);extern int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv);/* return 0 if not set */// 返回数据包进入网卡的索引号extern u_int32_t nfq_get_indev(struct nfq_data *nfad);// 返回数据包进入的物理网卡的索引号extern u_int32_t nfq_get_physindev(struct nfq_data *nfad);// 返回数据包发出网卡的索引号extern u_int32_t nfq_get_outdev(struct nfq_data *nfad);// 返回数据包发出的物理网卡的索引号extern u_int32_t nfq_get_physoutdev(struct nfq_data *nfad);// 返回数据包硬件地址extern struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad);/* return -1 if problem, length otherwise */// 获取数据包中载荷地址extern int nfq_get_payload(struct nfq_data *nfad, char **data);2.3 netfilter queue接口函数的实现/* libnetfilter_queue.c */// 删除一个queue节点// 各个nfq_q_handle结构都是其nfq_handle中的qh_list链表中的一个节点// 所以删除节点就是将其从链表中移出即可,该函数不进行内存释放操作// 结构可表示如下:// nfq_handle -> qh_list// ^ |// | V// | nfq_q_handle -> nfq_q_handle -> ...// | | |// |______________|_______________|_________________static void del_qh(struct nfq_q_handle *qh){ struct nfq_q_handle *cur_qh, *prev_qh = NULL; for (cur_qh = qh->h->qh_list; cur_qh; cur_qh = cur_qh->next) {if (cur_qh == qh) { if (prev_qh) prev_qh->next = qh->next; else qh->h->qh_list = qh->next; return;}prev_qh = cur_qh; }}// 把一个nfq_q_handle结构添加到链表中static void add_qh(struct nfq_q_handle *qh){ qh->next = qh->h->qh_list; qh->h->qh_list = qh;}// 根据ID号找nfq_q_handle节点static struct nfq_q_handle *find_qh(struct nfq_handle *h, u_int16_t id){ struct nfq_q_handle *qh; for (qh = h->qh_list; qh; qh = qh->next) {if (qh->id == id) return qh; } return NULL;}/* build a NFQNL_MSG_CONFIG message */// 向netlink socket发送配置信息,该函数是static的,外部函数不可见 static int__build_send_cfg_msg(struct nfq_handle *h, u_int8_t command,u_int16_t queuenum, u_int16_t pf){ char buf; struct nfqnl_msg_config_cmd cmd; struct nlmsghdr *nmh = (struct nlmsghdr *) buf; nfnl_fill_hdr(h->nfnlssh, nmh, 0, AF_UNSPEC, queuenum, NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK); cmd.command = command; cmd.pf = htons(pf); nfnl_addattr_l(nmh, sizeof(buf), NFQA_CFG_CMD, &cmd, sizeof(cmd)); return nfnl_talk(h->nfnlh, nmh, 0, 0, NULL, NULL, NULL);}// 接收netlink数据包,该函数是也static的,外部函数不可见static int __nfq_rcv_pkt(struct nlmsghdr *nlh, struct nfattr *nfa[],void *data){ struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); struct nfq_handle *h = data; u_int16_t queue_num = ntohs(nfmsg->res_id);// 根据ID找到nfq_q_handle 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;// 调用nfq_q_handle的回调函数 return qh->cb(qh, nfmsg, &nfqa, qh->data);}// 固定的回调结构static struct nfnl_callback pkt_cb = { .call= &__nfq_rcv_pkt, .attr_count = NFQA_MAX,};/* public interface */// 返回nfq_handle的netlink handlestruct nfnl_handle *nfq_nfnlh(struct nfq_handle *h){ return h->nfnlh;} // 返回nfq_handle的netlink handle的netlink套接字int nfq_fd(struct nfq_handle *h){ return nfnl_fd(nfq_nfnlh(h));}struct nfq_handle *nfq_open(void){// 先打开netlink handle struct nfnl_handle *nfnlh = nfnl_open(); struct nfq_handle *qh; if (!nfnlh)return NULL;// 再调用nfq_open_nfnl()打开nf queue handle qh = nfq_open_nfnl(nfnlh); if (!qh)nfnl_close(nfnlh); return qh;}// 已存在netlink handle时打开nfq_handlestruct nfq_handle *nfq_open_nfnl(struct nfnl_handle *nfnlh){ struct nfq_handle *h; int err;// 分配内存 h = malloc(sizeof(*h)); if (!h)return NULL; memset(h, 0, sizeof(*h));// 把nfq_handle和netlink handle连接起来 h->nfnlh = nfnlh;// 打开NFNL_SUBSYS_QUEUE类型的子系统 h->nfnlssh = nfnl_subsys_open(h->nfnlh, NFNL_SUBSYS_QUEUE, NFQNL_MSG_MAX, 0); if (!h->nfnlssh) {/* FIXME: nfq_errno */goto out_free; }// 登记回调处理函数 pkt_cb.data = h; err = nfnl_callback_register(h->nfnlssh, NFQNL_MSG_PACKET, &pkt_cb); if (err < 0) {nfq_errno = err;goto out_close; } return h;out_close: nfnl_subsys_close(h->nfnlssh);out_free: free(h); return NULL;}int nfq_close(struct nfq_handle *h){ int ret;// 关闭子系统nfnl_subsys_close(h->nfnlssh);// 关闭netlink handle ret = nfnl_close(h->nfnlh); if (ret == 0)free(h); return ret;}/* bind nf_queue from a specific protocol family */// 以下函数均是调用__build_send_cfg_msg()函数向内核发送消息命令int nfq_bind_pf(struct nfq_handle *h, u_int16_t pf){ return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_BIND, 0, pf);}/* unbind nf_queue from a specific protocol family */int nfq_unbind_pf(struct nfq_handle *h, u_int16_t pf){ return __build_send_cfg_msg(h, NFQNL_CFG_CMD_PF_UNBIND, 0, pf);}/* bind this socket to a specific queue number */// 生成一个号码为num的队列struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h,u_int16_t num,nfq_callback *cb,void *data){ int ret; struct nfq_q_handle *qh; if (find_qh(h, num))return NULL;// 分配queue节点空间, 设置相应参数 qh = malloc(sizeof(*qh)); memset(qh, 0, sizeof(*qh)); qh->h = h; qh->id = num; qh->cb = cb; qh->data = data;ret = __build_send_cfg_msg(h, NFQNL_CFG_CMD_BIND, num, 0); if (ret < 0) {nfq_errno = ret;free(qh);return NULL; }// 添加到队列中 add_qh(qh); return qh;}/* unbind this socket from a specific queue number */// 释放队列int nfq_destroy_queue(struct nfq_q_handle *qh){ int ret = __build_send_cfg_msg(qh->h, NFQNL_CFG_CMD_UNBIND, qh->id, 0); if (ret == 0) {del_qh(qh);free(qh); } return ret;}int nfq_handle_packet(struct nfq_handle *h, char *buf, int len){// 实际就是netlink处理包 return nfnl_handle_packet(h->nfnlh, buf, len);}int nfq_set_mode(struct nfq_q_handle *qh,u_int8_t mode, u_int32_t range){ char buf; struct nfqnl_msg_config_params params; struct nlmsghdr *nmh = (struct nlmsghdr *) buf; nfnl_fill_hdr(qh->h->nfnlssh, nmh, 0, AF_UNSPEC, qh->id, NFQNL_MSG_CONFIG, NLM_F_REQUEST|NLM_F_ACK); params.copy_range = htonl(range); params.copy_mode = mode; nfnl_addattr_l(nmh, sizeof(buf), NFQA_CFG_PARAMS, &params, sizeof(params)); return nfnl_talk(qh->h->nfnlh, nmh, 0, 0, NULL, NULL, NULL);}static int __set_verdict(struct nfq_q_handle *qh, u_int32_t id,u_int32_t verdict, u_int32_t mark, int set_mark,u_int32_t data_len, unsigned char *data){ struct nfqnl_msg_verdict_hdr vh; char buf; struct nlmsghdr *nmh = (struct nlmsghdr *) buf; struct iovec iov; int nvecs; /* This must be declared here (and not inside the data* handling block) because the iovec points to this. */ struct nfattr data_attr; memset(iov, 0, sizeof(iov));// 设置裁定结果头 vh.verdict = htonl(verdict); vh.id = htonl(id); nfnl_fill_hdr(qh->h->nfnlssh, nmh, 0, AF_UNSPEC, qh->id, NFQNL_MSG_VERDICT, NLM_F_REQUEST); /* add verdict header */ nfnl_addattr_l(nmh, sizeof(buf), NFQA_VERDICT_HDR, &vh, sizeof(vh));// 设置数据包的mark值 if (set_mark)nfnl_addattr32(nmh, sizeof(buf), NFQA_MARK, mark); iov.iov_base = nmh; iov.iov_len = NLMSG_TAIL(nmh) - (void *)nmh; nvecs = 1; if (data_len) {// 如果数据进行修改要传回内核,相应将数据添加到要发送到内核的数据向量中nfnl_build_nfa_iovec(&iov, &data_attr, NFQA_PAYLOAD, data_len, data);nvecs += 2;/* Add the length of the appended data to the message * header.The size of the attribute is given in the * nfa_len field and is set in the nfnl_build_nfa_iovec() * function. */nmh->nlmsg_len += data_attr.nfa_len; }// 向内核发送数据向量 return nfnl_sendiov(qh->h->nfnlh, iov, nvecs, 0);}int nfq_set_verdict(struct nfq_q_handle *qh, u_int32_t id,u_int32_t verdict, u_int32_t data_len,unsigned char *buf){ return __set_verdict(qh, id, verdict, 0, 0, data_len, buf);} int nfq_set_verdict_mark(struct nfq_q_handle *qh, u_int32_t id,u_int32_t verdict, u_int32_t mark,u_int32_t datalen, unsigned char *buf){ return __set_verdict(qh, id, verdict, mark, 1, datalen, buf);}/************************************************************* * Message parsing functions *************************************************************/// 以下函数均是调用nfnl_get_pointer_to_data()和nfnl_get_data()函数获取// 指定数据struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad){ return nfnl_get_pointer_to_data(nfad->data, NFQA_PACKET_HDR, struct nfqnl_msg_packet_hdr);}uint32_t nfq_get_nfmark(struct nfq_data *nfad){ return ntohl(nfnl_get_data(nfad->data, NFQA_MARK, u_int32_t));}int nfq_get_timestamp(struct nfq_data *nfad, struct timeval *tv){ struct nfqnl_msg_packet_timestamp *qpt; qpt = nfnl_get_pointer_to_data(nfad->data, NFQA_TIMESTAMP, struct nfqnl_msg_packet_timestamp); if (!qpt)return -1; tv->tv_sec = __be64_to_cpu(qpt->sec); tv->tv_usec = __be64_to_cpu(qpt->usec); return 0;}/* all nfq_get_*dev() functions return 0 if not set, since linux only allows * ifindex >= 1, see net/core/dev.c:2600(in 2.6.13.1) */u_int32_t nfq_get_indev(struct nfq_data *nfad){ return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_INDEV, u_int32_t));}u_int32_t nfq_get_physindev(struct nfq_data *nfad){ return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSINDEV, u_int32_t));}u_int32_t nfq_get_outdev(struct nfq_data *nfad){ return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_OUTDEV, u_int32_t));}u_int32_t nfq_get_physoutdev(struct nfq_data *nfad){ return ntohl(nfnl_get_data(nfad->data, NFQA_IFINDEX_PHYSOUTDEV, u_int32_t));}struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad){ return nfnl_get_pointer_to_data(nfad->data, NFQA_HWADDR, struct nfqnl_msg_packet_hw);}int nfq_get_payload(struct nfq_data *nfad, char **data){ *data = nfnl_get_pointer_to_data(nfad->data, NFQA_PAYLOAD, char); if (*data)return NFA_PAYLOAD(nfad->data); return -1;}2.4 程序实例/* nfqnl_test.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <netinet/in.h>#include <linux/netfilter.h> /* for NF_ACCEPT */#include <libnetfilter_queue/libnetfilter_queue.h>/* returns packet id */// 对数据包的处理函数, 本示例仅用于打印数据包的信息static u_int32_t print_pkt (struct nfq_data *tb){ int id = 0; struct nfqnl_msg_packet_hdr *ph; u_int32_t mark,ifi; int ret; char *data;// 提取数据包头信息,包括id,协议和hook点信息 ph = nfq_get_msg_packet_hdr(tb); if (ph){ id = ntohl(ph->packet_id); printf("hw_protocol=0x%04x hook=%u id=%u ", ntohs(ph->hw_protocol), ph->hook, id); }// 获取数据包的mark值, 也就是内核skb的nfmark值 mark = nfq_get_nfmark(tb); if (mark) printf("mark=%u ", mark);// 获取数据包的进入网卡的索引号 ifi = nfq_get_indev(tb); if (ifi) printf("indev=%u ", ifi);// 获取数据包的发出网卡的索引号 ifi = nfq_get_outdev(tb); if (ifi) printf("outdev=%u ", ifi);// 获取数据包载荷,data指针指向载荷,从实际的IP头开始 ret = nfq_get_payload(tb, &data); if (ret >= 0) printf("payload_len=%d ", ret); fputc('\n', stdout); return id;} // 回调函数定义, 基本结构是先处理包,然后返回裁定static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data){ // 数据包处理 u_int32_t id = print_pkt(nfa); printf("entering callback\n");// 设置裁定 return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);} int main(int argc, char **argv){ struct nfq_handle *h; struct nfq_q_handle *qh; struct nfnl_handle *nh; int fd; int rv; char buf; printf("opening library handle\n");// 打开nfq_handle h = nfq_open(); if (!h) { fprintf(stderr, "error during nfq_open()\n"); exit(1); } printf("unbinding existing nf_queue handler for AF_INET (if any)\n");// 先解开和AF_INET的绑定 if (nfq_unbind_pf(h, AF_INET) < 0) { fprintf(stderr, "error during nfq_unbind_pf()\n"); exit(1); } printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");// 绑定到AF_INET if (nfq_bind_pf(h, AF_INET) < 0) { fprintf(stderr, "error during nfq_bind_pf()\n"); exit(1); } printf("binding this socket to queue '0'\n");// 建立nfq_q_handle, 号码是0, 回调函数是cb// 可建立多个queue,用不同的号码区分即可 qh = nfq_create_queue(h,0, &cb, NULL); if (!qh) { fprintf(stderr, "error during nfq_create_queue()\n"); exit(1); } printf("setting copy_packet mode\n");// 设置数据拷贝模式, 全包拷贝 if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) { fprintf(stderr, "can't set packet_copy mode\n"); exit(1); } nh = nfq_nfnlh(h); fd = nfnl_fd(nh);// 从netlink套接字接收数据 while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) { printf("pkt received\n");// 处理数据,最终会调用到相应的回调函数 nfq_handle_packet(h, buf, rv); } printf("unbinding from queue 0\n");// 释放队列 nfq_destroy_queue(qh); #ifdef INSANE /* normally, applications SHOULD NOT issue this command, since * it detaches other programs/sockets from AF_INET, too ! */ printf("unbinding from AF_INET\n"); nfq_unbind_pf(h, AF_INET);#endif printf("closing library handle\n");// 关闭nfq_handle nfq_close(h); exit(0);}2.4 包装libipq可用netlink_queue包装libipq,ipq就相当于是号码为0的一个nfqueue而已:/* libipq_compat.c */struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol){ int status; struct ipq_handle *h; h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle)); if (h == NULL) {ipq_errno = IPQ_ERR_HANDLE;return NULL; }memset(h, 0, sizeof(struct ipq_handle));// 打开ipq的nfqueue handle h->nfqnlh = nfq_open(); if (!h->nfqnlh) {ipq_errno = IPQ_ERR_SOCKET;goto err_free; }// 绑定到PF_INET或PF_INET6 if (protocol == PF_INET)status = nfq_bind_pf(h->nfqnlh, PF_INET); else if (protocol == PF_INET6)status = nfq_bind_pf(h->nfqnlh, PF_INET6); else {ipq_errno = IPQ_ERR_PROTOCOL;goto err_close; } h->family = protocol; if (status < 0) {ipq_errno = IPQ_ERR_BIND;goto err_close; }// 按号码0建立queue,无回调函数,数据包由ipq直接读后处理 h->qh = nfq_create_queue(h->nfqnlh, 0, NULL, NULL); if (!h->qh) {ipq_errno = IPQ_ERR_BIND;goto err_close; } return h;err_close: nfq_close(h->nfqnlh);err_free: free(h); return NULL;}/* * No error condition is checked here at this stage, but it may happen * if/when reliable messaging is implemented. */int ipq_destroy_handle(struct ipq_handle *h){ if (h) {nfq_close(h->nfqnlh);free(h); } return 0;}int ipq_set_mode(const struct ipq_handle *h, u_int8_t mode, size_t range){ return nfq_set_mode(h->qh, mode, range);}/* * timeout is in microseconds (1 second is 1000000 (1 million) microseconds) * */// ipq_read包装得有点疑问,实际没进行接收操作,需要显式的recv接收数据包// 现在的ipq_read只是对接收的数据进行解析ssize_t ipq_read(const struct ipq_handle *h, unsigned char *buf, size_t len, int timeout){ struct nfattr *tb; struct nlmsghdr *nlh = (struct nlmsghdr *)buf; struct nfgenmsg *msg = NULL; struct nfattr *nfa; //return ipq_netlink_recvfrom(h, buf, len, timeout);/* This really sucks.We have to copy the whole packet* in order to build a data structure that is compatible to* the old ipq interface... */ nfa = nfnl_parse_hdr(nfq_nfnlh(h->nfqnlh), nlh, &msg); if (!msg || !nfa)return 0; if (msg->nfgen_family != h->family)return 0;nfnl_parse_attr(tb, NFQA_MAX, nfa, 0xffff); return 0;}int ipq_message_type(const unsigned char *buf){ return ((struct nlmsghdr*)buf)->nlmsg_type;}int ipq_get_msgerr(const unsigned char *buf){ struct nlmsghdr *h = (struct nlmsghdr *)buf; struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); return -err->error;}ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf){ return NLMSG_DATA((struct nlmsghdr *)(buf));}int ipq_set_verdict(const struct ipq_handle *h, ipq_id_t id, unsigned int verdict, size_t data_len, unsigned char *buf){ return nfq_set_verdict(h->qh, id, verdict, data_len, buf);}/* Not implemented yet */int ipq_ctl(const struct ipq_handle *h, int request, ...){ return 1;}char *ipq_errstr(void){ return ipq_strerror(ipq_errno);}void ipq_perror(const char *s){ if (s)fputs(s, stderr); elsefputs("ERROR", stderr); if (ipq_errno)fprintf(stderr, ": %s", ipq_errstr()); if (errno)fprintf(stderr, ": %s", strerror(errno)); fputc('\n', stderr);}...... 待续 ......发表于: 2006-11-13,修改于: 2006-11-13 09:27,已浏览4492次,有评论12条 推荐 投诉网友: wolf_jack@163.com 时间:2007-10-16 17:09:52 IP地址:203.187.169.★如文,从用户空间到内核的路还是只有一条,请问,nf_queue和ip_queue的关系到底是怎样的?2.6.14仍然支持ipq,并可用libipq开发用户空间程序,那么nf_queue的架构在其中做了什么呢?非常感谢你在BLOG做作的精彩评述:)网友: yfydz 时间:2007-10-17 08:48:03 IP地址:218.247.216.★本blog有专门文章比较这两个的,自己找吧网友: ecjtubaowp 时间:2008-07-12 15:48:25 IP地址:125.77.254.★你能详细讲解一下在redhat EL4下安装libnetfilter_queue的步骤吗,谢谢了???网友: yfydz 时间:2008-07-16 18:43:19 IP地址:58.31.248.★这种东西自己摸索一下就有了
页:
[1]