Ввод/вывод с интерфейсом Tun

Цель состоит в том, чтобы программа перехватывала набор IP-пакетов и считывала их необработанное содержимое, а затем повторно вставляла его в сеть после обработки.

Мой подход основан на настройке интерфейса Tuntap (точнее, Tun), а затем iptables и подобные перенаправляют нужные пакеты на этот туннельный интерфейс.

В целях тестирования я написал этот короткий сценарий оболочки, который настраивает интерфейс 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() звонка.


person Jarmund    schedule 25.05.2015    source источник
comment
Вас также может заинтересовать использование libnetfilter_queue/NFQUEUE. см. home.regit.org/netfilter-en/ для краткого обсуждения.   -  person Jay Kominek    schedule 29.05.2015


Ответы (1)


Во-первых, интерфейс 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;
  }

Вы можете найти значения для различных флагов (IFF_TUN, IFF_TAP), используя тот же код, который мы использовали для поиска значения TUNSETIFF.

Если вы пишете код на Perl, вам понадобится способ создать соответствующую C-совместимую структуру. Мой Perl слишком заржавел, чтобы с ходу предложить правильное решение.

Однако, по-видимому, существует ряд доступных модулей Perl, которые упрощают весь процесс:

person larsks    schedule 27.05.2015