【问题标题】:TUNTAP interface in C (Linux) : Can't capture UDP packets sent on the TUNTAP with sendto()C (Linux) 中的 TUNTAP 接口:无法使用 sendto() 捕获在 TUNTAP 上发送的 UDP 数据包
【发布时间】:2012-03-11 13:30:49
【问题描述】:

我正在尝试用 C 语言编写一个隧道程序,该程序将从 TUNTAP 接口获取 UDP 数据包并将它们发送到串行接口。

我所做的是从克隆设备/dev/net/tun分配接口,打开它并给它一个ip地址:

int tun_setup(char *dev, int flags) {

  struct sockaddr_in  my_addr;
  struct ifreq ifr;
  int fd, err;
  string clonedev = "/dev/net/tun";

  // Open clone device file descriptor
  if( (fd = open(clonedev.c_str() , O_RDWR)) < 0 ) {
    perror("Opening /dev/net/tun");
    return fd;
  }

  // Initialise interface parameters structure
  memset(&ifr, 0, sizeof(ifr));

  // Set up flags
  ifr.ifr_flags = flags;

  // Set up interface name
  if (*dev) {
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);
  }

  // Put interface in TUN mode
  if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) {
    perror("ioctl(TUNSETIFF)");
    close(fd);
    return err;
  }

  strcpy(dev, ifr.ifr_name);

  // Create a socket
  if ( (s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  perror("socket");
      exit(1);
  }

  // Get interface flags
  if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
    perror("cannot get interface flags");
    exit(1);
  }

  // Turn on interface
  ifr.ifr_flags |= IFF_UP;
  if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
    fprintf(stderr, "ifup: failed ");
    perror(ifr.ifr_name);
    exit(1);
  }

  // Set interface address
  bzero((char *) &my_addr, sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(inet_network("192.168.2.1"));
  memcpy(&ifr.ifr_addr, &my_addr, sizeof(struct sockaddr));

  if (ioctl(s, SIOCSIFADDR, &ifr) < 0) {
    fprintf(stderr, "Cannot set IP address. ");
    perror(ifr.ifr_name);
    exit(1);
  }

  // Return interface file descriptor
  return fd;
}

然后我创建一个线程,该线程将对创建的接口的文件描述符进行 poll() 并在事件发生时执行 read() + 其他一些操作。

void* tun_readThreadProc (void* param) {
  struct pollfd fds[1];
  int nread;
  unsigned char buffer[BUFFERSIZE];
  fds[0].fd = tun_fd;
  fds[0].events = POLLIN;

  printf("%s : Entered. tun_fd = %d \n",__FUNCTION__,tun_fd);

  for(;;)
  {
    printf("%s : Entered loop\n",__FUNCTION__);
    if((poll(fds, 1, -1)) == -1)
    {
      perror("poll");
      exit(1);
    }

    printf("%s : Poll sensed something\n",__FUNCTION__);

    if((nread = read(tun_fd, buffer, BUFFERSIZE)) < 0)
    {
      perror("read");
      close(tun_fd);
      exit(1);
    }

    printf("%s : Read something : %d bytes\n",__FUNCTION__,nread);
  }
  return 0;
}

在程序的另一部分,我将一个 UDP 套接字绑定到这个 TUNTAP 接口的 IP 地址。

void socketInit( void )
{
  int                 on = 1;
  struct sockaddr_in  my_addr;
  unsigned short DefaultPort = 47808;

  // Create a socket
  if ( (s1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }

  // Bind to it
  bzero((char *) &my_addr, sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(inet_network("192.168.2.1"));
  my_addr.sin_port = htons(DefaultPort);

  if ( (bind(s, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) ) {
    perror("bind");
  }
  // Allow it to broadcast
  if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on)) < 0) {
    perror("setsockopt");
  }
}

在另一个函数中,我使用 sendto() 通过这个套接字发送数据包。我应该使用 poll() + read() 线程捕获这些数据包,然后在串行端口上发送它们,但 poll() 从不捕获 TUNTAP 接口上的事件。

我可以通过使用 ping -I tun0 [some destination] (tun0 = TUNTAP 接口的名称) ping 这个接口

但如果我使用 ping -I 192.168.2.1 [some destination] (192.168.2.1 = TUNTAP 接口地址),它会通过默认接口(eth0,物理网卡)。

我能够使用 Wireshark 进行验证。

这很可能是ip路由配置问题...

如果有人可以帮助我,我会非常高兴。

【问题讨论】:

  • 此处创建套接字代码:pastebin.com/raw.php?i=JZLKkuxS
  • UDP 数据包“在这个接口上”是什么意思?您是指通过该接口路由的目的地吗?
  • 我在这里从 pastebin 中添加了你的代码,它更容易阅读(如果 FBI 关闭了 pastebin 以帮助人们太多,StackOverfow 仍然能够生存:P!)
  • @Eregrith Thx,虽然我还没有看到任何东西
  • @Eregrith 刚刚批准了您的更改,谢谢您的帮助:)

标签: c networking udp tunneling


【解决方案1】:

您是否清除了 pollfd 上的事件。 fds[0].revents=0?

我们需要清除接收到的事件。

源:http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/

【讨论】:

    【解决方案2】:

    你好像只给tun设备分配了一个IP地址(192.168.2.1),没有指定子网掩码,也没有设置路由。如果没有为 192.168.2.0/24 网络定义特定路由,IP 层将认为此数据包旨在通过默认路由发送。

    您可以在设置 tun 设备 IP 地址时指定子网掩码 (/24),也可以手动设置 192.168.2.0/24 网络的路由。

    其中一种方法是使用 system() 函数调用“ip”。使用子网掩码指定地址(已包含标头):

    // assuming the tun device name is "tun0"
    system("ip addr add 192.168.2.1/24 dev tun0"); 
    

    另外,如果您已经为设备分配了 IP 地址,您可以使用ip route comamnd 来设置路由:

    ip route add 192.168.2.0/24 dev tun0
    

    【讨论】:

      【解决方案3】:

      tun tap接口的IP地址是怎么设置的?使用"ip addr add" 命令?您是否检查/配置了 tun 抽头,即"ip link set &lt;&lt;tun tap interface name&gt;&gt; up"?最后,您确定您的 UDP 套接字 sendto 代码在满足上述两个条件后运行,即它具有正确的 ip 地址并且 tun tap 接口已启动。

      更新

      我认为您的概念有误,或者我不太了解您。 AFAIK 你不需要做这么多事情。 tun tap 设备的概念是 tun tap 接口接收到的任何数据包都将其发送到用户空间程序,而用户空间程序向 tun tap 设备写入的任何数据包都将其发送到网络。话虽如此并读到您的要求是将UDP数据包隧道传输到串行接口,您应该做的是以下

      1) 将默认路由设置为 tun tap IP 地址。这样,所有的数据包都将被用户空间程序获取。 route add default gw 192.168.2.1 tun0

      所以现在在用户空间程序中,您将获得带有 IP 和 UDP 标头的整个数据包。现在,整个数据包将成为您要通过串行接口传输的消息。因此,当我们使用 UDP 或 TCP(无论您喜欢哪个)通过串行接口发送它时,我们会自动再次用一个额外的 UDP 和 IP 标头封装整个数据包。

      2) 您还需要使用串行接口地址添加主机规则。这样,上述封装的串行接口数据包被发送到串行接口,由于默认规则,它们不会再次返回到 TUN TAP 设备。 route add -host &lt;&lt;serial ip address&gt;&gt; dev &lt;&lt;serial device&gt;&gt;

      route -n并检查路由是否正确。确保您没有其他不需要的路线。如果是,请删除它们。

      注意: 您还可以仅从数据包中提取有效负载,并使用 RAW 套接字创建一个自定义 UDP 标头,其中目标为串行接口的 IP 地址,并使用 TUN TAP 设备进行写入。该数据包将被视为属于串行接口的内核路由实例,并且由于主机规则将被转发到那里。如果您要处理这种情况,您需要创建一个自定义 IP 和 UDP 标头以及 CRC 计算。

      【讨论】:

      • 我在打开udp套接字时使用ioctl设置接口地址:ioctl(portSocket, SIOCSIFADDR, &ifr)。我也用 ioctl 来打开它: ifr.IRFFLAGS |= IFF_UP; (ioctl(portSocket, SIOCSIFFLAGS, &ifr); 所以我很确定这些步骤是在发送任何数据包之前完成的。无论如何,我现在正在重写代码,并在它准备好时用这个新代码更新我的问题。我希望它将帮助任何人(包括我)找出问题所在。
      • 我无法使用串行线路 IP (SLIP)。我必须“手动”从 tun tap 捕获数据包,然后以与接收设备相关的特定方式封装它们。我没有在这里谈论它,因为它与我的问题没有直接关系。
      • 但是route add default gw 192.168.2.1 tun0 是通过 tun0 路由所有流量,这不是我想要的...
      • 1) 请粘贴 route -n 的输出 2) 您是否尝试过使用 sendip 将 UDP 发送到 TUN TAP?在这种情况下,民意调查工作正常吗?
      • 当我使用route add default gw 192.168.2.1 tun0 时,poll / read 完成了它的工作,但另一方面,所有流量都通过 tun0,这是无用的。所以作为一个临时解决方案,我只是使用route add -net 192.168.2.0 netmask 255.255.255.0 dev tun0 ...这让我可以在我的项目环境中进行一些测试。虽然我想要一个永久的解决方案,让我确保任何源 ip 为 192.168.2.1(tuntap 地址)的数据包确实正在通过 tuntap
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-02
      • 2016-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多