Как MASQUERADE нажмите интерфейс трафика

1

В настоящее время я разрабатываю VPN-сервер в Java, по крайней мере, как можно больше на 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)

Выходной сигнал 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().

Как я подготовляю рамки ethernet следующим образом:

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

mac - это MAC-адрес устройства- tap, и я генерирую MAC-адрес клиента путем XORing последних четырех байтов MAC-адреса коммутатора с помощью виртуального IP-адреса, назначенного мной. (В моих тестах 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, поэтому кажется, что я успешно готовю пакеты Ethernet. 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

Любая помощь высоко ценится.

PS: Я развиваюсь на Ubuntu 16.04.

EDIT: чтобы убедиться, что пакеты не отходят от eth0, я запустил tcpdump -i eth0 host 5.189.147.197 -n и tcpdump -i tap0 host 5.189.147.197 -n на отдельных терминалах, а между тем клиент попытался подключиться к 5.189.147.197. Я наблюдал трафик на интерфейсе tap0 но не на интерфейсе eth0. Конечно, они не перенаправлены.

  • 0
    Что вы видите с tcpdump на eth0? Разве они не делают это там вообще? Каковы правила для таблицы фильтров ( sudo iptables -L )?
  • 0
    @user1794469 user1794469 все цепочки имеют ACCEPT качестве политики по умолчанию, и других правил нет.
Показать ещё 3 комментария
Теги:
iptables
vpn

1 ответ

0
Лучший ответ

Как всегда, причина была довольно простой: во время ввода пакетов я просчитал контрольную сумму в IP-заголовке. А именно, я забыл перевернуть бит после добавления. Мне стыдно :(.

Я не исключаю весь вопрос, так как он может помочь людям при разработке подобных приложений.

Чтобы проверить, соответствуют ли контрольные суммы, вы можете запустить tcpdump в более подробном режиме, то есть включить несколько параметров -v. Я использовал tcpdump -i tap0 -n -X -s 0 -vvv чтобы также наблюдать за содержимым пакетов. Это мне очень помогло.

Что касается опции моста, предложенной пользователем1794469, вам необходимо иметь дополнительные IP-адреса в подсети eth0, которые доступны для назначения клиентам, потому что мосты делают клиента так, как будто новый участник (без какого-либо NAT) присоединился к подсети. Однако мой VPS имеет один IP-адрес, назначенный ему, и он не находится за NAT, поэтому нет DHCP и т.д. Поэтому мне нужно иметь собственный NAT. Теперь он работает безупречно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню