Java 如何伪装tap接口流量

Java 如何伪装tap接口流量,java,c,linux,vpn,iptables,Java,C,Linux,Vpn,Iptables,我目前正在用Java开发一个VPN服务器,至少要用Java开发尽可能多的VPN服务器,并且我计划通过tap设备执行客户端数据包的路由 目前,我能够将以太网帧写入轻触设备,并且可以通过tcpdump观察这些数据包。但是,它们不会通过eth0路由,尽管我启用了ip转发,并在iptables中添加了MASQUERADE规则。(这个问题似乎与相同,只是网关接口是一个真实的接口,在我的情况下是一个虚拟接口。) ifconfig tap0的输出如下: tap0 Link encap:Etherne

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

目前,我能够将
以太网
帧写入
轻触
设备,并且可以通过
tcpdump
观察这些数据包。但是,它们不会通过
eth0
路由,尽管我启用了ip转发,并在
iptables
中添加了
MASQUERADE
规则。(这个问题似乎与相同,只是网关接口是一个真实的接口,在我的情况下是一个虚拟接口。)

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)
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
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;
    }
}
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
ip link show 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)
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
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;
    }
}
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
成功获取文件描述符后,通过
write()
调用写入设备是很简单的

我如何准备以太网帧,如下所示:

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)
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
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;
    }
}
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
数据包被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
并输出
路由-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
非常感谢您的任何帮助

附言:我正在Ubuntu 16.04上开发

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

一如既往,原因很简单:在注入数据包时,我错误地计算了IP报头中的校验和。也就是说,我忘了在添加后翻转位。我真丢脸

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

要检查校验和是否正常,您可以在更详细的模式下运行
tcpdump
,即包括一些
-v
参数。我使用
tcpdump-i tap0-n-X-s0-vvv
来观察数据包的内容。这对我帮助很大


关于user1794469建议的桥接选项,您需要在
eth0
子网中具有可分配给客户端的额外IP,因为桥接使客户端看起来像一个新的参与者(没有任何NAT)加入子网。但是,我的VPS有一个单独的IP分配给它,并且它不在NAT后面,所以没有DHCP等。这就是为什么我需要有自己的NAT。现在它可以完美地工作。

你看到eth0上的
tcpdump
有什么吗?它们根本就不存在吗?过滤表(
sudo iptables-L
)上的规则是什么?@user1794469所有链都将
接受
作为默认策略,并且没有其他规则。@user1794469由于响应不会返回,我认为它们根本不会到达
eth0
,但让我确定一下。将更新。@user1794469请查看编辑。很快,数据包不会出现在
eth0
上。您知道吗你知道发生了什么吗?实际上这是一个非常基本的路由问题。也许有比我更了解的人可以插话,但我认为你需要将
tap0
eth0
连接起来。以下是有关如何: