netfilter_queue 使用示例

2024-02-24 04:18
文章标签 使用 示例 queue netfilter

本文主要是介绍netfilter_queue 使用示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 简介
  • 感谢
  • 环境依赖
  • 示例
  • 测试
    • 编译
    • 配置iptables规则
    • ping本机loopback地址
    • 启动nf-queue程序

简介

之前写过一篇文章《iptables queue 应用示例》,介绍使用libipq实现用户态数据包处理。最近有遇到国产麒麟系统不支持libipq的问题,好在还支持netfilter_queue,整理一个关于NFQUEUE的版本出来。

感谢

本文大部分内容参考_Raymond_的《netfilter_queue 总结》,作者对netfilter做了详细的介绍,原文链接:https://blog.csdn.net/weixin_43745072/article/details/110096900

环境依赖

需要安装libnetfilter_queue,libnetfilter_queue-devel,iptables,iptables-devel

yum install -y libnetfilter_queue libnetfilter_queue-devel iptables iptables-devel

示例

nf-queue.c

 /*     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分配给数据包的IDverdict:决定返回到netfilterdata_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_queueif (!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);
}
  • nfq_open():打开一个nfqueue的句柄。
struct nfq_handle *nfq_open(void);

参数
return:生成的句柄

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

参数
h:nfq_open()创建的句柄
return: 0 成功,非0失败

  • nfq_unbind_pf() 将给定队列连接句柄与处理中的属于某个特定协议家族的数据包解除绑定。
int nfq_unbind_pf(struct nfq_handle *h, uint16_t pf);

参数
h:nfq_open()创建的句柄
pf:将被解绑的协议家族
return:0 成功,非0失败

  • nfq_unbind_pf() 将给定队列连接句柄与处理中的属于某个特定协议家族的数据包进行绑定。
int nfq_bind_pf(struct nfq_handle *h, uint16_t pf);

参数:
h:nfq_open()创建的句柄
pf:将被绑定的协议家族
return:0 成功,非0失败

  • 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的指针

  • 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其他情况。

  • nfq_fd()得到nfqueue句柄相关的文件描述符。
int nfq_fd(struct nfq_handle *h);

参数:
h:nfq_open()获得的句柄
return:与nfqueue句柄相关的文件描述符。

  • nfq_handle_packet() 处理从nfqueue子系统收到的数据包
int nfq_handle_packet(struct nfq_handle *h, char *buf, int len);

参数
h:nfq_open()获得的数据包
buf:传递给回调函数的数据
len:buf中packet数据的大小

  • nfq_destroy_queue() 摧毁nfq_create_queue()创建的队列。摧毁的同时会自动解绑,所以不需要调用nfq_unbind_pf()
int nfq_destroy_queue(struct nfq_q_handle *qh);

参数:
qh:被摧毁的nfq_create_queue()句柄
return:

  • 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));
  • get hardware address() 获得硬件地址
struct nfqnl_msg_packet_hw *nfq_get_packet_hw(struct nfq_data *nfad);

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

  • nfq_get_nfmark() 获得数据包的标志
uint32_t nfq_get_nfmark(struct nfq_data *nfad);

参数:
nfad:将要传递给回调函数的Netlink数据包
return:分配给这个数据包的标志

  • nfq_get_indev() 获取接受数据包的索引信息。
uint32_t nfq_get_indev(struct nfq_data *nfad);

参数:
nfad:将要传递给回调函数的Netlink数据包
return:接收排队数据包的设备的索引。 如果返回的索引为0,则说明该数据包是在本地生成的,或者输入接口未知(即OSTROUTING未知)。

  • nfq_get_outdev() 获取接受数据包的索引信息。
uint32_t nfq_get_outdev(struct nfq_data *nfad);

参数:
nfad:将要传递给回调函数的Netlink数据包
return:排队的数据包将被发送出去的设备的索引。 如果返回的索引为0,则该数据包将发送到本机或尚不知道输出接口(即REROUTING未知)。

  • nfq_get_physindev() 获取接受的数据包的物理层接口信息。
uint32_t nfq_get_physindev(struct nfq_data *nfad);

参数:
nfad:将要传递给回调函数的Netlink数据包
return:接收排队数据包的物理设备的索引。 如果返回的索引为0,则说明该数据包是在本地生成的,或者输入接口未知(即OSTROUTING未知)。

  • nfq_get_outdev() 获取即将发送的数据包的物理层接口信息。
uint32_t nfq_get_outdev(struct nfq_data *nfad);

参数:
nfad:将要传递给回调函数的Netlink数据包
return:将要发送的排队数据包的物理设备的索引。 如果返回的索引为0,则说明该数据包将发往本机,或者输入接口未知(即OSTROUTING未知)。

  • nfq_get_payload() 获取有效载荷
int nfq_get_payload(struct nfq_data *nfad, unsigned char **data);

参数:

nfad:将要传递给回调函数的Netlink数据包
data:指向payload的指针的指针
return: -1错误,其余情况>0

  • 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: 对数据包操作的办法,主要有如下几种:

  1. NF_DROP: 丢弃该报文,释放所有与该报文相关的资源;
  2. NF_ACCEPT: 接受该报文,并继续处理;
  3. NF_STOLEN: 该报文已经被HOOK函数接管,协议栈无须继续处理;
  4. NF_QUEUE: 将该报文传递到用户态去做进一步的处理;
  5. NF_REPEAT: 再次调用本HOOK函数。
  6. NF_STOP : 与NF_ACCEPT类似但强于NF_ACCEPT,一旦挂接链表中某个hook节点返回NF_STOP,该skb包就立即结束检查而接受,不再进入链表中后续的hook节点,而NF_ACCEPT则还需要进入后续hook点检查。

data_len:数据长度
buf:数据

测试

编译

gcc -o nf-queue nf-queue.c -lnetfilter_queue

配置iptables规则

在用户态对数据包进行处理,需要配置iptables规则,将符合条件的数据包转到netfilter提供的queue中。

ip6tables -I INPUT -p icmpv6 --icmpv6-type 128 -m length --length :1280 -j NFQUEUE --queue-num 0

ping本机loopback地址

ping6 -s 1120 fe80::8261:5fff:fe03:fd3d%lo

此时没有数据包回复
在这里插入图片描述

启动nf-queue程序

./nf-queue 

此时ping包能够收到回复数据
在这里插入图片描述

这篇关于netfilter_queue 使用示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/740923

相关文章

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.