转发数据包在同一个主机服务,而不使用网络环回 [英] forwarding packets to service in same host without using loopback network

查看:603
本文介绍了转发数据包在同一个主机服务,而不使用网络环回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有接收基于iptables的一些规则从内核包这个libnetfilter_queue应用。前直去我的问题,我给一个样本可行code和其他工具,以建立一个测试环境,让我们的定义问题和可能的解决方案可以更准确和鲁棒性。

I have this libnetfilter_queue application which receives packets from kernel based on some iptables rule. Before going straight to my problem, i'm giving a sample workable code and other tools to set up a test environment so that We problem definition and possible solutions can be more accurate and robust.

以下code描述应用程序的核心功能:

The following code describes the core functionality of the application:

#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()通知被注释掉。这是我裂伤传入和传出的数据包。我觉得这$​​ C $ C将足以形容我的情况。如果进一步的澄清需要请咨询,我将发布更多细节。

Notice in the callback function two calls to my_mangling_fun() is commented out. This is where i mangle the incoming and outgoing packet. I think this code would be sufficient to describe my case. If further clarification is need please ask, i will post further details.

可以说,伴随的iptables规则如下:

Lets say accompanying iptables rules are following :

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

让编译和火灾UDP的东西。

lets compile and fire udp the thing.

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

现在我们可以通过netcat的客户端和服务器模式养活垃圾UDP有效载荷这个事情

now we can feed garbage udp payload to this thing by netcat both client and server mode

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

这会从我的标准输出netfilter_queue打印应用程序包。现在,开发环境设置,我们可以移动到下一个事情。

This will print the packet from my netfilter_queue app in stdout. Now that the development environment is set up, we can move to the next thing.

我们正在努力实现的是以下几点:

What we are trying to achieve is following :

我们的服务器监听端口5000。现在注定要UDP端口5000的所有传入的数据包将被内核排队。和手柄到此队列将给予我们前面列出的用户应用程序。此队列机制的工作原理是这样的:当一个数据包可用,回调函数(CB()在我们的code)被调用。处理之后,回调函数调用nfq_set_verdict()。一个宣判后返回,下一个包会弹出从队列中。请注意,如果preceding包尚未下发判决的包将不会从队列中弹出。此判决值NF_ACCEPT接受数据包,NF_DROP为丢弃数据包。

Our server is listening on 5000 port. Now all incoming packet destined to udp port 5000 will be queued by kernel. And the handle to this queue will be given to user application we listed earlier. This queue mechanism works like this: When a packet is available, the callback function(cb() in our code) is called. after processing, the callback function calls nfq_set_verdict(). after a verdict is returned, next packet will pop from the queue. notice that a packet will not pop from queue if its preceding packet has not been issued a verdict. This verdict values are NF_ACCEPT for accepting packet, NF_DROP for dropping the packet.

现在如果我要连接的输入和输出数据包的UDP有效载荷,无需客户端和服务器端的code?

Now what if i want to concatenate the udp payloads of the incoming and outgoing packet without touching client and server side code?

如果我想从我们的应用程序连接起来的UDP有效载荷这个非常的应用程序,那么我们就需要手边多个包。但是,我们已经看到,之前的判决被颁发给了preceding一个数据包不从队列中弹出。

If i want to concatenate udp payloads from our app this very app, then we need to have multiple packets at hand. But we have seen that a packet does not pops from queue before a verdict is issued to its preceding one.

那么,如何才能做到这一点?

So how can this be done?

一个可能的解决方案是发出一个NF_DROP对每个数据包,并在中间数据结构保存这些数据包。比方说,我们都做到了。但是,如何可以在这个数据包可以被传递到服务侦听端口5000?

One possible solution is issue a NF_DROP to every packet and save those packets in an intermediate data structure. Let's say we have done it. But how can this packet can be delivered to the service listening on 5000 port?

我们不能使用的网络协议栈用于传递数据包,因为如果我们这样做,那么数据包将在NFQUEUE再次结束。

We can't use network stack for delivering the packet, because if we do, then packets will end up in NFQUEUE again.

另一个问题是,该服务器是完全无关的这个应用程序。这意味着它不应该看到的数据包的任何差异。应该看到的数据包,如果它从原来的客户端来了。

Another problem is, the server is totally agnostic about this app. That means it should not see any difference in the packets. It should see packets as if it came from the original client.

我听说一个应用程序可以在同一台主机将数据发送到服务器,而无需通过编写一些文件使用网络层(IP,端口)。我不知道这种说法的正确性。但是,如果有人知道这事,这将是美好的。

I have heard that a application can send data to a server in the same host without using network layer(ip,port) by writing some files. I do not know the validity of this statement. But if anyone knows anything about it , it will be wonderful.

我可以踏踏实实投太多冗长。但我认为这可能是有趣的会话。我们可以一起找到解决办法:)

I may get down voted for too much verbosity. But I think this can be fun session. we can find the solution together :)

推荐答案

我提出以下解决方案:


  • 存储包中的应用程序并返回判决NF_DROP

  • 重新注入使用原始套接字包进入网络堆栈

  • 标签级联的UDP数据包用DSCP(见IP数据包格式)

  • 在iptables的,添加一个规则来匹配这个DSCP(--dscp),并直接接受数据包,没有它通过你的网络过滤应用程序

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

If your provider already tags some packets with DSCP, you can add some iptables rules to clear them, like:

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

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

I hope this solves your use-case.

这篇关于转发数据包在同一个主机服务,而不使用网络环回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆