netfilter_queue 总结

本文详细介绍了libnetfilter_queue库的主要函数及其用途,包括打开句柄、创建队列、设置处理模式、处理数据包等。提供了一个示例程序展示了如何使用这些函数来接收和处理网络数据包,并对数据包进行裁决。libnetfilter_queue库允许用户空间程序拦截和处理网络流量,实现自定义的网络过滤和策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请注明出处

libnetfilter_queue-1.0.5.tar.bz2官网下载链接

函数部分

  1. nfq_open():打开一个nfqueue的句柄。
struct nfq_handle *nfq_open(void);

参数

  • return:生成的句柄
  1. nfq_close():关闭nfq_open()创建的句柄。
int nfq_close(struct nfq_handle *h);

参数

  • h:nfq_open()创建的句柄
  • return: 0 成功,非0失败
  1. nfq_unbind_pf() 将给定队列连接句柄与处理中的属于某个特定协议家族的数据包解除绑定。
int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf);

参数

  • h:nfq_open()创建的句柄
  • pf:将被解绑的协议家族
  • return:0 成功,非0失败
  1. nfq_unbind_pf() 将给定队列连接句柄与处理中的属于某个特定协议家族的数据包进行绑定。
int nfq_bind_pf(struct nfq_handle *h, uint16_t pf);

参数:

  • h:nfq_open()创建的句柄
  • pf:将被绑定的协议家族
  • return:0 成功,非0失败
  1. nfq_create_queue()
struct nfq_q_handle *nfq_create_queue(struct nfq_handle *h, uint16_t num, nfq_callback *cb, void *data);

参数:

  • h:nfq_open()创建的句柄
  • num带绑定的队列的编号
  • cb: 回调函数
  • data:传递给回调函数的参数
  • return:一个新创建的nfq_q_handle的指针
  1. nfq_set_mode() 复制到用户的方式,丢弃,复制元数据,复制整个数据包
int nfq_set_mode(struct nfq_q_handle *qh, uint8_t mode, uint32_t range);

参数:

  • qh:nfq_create_queue()创建的句柄。
  • mode想要使用数据包的模式
    • NFQNL_COPY_NONE- 丢弃
    • NFQNL_COPY_META- 复制元数据
    • NFQNL_COPY_PACKET- 复制整个数据包
  • range:我们期望得到的数据包的大小
  • return: -1失败,>0其他情况。
  1. nfq_fd()得到nfqueue句柄相关的文件描述符。
int nfq_fd(struct nfq_handle *h);

参数:

  • h:nfq_open()获得的句柄
  • return:与nfqueue句柄相关的文件描述符。
  1. nfq_handle_packet() 处理从nfqueue子系统收到的数据包
int nfq_handle_packet(struct nfq_handle *h, char *buf, int len);

参数:

  • h:nfq_open()获得的数据包
  • buf:传递给回调函数的数据
  • len:bufpacket数据的大小
  1. nfq_destroy_queue() 摧毁nfq_create_queue()创建的队列。摧毁的同时会自动解绑,所以不需要调用nfq_unbind_pf()
int nfq_destroy_queue(struct nfq_q_handle *qh);

参数:

  • qh:被摧毁的nfq_create_queue()句柄
  • return:
  1. nfq_get_msg_packet_hdr() 解析消息的函数,返回包装数据的头部部分。
struct nfqnl_msg_packet_hdr *nfq_get_msg_packet_hdr(struct nfq_data *nfad);

参数:

  • nfq_data:将要传递给回调函数的Netlink数据包的句柄
struct nfqnl_msg_packet_hdr {
		uint32_t	packet_id;	// 数据包在队列中的唯一id标志符
		uint16_t	hw_protocol;	// hw协议
		uint8_t		hook;		// netfilter 钩子
	} __attribute__ ((packed));
  1. get hardware address() 获得硬件地址
struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad);

参数:

  • nfad:将要传递给回调函数的Netlink数据包的句柄
  • return:硬件地址。在以太网中就是MAC地址。 直到POSTROUTING和成功的ARP请求之后,才知道目标MAC地址,因此当前无法获得。
  1. nfq_get_nfmark() 获得数据包的标志
uint32_t nfq_get_nfmark(struct nfq_data *nfad);

参数:

  • nfad:将要传递给回调函数的Netlink数据包
  • return:分配给这个数据包的标志
  1. nfq_get_indev() 获取接受数据包的索引信息。
uint32_t nfq_get_indev(struct nfq_data *nfad);

参数:

  • nfad:将要传递给回调函数的Netlink数据包
  • return:接收排队数据包的设备的索引。 如果返回的索引为0,则说明该数据包是在本地生成的,或者输入接口未知(即OSTROUTING未知)。
  1. nfq_get_outdev() 获取接受数据包的索引信息。
uint32_t nfq_get_outdev(struct nfq_data *nfad);

参数:

  • nfad:将要传递给回调函数的Netlink数据包
  • return:排队的数据包将被发送出去的设备的索引。 如果返回的索引为0,则该数据包将发送到本机或尚不知道输出接口(即REROUTING未知)。
  1. nfq_get_physindev() 获取接受的数据包的物理层接口信息。
uint32_t nfq_get_physindev(struct nfq_data *nfad);

参数:

  • nfad:将要传递给回调函数的Netlink数据包
  • return:接收排队数据包的物理设备的索引。 如果返回的索引为0,则说明该数据包是在本地生成的,或者输入接口未知(即OSTROUTING未知)。
  1. nfq_get_outdev() 获取即将发送的数据包的物理层接口信息。
uint32_t nfq_get_outdev(struct nfq_data *nfad);

参数:

  • nfad:将要传递给回调函数的Netlink数据包
  • return:将要发送的排队数据包的物理设备的索引。 如果返回的索引为0,则说明该数据包将发往本机,或者输入接口未知(即OSTROUTING未知)。
  1. nfq_get_payload() 获取有效载荷
int nfq_get_payload(struct nfq_data *nfad, unsigned char **data);

参数:

  • nfad:将要传递给回调函数的Netlink数据包
  • data:指向payload的指针的指针
  • return: -1错误,其余情况>0
  1. nfq_set_verdict()
int nfq_set_verdict(struct nfq_q_handle *qh, uint32_t id, uint32_t verdict, uint32_t data_len, const unsigned char *buf)

参数:

  • qh:nfq_create_queue()创建的句柄。
  • id:唯一标志符
  • verdict: 对数据包操作的办法:(NF_ACCEPT, NF_DROP)
  • data_len:数据长度
  • buf:数据

样例部分

编译

gcc -o nf-queue nf-queue.c -lnetfilter_queue -lmnl
 /*     test.c      */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h>		/* for NF_ACCEPT */
#include <errno.h>
 
 
#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;
	struct nfqnl_msg_packet_hw *hwph;
	u_int32_t mark,ifi; 
	int ret;
	unsigned 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);
	}
 
 
	hwph = nfq_get_packet_hw(tb);
	if (hwph) {
		int i, hlen = ntohs(hwph->hw_addrlen);
 
 
		printf("hw_src_addr=");
		for (i = 0; i < hlen-1; i++)
			printf("%02x:", hwph->hw_addr[i]);
		printf("%02x ", hwph->hw_addr[hlen-1]);
	}
 
 
	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);
	ifi = nfq_get_physindev(tb);
	if (ifi)
		printf("physindev=%u ", ifi);
 
 
	ifi = nfq_get_physoutdev(tb);
	if (ifi)
		printf("physoutdev=%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)
{
	printf("entering callback\n");//进入到回调函数
	u_int32_t id = print_pkt(nfa);
	/**
	 * 函数功能:
		对一个数据包发表裁决。
	 * 函数参数:
		qh:通过调用nfq_create_queue()获得的Netfilter队列句柄。
		id:由netfilter分配给数据包的ID
		verdict:决定返回到netfilter
		data_len: buf缓冲区的字节数
		buf:包含数据包数据的缓冲区
	 * 函数返回值:
		出错返回-1,否则返回值大于等于0。
	*/
	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[4096] __attribute__ ((aligned));
 
 
	printf("opening library handle\n");
	h = nfq_open();//创建 netfilter_queue
	if (!h) {//创建失败
		fprintf(stderr, "error during nfq_open()\n");
		exit(1);
	}
 
 
	printf("unbinding existing nf_queue handler for AF_INET (if any)\n");//解绑已经存在的队列
	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");//绑定上我们创建的队列
	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");//cb是回调函数
	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);
	}
 
 
	fd = nfq_fd(h);
 
 
	for (;;) {
		if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
			printf("pkt received\n");
			nfq_handle_packet(h, buf, rv);
			continue;
		}
		/* if your application is too slow to digest the packets that
		 * are sent from kernel-space, the socket buffer that we use
		 * to enqueue packets may fill up returning ENOBUFS. Depending
		 * on your application, this error may be ignored. Please, see
		 * the doxygen documentation of this library on how to improve
		 * this situation.
		 */
		if (rv < 0 && errno == ENOBUFS) {
			printf("losing packets!\n");
			continue;
		}
		perror("recv failed");
		break;
	}
 
 
	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_close(h);
 
 
	exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值