如何伪装窃听接口流量

ram*_*ram 4 c java linux vpn iptables

我目前正在使用 Java 开发 VPN 服务器,至少尽可能使用 Java,并且我计划通过tap设备执行客户端数据包的路由。

目前,我能够将ethernet帧写入tap设备,并且可以通过观察这些数据包tcpdump。然而eth0,尽管我启用了 ip 转发MASQUERADE并向iptables. (这个问题似乎与那个相同,只是网关接口是一个真实的接口,而在我的情况下是一个虚拟接口。)

的输出ifconfig tap0如下:

tap0      Link encap:Ethernet  HWaddr 82:7d:95:39:71:a1  
          inet addr:10.1.0.1  Bcast:10.1.255.255  Mask:255.255.0.0
          inet6 addr: fe80::807d:95ff:fe39:71a1/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:767 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:56838 (56.8 KB)  TX bytes:0 (0.0 B)
Run Code Online (Sandbox Code Playgroud)

的输出ip link show tap0如下:

12: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 82:7d:95:39:71:a1 brd ff:ff:ff:ff:ff:ff
Run Code Online (Sandbox Code Playgroud)

以下是我连接到 Tap 设备的方法:

int helper_open(const char* dev_name, int tun_or_tap) {
    struct ifreq ifr;
    int fd;

    if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
        return -1;
    }

    memset(&ifr, 0, sizeof (ifr));
    strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);
    ifr.ifr_flags = IFF_NO_PI;
    if (tun_or_tap == DEVICE_TUN) {
        ifr.ifr_flags |= IFF_TUN;
    } else if (tun_or_tap == DEVICE_TAP) {
        ifr.ifr_flags |= IFF_TAP;
    } else {
        return -2;
    }

    if (ioctl(fd, TUNSETIFF, (void *) &ifr) == -1) {
        close(fd);
        return -3;
    }

    return fd;
}
Run Code Online (Sandbox Code Playgroud)

成功获取文件描述符后,通过write()调用写入设备就很简单了。

我如何准备以太网帧如下:

public boolean sendIp(byte[] buffer, int start, int length) {
    byte[] frame = new byte[length+14];
    System.arraycopy(mac, 0, frame, 0, 6);
    System.arraycopy(mac, 0, frame, 6, 2);
    byte[] ip = IpUtils.getSourceIp(buffer, start).getAddress();
    for (int i = 0; i < 4; i++) {
        frame[8+i] = (byte) (0xFF & (ip[i] ^ mac[i+2]));
    }
    frame[12] = 0x08;
    frame[13] = 0x00;
    System.arraycopy(buffer, start, frame, 14, length);
    try {
        write(frame, 0, frame.length);
        return true;
    } catch (IOException e) {
        logger.error("cannot send ip packet.", e);
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

mac是设备的 MAC 地址tap,我通过将 Tap 设备 MAC 的最后四个字节与我分配的虚拟 IP 进行异或来生成客户端的 MAC 地址。(在我的测试中,客户端的IP是10.1.0.2。)这样,对于所有参与者来说都是唯一的,并且很容易处理ARP/RARP协议。

RX packets从输出字段可以看出ifconfig,设备接收到数据包tap。另外,示例tcpdump -i tap0 -n输出如下:

15:53:48.395082 IP 10.1.0.2.47132 > 216.58.208.34.443: Flags [S], seq 3162009985, win 65535, options [mss 1460,sackOK,TS val 4294939804 ecr 0,nop,wscale 6], length 0
15:53:49.396355 IP 10.1.0.2.39713 > 216.58.208.42.443: Flags [S], seq 2459164785, win 65535, options [mss 1460,sackOK,TS val 4294939905 ecr 0,nop,wscale 6], length 0
15:53:49.678691 IP 10.1.0.2.58306 > 194.177.210.54.123: NTPv3, Client, length 48
15:53:50.508132 IP 10.1.0.2.38112 > 172.217.22.110.443: Flags [S], seq 3132386571, win 65535, options [mss 1460,sackOK,TS val 4294940016 ecr 0,nop,wscale 6], length 0
15:53:51.519119 IP 10.1.0.2.37492 > 216.58.207.42.443: Flags [S], seq 3750738666, win 65535, options [mss 1460,sackOK,TS val 4294940117 ecr 0,nop,wscale 6], length 0
Run Code Online (Sandbox Code Playgroud)

数据包已被 tcpdump 正确解码,因此看来我正在成功准备以太网帧。sysctl net.ipv4.ip_forward显示ip转发已启用。那为什么他们没有被路由通过呢eth0

输出iptables -L -n -v -t nat

Chain PREROUTING (policy ACCEPT 2436 packets, 132K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain INPUT (policy ACCEPT 2436 packets, 132K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 20 packets, 1462 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   20  1462 MASQUERADE  all  --  *      eth0    0.0.0.0/0            0.0.0.0/0
Run Code Online (Sandbox Code Playgroud)

和输出route -n

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         173.212.233.1   0.0.0.0         UG    0      0        0 eth0
10.1.0.0        0.0.0.0         255.255.0.0     U     0      0        0 tap0
173.212.233.0   173.212.233.1   255.255.255.0   UG    0      0        0 eth0
173.212.233.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0
Run Code Online (Sandbox Code Playgroud)

非常感谢任何形式的帮助。

PS:我正在Ubuntu 16.04上进行开发。

编辑: 为了确保数据包不会偏离eth0,我在不同的终端上启动tcpdump -i eth0 host 5.189.147.197 -ntcpdump -i tap0 host 5.189.147.197 -n,同时客户端尝试连接到5.189.147.197。我观察到了接口上的流量tap0,但没有观察到eth0接口上的流量。所以它们肯定不会被转发。

ram*_*ram 5

与往常一样,原因很简单:在注入数据包时,我错误地计算了 IP 标头中的校验和。也就是说,我忘记在添加后翻转这些位。为我感到羞耻:(。

我不会删除整个问题,因为它可能会帮助人们开发类似的应用程序。

要检查校验和是否正确,您可以tcpdump以更详细的模式运行,即包含一些-v参数。我曾经tcpdump -i tap0 -n -X -s 0 -vvv也观察过数据包的内容。这对我帮助很大。

关于用户1794469建议的桥接选项,您需要在该子网中有额外的IPeth0可分配给客户端,因为桥接使客户端就像一个新的参与者(没有任何NAT)加入到该子网。但是,我的 VPS 分配了一个 IP,并且它不在 NAT 之后,因此没有 DHCP 等。这就是为什么我需要拥有自己的 NAT。现在它完美地工作了。