如何MASQUERADE点击界面流量

问题描述 投票:1回答:1

我目前正在用Java开发一个VPN服务器,至少在Java中是这样,我打算通过tap设备执行客户端数据包的路由。

目前,我能够将ethernet帧写入tap设备,我可以通过tcpdump观察这些数据包。然而,他们没有在eth0上路由,虽然我启用了ip转发并在MASQUERADE添加了iptables规则。 (这个问题与that似乎相同,只是网关接口是真实的接口,在我的情况下是虚拟接口。)

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)

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

以下是我如何连接到水龙头设备:

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

成功获取文件描述符后,通过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;
    }
}

mactap设备的MAC地址,我通过将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

数据包被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

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

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

P.S:我正在开发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界面上的流量。所以他们肯定没有转发。

java c linux vpn iptables
1个回答
0
投票

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

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

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

关于user1794469建议的桥接选项,您需要在eth0的子网上有可用于分配给客户端的其他IP,因为桥接使客户端就像新的参与者(没有任何NAT)加入子网一样。但是,我的VPS有一个IP分配给它,它不在NAT后面,所以没有DHCP等。这就是为什么我需要拥有自己的NAT。现在它完美无缺。

© www.soinside.com 2019 - 2024. All rights reserved.