- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在使用 netfilter 队列库实现用户空间防火墙。我使用 nfq_fd()
获得了队列的文件描述符,因此我可以调用 recv(fd, recv_buf, BUFFERSIZE, MSG_DONTWAIT)
来获取数据包数据而不会阻塞。但有时 recv()
在我每次调用它时都会开始返回 52 字节的数据包。如果我检查 iptables -nvL INPUT
的输出,数据包的数量不会增加,所以它们实际上并不是从网络发送的。 Edit3:当我向 nfq_handle_packet() 传递其中一个奇怪的数据包时,它返回 -1,并且它永远不会触发回调函数,因此我无法获取数据包 ID 或返回结论。
为什么 recv() 给我这些奇怪的数据包?
编辑1:
数据包并不完全相同,但它们具有相似的结构。也有一些重复。这是其中一些的 hexdump:
0000 34 00 00 00 02 00 00 00 00 00 00 00 BE 4E 00 00 4............N..
0010 FE FF FF FF 20 00 00 00 01 03 01 00 00 00 00 00 .... ...........
0020 00 00 00 00 00 00 00 00 0C 00 02 00 00 00 00 01 ................
0030 01 00 00 00 ....
0000 34 00 00 00 02 00 00 00 00 00 00 00 5B 69 00 00 4...........[i..
0010 FE FF FF FF 20 00 00 00 01 03 01 00 00 00 00 00 .... ...........
0020 00 00 00 00 00 00 00 00 0C 00 02 00 00 00 00 01 ................
0030 00 00 01 95 ....
0000 34 00 00 00 02 00 00 00 00 00 00 00 5B 69 00 00 4...........[i..
0010 FE FF FF FF 20 00 00 00 01 03 01 00 00 00 00 00 .... ...........
0020 00 00 00 00 00 00 00 00 0C 00 02 00 00 00 00 01 ................
0030 00 00 01 95 ....
编辑2:
代码非常简陋,只是根据我找到的一些 netfilter_queue 教程进行了调整。
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#define BUFFERSIZE 500
int main()
{
struct nfq_handle *h;
struct nfq_q_handle *qh;
struct my_nfq_data msg;
int fd;
unsigned char recv_buf[BUFFERSIZE];
int action;
if ((stat("/proc/net/netfilter/nfnetlink_queue", &fbuf) < 0) && (errno == ENOENT))
{
fprintf(stderr, "Please make sure nfnetlink_queue is installed, or that you have\ncompiled a kernel with the Netfilter QUEUE target built in.\n");
exit(EXIT_FAILURE);
}
openlog("packetbl", LOG_PID, "local6");
if ((h = nfq_open()) == 0)
{
syslog(LOG_ERR, "Couldn't open netlink connection: %s", strerror(errno));
exit(EXIT_FAILURE);
}
nfq_unbind_pf(h, AF_INET);
if ((nfq_bind_pf(h, AF_INET) < 0))
{
syslog(LOG_ERR, "Couldn't bind to IPv4: %s", strerror(errno));
}
nfq_unbind_pf(h, AF_INET6);
if ((nfq_bind_pf(h, AF_INET6) < 0))
{
syslog(LOG_ERR, "Couldn't bind to IPv6: %s", strerror(errno));
}
if ((qh = nfq_create_queue(h, 0, &callback, &msg)) == NULL)
{
syslog(LOG_ERR, "Couldn't create nfq: %s", strerror(errno));
exit(EXIT_FAILURE);
}
if ((nfq_set_mode(qh, NFQNL_COPY_PACKET, BUFFERSIZE)) == -1)
{
syslog(LOG_ERR, "nfq_set_mode error: %s", strerror(errno));
if (errno == 111)
{
syslog(LOG_ERR, "try loading the nfnetlink_queue module");
}
exit(EXIT_FAILURE);
}
fd = nfq_fd(h);
while(1)
{
/* Up here I print some statistics on packets allowed and blocked.
It prints on a schedule, so the recv() call has to be non-blocking
or else the statistics would only print out when there's a packet. */
recv_return_code = recv(fd, recv_buf, BUFFERSIZE, MSG_DONTWAIT); //nonblocking
if (recv_return_code < 0)
{
if (errno == EAGAIN ||
errno == EWOULDBLOCK)
{
nanosleep(×,NULL);
}
else
{
syslog(LOG_ERR, "recv failed: %s", strerror(errno));
}
continue;
}
printf("received %d bytes\n", recv_return_code);
/* when nfq_handle_packet() succeeds, it triggers the callback
which puts the packet data into a global variable "msg" */
if (nfq_handle_packet(h, recv_buf, recv_return_code) != 0)
{
syslog(LOG_ERR, "couldn't handle packet");
}
action = packet_check_ip(msg);
pbl_set_verdict(qh, ntohl(msg.header.packet_id), action);
}
}
编辑 4:
我正在使用 scapy 作为流量生成器。如果我一次只发送一个数据包,那么我会收到 0 或 1 个虚假数据包,然后它就会停止。这是 strace 的输出:
recvfrom(3, "x\0\0\0\0\3\0\0\0\0\0\0\0\0\0\0\n\0\0\0\v\0\1\0\0\0\0\6\206\335\1\0\10\0\5\0\0\0\0\2\20\0\t\0\0\6\261\201\0\f)7Z\22\0\0@\0\n\0`\0\0\0\0\24\6@&\6\364\0\10\0\0\0\0\0\0\0\0\0p\5&\6\364\0\10\0\0\0\0\0\0\0\0\0p\4\0\24\0\31\0\0\0\0\0\0\0\0P\2 \0k\236\0\0", 9216, MSG_DONTWAIT, NULL, NULL) = 120
sendto(4, "<182>Jan 13 10:51:20 packetbl[8785]: [Found in cache (accept)] [2606:f400:800::7005,20,25]", 90, MSG_NOSIGNAL, NULL, 0) = 90
sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{" \0\0\0\1\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\f\0\2\0\0\0\0\1\0\0\0\6", 32}], msg_controllen=0, msg_flags=0}, 0) = 32
recvfrom(3, "x\0\0\0\0\3\0\0\0\0\0\0\0\0\0\0\n\0\0\0\v\0\1\0\0\0\0\7\206\335\1\0\10\0\5\0\0\0\0\2\20\0\t\0\0\6\261\201\0\f)7Z\22\0\0@\0\n\0`\0\0\0\0\24\6@&\6\364\0\10\0\0\0\0\0\0\0\0\0p\1&\6\364\0\10\0\0\0\0\0\0\0\0\0p\4\0\24\0\31\0\0\0\0\0\0\0\0P\2 \0k\242\0\0", 9216, MSG_DONTWAIT, NULL, NULL) = 120
futex(0x60c984, FUTEX_CMP_REQUEUE_PRIVATE, 1, 2147483647, 0x607fc0, 8) = 2
futex(0x607fc0, FUTEX_WAKE_PRIVATE, 1) = 1
sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{" \0\0\0\1\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\f\0\2\0\0\0\0\1\7\0\0\0", 32}], msg_controllen=0, msg_flags=0}, 0) = 32
recvfrom(3, "4\0\0\0\2\0\0\0\0\0\0\0Q\"\0\0\376\377\377\377 \0\0\0\1\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\f\0\2\0\0\0\0\1\7\0\0\0", 9216, MSG_DONTWAIT, NULL, NULL) = 52
sendto(4, "<179>Jan 13 10:51:22 packetbl[8785]: couldn't handle packet", 59, MSG_NOSIGNAL, NULL, 0) = 59
sendmsg(3, {msg_name(12)={sa_family=AF_NETLINK, pid=0, groups=00000000}, msg_iov(1)=[{" \0\0\0\1\3\1\0\0\0\0\0\0\0\0\0\0\0\0\0\f\0\2\0\0\0\0\1\0\0\0\7", 32}], msg_controllen=0, msg_flags=0}, 0) = 32
我可以尽可能快地发送单个数据包,而且它永远不会进入死亡螺旋。但是如果我让 scapy 一次发送 4 个数据包,它有时会为每个真实数据包触发一个(或零个)虚假数据包,但其他时候我会收到无限的虚假数据包。如果我发送大量数据包,它总是会变得无限。
我以前见过一些这种行为,但 Nominal Animal 的回答勾起了我的内存。如上所示,关于我的代码的一件奇怪的事情是,即使 nfq_handle_packet()
失败,我仍然执行 packet_check_ip()
和 pbl_set_verdict()
。我认为在这种情况下放置 continue;
是有意义的,否则我将处理 msg
变量中的陈旧数据。 (如果我错了,请纠正我,但这应该与将数据包处理和判决移至回调中具有相同的效果。)但这始终会在甚至 1 个真实数据包之后引发无限的虚假数据包。我还暂时将判断移到了回调中,它没有改变任何东西。
所以不知何故,对旧数据调用 set_verdict 有时会阻止无穷大?
哦,这里是 pbl_set_verdict()
的代码,如果有人担心它可能会做任何聪明的事情的话:)
static void pbl_set_verdict(struct nfq_q_handle *qh,
uint32_t id,
unsigned int verdict)
{
nfq_set_verdict(qh, id, verdict, 0, NULL);
}
编辑 5:
我已经编译并运行了与 libnetfilter_queue 一起分发的 nfqnl_test.c 示例,它运行得很好。所以这可能不是库本身的问题。
编辑 6:
现在我有所进展 :) 事实证明,在容量过剩的情况下,ntohl()
被调用了两次!因为即使 nfq_handle_packet
失败,我也在旧数据上调用 pbl_set_verdict()
,它正确地运行了数据,产生了正确的效果。这就是为什么当我将 pbl_set_verdict()
调用移入回调函数时队列已满的原因 - 它从未有机会解决由容量过剩情况引起的问题。陈旧的数据只包括一些已处理的数据包,所以无论如何它们最终都会填满队列。
即使我的程序现在可以运行,我仍然对这些数据包是什么以及为什么它们似乎没有记录在案感到困惑。
最佳答案
将您的代码与 libnetfilter_queue 源代码中的 example 进行比较。您的代码设置判决(假设这是您代码中的 pbl_set_verdict()
所做的)在处理数据包之后。该示例在回调函数中设置判断。
我对 netfilter 的内部结构没有足够的信心可以肯定地说这是你问题的根本原因,但我确实相信它是。
至于使用非阻塞读,没有那个必要。取而代之的是,让间隔计时器定期触发一个信号(比如 HUP
或像 SIGRTMIN+1
这样的实时信号),并为该信号安装一个空信号处理函数.当信号被传送时(到空体处理程序;IGN
或 DFL
将不起作用),这会导致任何阻塞的 I/O 调用被中断,假设您进程只有一个线程。如果间隔很长,使用 HUP
很有用,因为用户可以从外部发送 HUP 以立即打印统计信息。这样就不会浪费额外的 CPU 时间。
如果您的应用程序使用多线程,您需要更多的机器。处理程序需要检查源是否是定时器中断 (siginfo->si_code==SI_TIMER
),如果是,则使用 pthread_sigqueue()
将中断(相同信号)转发到目标线程,除非当前线程是目标线程。通过 netlink 读取消息的线程需要将它们的线程 ID 保存到中断处理程序可以访问它们的某个地方。 (此外,您的其他代码必须知道 errno==EINTR
可能会发生,这不是错误,除非它们专门阻止信号。)
换句话说,我希望您的代码更像
/* In case of an error, break out of the following loop.
* You can either exit, or close and re-establish the netlink
* and queue.
*/
while(1)
{
ssize_t bytes;
/* Read a new netlink message.
Note: Technically, BUFFERSIZE should be about 65536,
since each message has a uint16_t message length field.
*/
bytes = recv(fd, recv_buf, BUFFERSIZE, MSG_DONTWAIT);
/* C library, or kernel recv() bug?
*/
if (bytes < (ssize_t)-1 || bytes > (ssize_t)BUFFERSIZE) {
errno = EIO;
break; /* out of the while (1) loop */
}
/* Netlink closed? Should not occur. */
if (bytes == (ssize_t)0) {
errno = 0;
break; /* No error, just netlink closed. Drop out. */
}
/* No message? */
if (bytes == (ssize_t)-1) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
/* Print overall statistics.
*/
continue;
} else
break; /* Other errors drop out of the loop. */
}
if (nfq_handle_packet(h, recv_buf, bytes)) {
/* Packet was dropped on the floor.
* This is a serious problem, so we treat this as EIO.
*/
errno = EIO;
break;
}
}
基本上是回调
static int callback(struct nfq_q_handle *qh,
struct nfgenmsg *nfmsg,
struct nfq_data *nfa,
void *data)
{
return nfq_set_verdict(qh, id, packet_check_ip(nfmsg), 0, NULL);
}
至于多线程上面,你可以简单地让多个线程同时运行上面的循环(很明显,使用不同的recv_buf
缓冲区)。然后,接收数据包的线程也处理它,包括回调。线程安全应该没有问题,除非您自己的代码是非线程安全的。如果线程应该退出,您还可以在 if 子句内的 “打印总体统计信息” 注释之前添加一个检查(对于某些全局 volatile 标志);然后你可以简单地设置标志,并发送信号来更新统计数据,让所有工作线程退出,而不会将任何数据包丢弃在“地板上”。
有什么问题吗?
关于c - netfilter_queue 虚假数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20954110/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!