使用Tun接口进行I/O

9
意图是让一个程序拦截一系列IP数据包并读取其原始内容,然后在进行调整后重新插入到网络中。
我的方法是基于设置一个Tuntap接口(具体来说是Tun),然后使用iptables等工具将所需的数据包重定向到该隧道接口。
为了测试目的,我编写了这个短小的shell脚本来设置Tun接口并添加所需的规则。现在,我打算在任何从我的本地机器发送到目的地为123.123.123.123的数据包上进行测试。以下是启动脚本:
# Set up the tunnel
ip tuntap add dev maintun mode tun
ifconfig maintun inet 10.10.10.1 netmask 255.255.255.0 up

# Mark packets for forwarding to tun
iptables -t mangle -A PREROUTING -d 123.123.123.123 -j MARK --set-mark 2

# Apply ClientRouter table to mark 2 as it's defined in /etc/iproute2/rt_tables
# 201 ClientRouter
ip rule add fwmark 2 table ClientRouter

# Apply gw if to ClienRouter
ip route add default via 10.10.10.1 dev maintun table ClientRouter

我开始编写一个perl脚本来从Tun设备读取数据,但是我同时卡在了多个点上:

  1. 我认为这样做的方法是让脚本自己通过调用ioctl()/dev/net/tun文件句柄来创建接口,但我不确定ioctl()需要的其他参数。这导致我有下面两个问题:
  2. 我看到第二个参数引用了TUNSETIFF。我只知道它必须是数字。它要求什么?
  3. 据我所了解,第三个参数应该是某种标志,但我没有找到相关信息。可能,其中一个标志是选择它是否应该是Tun或Tap隧道。有关此事的任何信息吗?

由于我被ioctl()标志困住了,我想退一步问:如何以编程方式从Tun设备读取数据,最好是预先配置好的设备?

此外,如果有人看到启动脚本有任何问题,请随意指出。

尽管理想情况下解决方案应该是使用perl,但并非必须如此,只是这是我最容易阅读的语言。Java也是不错的选择。不幸的是,我的C语言水平远远不够胜任此项工作。
编辑:
如果有其他方法可以实现第一段描述的功能而不是使用Tun/Tap,那么任何建议当然都会受到欢迎。

注意:

我遇到了这个问题,虽然类似,但它并没有提供关于ioctl()参数的答案。不过,它表明需要调用ioctl()


1
你可能也会对使用libnetfilter_queue / NFQUEUE感兴趣。请参阅https://home.regit.org/netfilter-en/using-nfqueue-and-libnetfilter_queue/进行简要讨论。 - Jay Kominek
1个回答

3

首先,TUN/TAP接口的相关文档可以在Linux内核文档中找到,如果您还没有看过的话,值得一读。当然,示例是用C语言编写的,但仍然有用。

我看到第二个参数被称为TUNSETIFF,我只知道它必须是数字。它在问什么?

这是一个C库常数,您可以使用以下代码确定其值:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_tun.h>

int main(int argc, char **argv) {
  printf("TUNSETIFF: %d\n", TUNSETIFF);
  return 0;
}

在我的系统上运行该命令会返回:
TUNSETIFF: 1074025674

据我所了解,第三个参数应该是某种标志,但我没有找到相关信息。可能有一个标志用于选择是Tun还是Tap隧道。您有关于此的任何信息吗?
第三个参数是一个指向结构体的指针,该结构体具有一个ifr_name属性,其中包含设备名称,以及一个ifr_flags属性,其中包含标志。内核文档提供以下示例代码:
  /* Flags: IFF_TUN   - TUN device (no Ethernet headers) 
   *        IFF_TAP   - TAP device  
   *
   *        IFF_NO_PI - Do not provide packet information  
   */ 
  ifr.ifr_flags = IFF_TUN; 
  if( *dev )
     strncpy(ifr.ifr_name, dev, IFNAMSIZ);

  if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
     close(fd);
     return err;
  }

您可以使用上面我们用来查找TUNSETIFF值的代码来查找不同标志(IFF_TUNIFF_TAP)的值。
如果您正在使用Perl编写代码,则需要一种方法来创建相应的C兼容结构。我的Perl语言有点生疏,无法马上为您提供正确的解决方案。
但是,似乎有许多Perl模块可用于简化整个过程:

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接