转发数据包在同一个主机服务,而无需使用回网络(forwarding packets to servi

2019-07-29 04:05发布

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

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

#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()被注释掉了。 这是其中i裂伤传入和传出数据包。 我觉得这个代码就足以说明我的情况。 如果进一步的澄清需要请你,我将发布更多细节。

比方说,伴随的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

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

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

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

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

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

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

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

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

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

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

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

Answer 1:

我提出以下解决方案:

  • 店包中的应用及回报判决NF_DROP
  • 使用RAW套接的重新注入包进入所述网络栈
  • 标签级联UDP数据包与DSCP(见IP数据包格式)
  • 在iptables中,添加一个规则来匹配该DSCP(--dscp),并直接接受数据包,没有它通过你的网络过滤器的应用程序

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

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

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



Answer 2:

首先,非常感谢你Aftnix! 你的榜样开始踢我的自动分组检查网络唤醒项目。 我想我的家庭服务器时,它的闲置睡觉,但只要一些请求到来时醒来。这样做是为了检查一个DDWRT的路由器上的请求,决定它是一个合法的请求,并发送WOL包。 SMTP的想法是要排队的多个数据包,保留另一端快乐地与transparantly真实服务器的一些虚假的回应和踢。

我修改你的例子一点点地排队1包,并与下一个数据包发送。 这仅仅是一个验证的概念,但它工作正常。

// struct and variable needed to store 1 packet
struct packetbuffer {
  struct nfq_q_handle *qh;
  u_int32_t id;
  u_int32_t verdict;
  u_int32_t data_len;
  unsigned char *buf;
};

struct packetbuffer pbuf;
int counter = 0;

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

  // My modification starts here
  if ((counter % 2) == 0)
  {
    pbuf.qh = qh;
    pbuf.id = id;
    pbuf.data_len = ret;
    pbuf.buf = malloc(ret);
    memcpy(pbuf.buf, buffer, ret);
    printf(" queue package %d \n", id);
  }
  else
  {
      printf(" output 1st package %d, len=%d\n", pbuf.id, pbuf.data_len);
      verdict = nfq_set_verdict (pbuf.qh, pbuf.id, NF_ACCEPT, pbuf.data_len, pbuf.buf);
      free(pbuf.buf);

      printf(" output 2nd package %d, len=%d\n", id, ret);
      verdict = nfq_set_verdict (qh, id, NF_ACCEPT, ret, buffer);
  }
  counter++;
  return 0;
}

这还不是全部的代码,但它应该是相当明显发生了什么变化。

我知道我有点迟到了,但我还是决定发布这一解决方案以供将来参考,希望一些批评。

编辑:嗯,也许我最好写这样rinetd一个用户空间的过程。



文章来源: forwarding packets to service in same host without using loopback network