gpt4 book ai didi

c - 在不使用环回网络的情况下将数据包转发到同一主机中的服务

转载 作者:太空狗 更新时间:2023-10-29 11:15:44 31 4
gpt4 key购买 nike

我有这个 libnetfilter_queue 应用程序,它根据一些 iptables 规则从内核接收数据包。在直接解决我的问题之前,我将提供示例可行代码和其他工具来设置测试环境,以便我们可以更准确、更可靠地定义问题和可能的解决方案。

以下代码描述了应用程序的核心功能:

#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>
#define PREROUTING 0
#define POSTROUTING 4
#define OUTPUT 3


/* 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;

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);

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)
{
uint32_t ip_src, ip_dst;
struct in_addr s_ip;
struct in_addr d_ip;
uint16_t src_port;
uint16_t dst_port;
int verdict;
int id;
int ret;
unsigned char *buffer;
struct nfqnl_msg_packet_hdr *ph = nfq_get_msg_packet_hdr (nfa);
if (ph)
{
id = ntohl (ph->packet_id);
printf ("received packet with id %d", id);
}
ret = nfq_get_payload (nfa, &buffer);
ip_src = *((uint32_t *) (buffer + 12));
ip_dst = *((uint32_t *) (buffer + 16));
src_port = *((uint16_t *) (buffer + 20));
dst_port = *((uint16_t *) (buffer + 22));
s_ip.s_addr = (uint32_t) ip_src;
d_ip.s_addr = (uint32_t) ip_dst;
*(buffer + 26) = 0x00;
*(buffer + 27) = 0x00;
printf ( "source IP %s", inet_ntoa (s_ip));
printf ( "destination IP %s", inet_ntoa (d_ip));
printf ( "source port %d", src_port);
printf ( "destination port %d", dst_port);
if (ret)
{
switch (ph->hook)
{
case PREROUTING:
printf ( "inbound packet");
//my_mangling_fun();
break;
case OUTPUT:
printf ( "outbound packet");
//my_mangling_fun();
break;
}
}
verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer);
if (verdict)
printf ( "verdict ok");
return verdict;
}

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 ();
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");
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);
}

注意回调函数中对 my_mangling_fun() 的两次调用被注释掉了。这是我处理传入和传出数据包的地方。我认为这段代码足以描述我的情况。如果需要进一步说明,请询问,我将发布更多详细信息。

假设伴随的 iptables 规则如下:

$iptables -t mangle -A PREROUTING -p udp --dport 5000 -j NFQUEUE
$iptables -t mangle -A OUTPUT -p udp --sport 5000 -j NFQUEUE

让我们编译并启动 udp 吧。

$gcc -g3 nfq_test.c -lnfnetlink -lnetfilter_queue
$./a.out (should be as root)

现在我们可以通过 netcat 在客户端和服务器模式下将垃圾 udp 有效载荷提供给这个东西

$nc -ul 5000
$nc -uvv <IP> 5000

这将在标准输出中打印来 self 的 netfilter_queue 应用程序的数据包。现在开发环境已经设置好了,我们可以进行下一步了。

我们正在努力实现以下目标:

我们的服务器正在监听 5000 端口。现在所有发往 udp 端口​​ 5000 的传入数据包都将由内核排队。这个队列的句柄将提供给我们前面列出的用户应用程序。这个队列机制是这样工作的:当一个数据包可用时,回调函数(我们代码中的 cb() )被调用。处理后回调函数调用nfq_set_verdict()。 verdict 返回后,下一个数据包将从队列中弹出。请注意,如果其前一个数据包尚未作出裁决,则该数据包不会从队列中弹出。该判断值是 NF_ACCEPT 用于接受数据包,NF_DROP 用于丢弃数据包。

现在,如果我想在不触及客户端和服务器端代码的情况下连接传入和传出数据包的 udp 负载怎么办?

如果我想在这个应用程序中连接来 self 们应用程序的 udp 有效负载,那么我们需要手头有多个数据包。但是我们已经看到,在对前一个数据包作出裁决之前,数据包不会从队列中弹出。

那么如何做到这一点呢?

一个可能的解决方案是向每个数据包发出 NF_DROP 并将这些数据包保存在中间数据结构中。假设我们已经做到了。但是如何才能将这个数据包传送到监听 5000 端口的服务呢?

我们不能使用网络栈来传递数据包,因为如果这样做,数据包将再次进入 NFQUEUE。

另一个问题是,服务器完全不知道这个应用程序。这意味着它不应该在数据包中看到任何差异。它应该将数据包视为来自原始客户端。

我听说一个应用程序可以通过写一些文件而不使用网络层(ip,端口)将数据发送到同一主机中的服务器。我不知道这个声明的有效性。但是,如果有人对此有所了解,那就太好了。

我可能会因为过于冗长而被否决。但我认为这会很有趣。我们可以一起找到解决方案:)

最佳答案

我提出以下解决方案:

  • 在应用程序中存储数据包并返回判断 NF_DROP
  • 使用 RAW 套接字将数据包重新注入(inject)网络堆栈
  • 用 DSCP 标记串联的 UDP 数据包(参见 IP 数据包格式)
  • 在 iptables 中,添加规则以匹配此 DSCP (--dscp) 并直接接受数据包,而不通过您的 netfilter 应用程序

如果您的提供商已经使用 DSCP 标记了一些数据包,您可以添加一些 iptables 规则来清除它们,例如:

iptables -t mangle -A INPUT -j DSCP --set-dscp 0

我希望这能解决您的用例。

关于c - 在不使用环回网络的情况下将数据包转发到同一主机中的服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10314155/

31 4 0