Идеи или алгоритмы при программировании NAT

Я работаю над проектом туннелирования Python с использованием TUNTAP. Данные, полученные через интерфейс TUNTAP, содержат исходный IP-пакет, включая все заголовки. Я могу сделать одно из двух.

На входящей стороне я слушаю Twisted. На исходящей стороне у меня будет сырой сокет, который выгружает IP-пакет. Перед сбросом пакета программа меняет местами адрес источника с адресом сервера. Он также пересчитывает контрольные суммы TCP и UDP. Он также меняет местами порты, используя один из следующих методов. Эта информация отслеживается в таблице NAT.

1) Используйте один порт для каждого пользователя, например

 US.ER.01.IP:10000 ----> SE.RV.ER.IP:3000 ----> facebook.com:80
 US.ER.01.IP:10001 ----> SE.RV.ER.IP:3000 ----> facebook.com:80
 US.ER.02.IP:3000 ----> SE.RV.ER.IP:3001 ----> facebook.com:80

Может ли это вызвать проблемы, если второй с одновременными запросами пользователя на facebook? Как система узнает, как маршрутизировать ответ facebook. Он поступает на порт 3000, поэтому он принадлежит пользователю user1, но отображается ли он обратно на 10000 или 10001?

2) Используйте уникальный порт для каждого подключения, например

 US.ER.01.IP:10000 ----> SE.RV.ER.IP:3000 ----> facebook.com:80
 US.ER.01.IP:10001 ----> SE.RV.ER.IP:3001 ----> facebook.com:80
 US.ER.02.IP:3000 ----> SE.RV.ER.IP:3002 ----> remoteHost.com:22

Как мне узнать, когда нужно удалить записи из таблицы NAT? Я мог видеть, как таблица NAT заполнялась очень быстро, используя этот метод. Решения для этого:

  I could wit for FIN packets from the server.  This will not work with UDP though.
  I could age the NAT entry on each hit.  I could then run garbage collection 
     every N seconds.  I see this being an issue if garbage collection runs
     and how would a server's delayed response get to the proper host if it gets
     deleted from the table.

Также существует проблема чтения из сырого сокета. Я знаю, как отправлять по одному, но можно ли получать отдельные IP-пакеты. Может ли необработанный сокет получить один пакет на вызов sock.recieve (65535), возможно, получить более одного IP-пакета?

Какая реализация лучше? Есть ли другие советы или вещи, на которые мне следует обратить внимание?

РЕДАКТИРОВАТЬ:

Итак, у меня N много клиентов. Если вы меня неправильно поняли, между клиентом и им самим используется enitre / 30. Это просто абстракция, позволяющая создать туннель. Я также не думал, что это имеет значение, но на самом деле веб-сокет проходит через «прокси» в локальной сети (IP-данные просто переупаковываются в новый веб-сокет, однако сопоставления уникальны). Я не хотел, чтобы объяснение было таким запутанным. Я не вижу, как это что-то меняет.

      Client PC     CLIENT PC              Client PC----->LAN                               INTERNET    
 Client 1: 10.1.1.2 ----> 10.1.1.1 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP
 Client 2: 10.1.1.4 ----> 10.1.1.3 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP
 Client 3: 10.1.1.6 ----> 10.1.1.5 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP

Каждый клиент устанавливает свой маршрут по умолчанию как конечную точку туннеля (например, 10.1.1.1). Клиент получает IP-датаграмму, помещает ее в веб-сокет, отправляет веб-сокет в браузер в локальной сети, который затем отправляет его на сервер (или, возможно, другой прокси). Внутри веб-сокета содержится исходная IP-дейтаграмма (с источником 10.1.1.2 или каким-либо другим внутренним IP-адресом).

Важно отметить, что сервер получает сообщение веб-сокета из Интернета, СОДЕРЖАЩИЕ блага (с частным адресом источника). Как это будет использовать сервер Python? Создать новый туннель с самим собой, затем выгружать необработанный пакет в туннель и соответствующим образом маршрутизировать?

Или, может быть, я мог бы использовать отображение?

Как я смогу «отобразить» абстракцию туннеля по этой цепочке веб-сокетов? У клиента нет маршрута к Интернету, но он может подключиться к «Браузеру», который может подключиться к Интернету. Похоже, то же самое и с туннелями VPN. Абстракция будет такой:

 Client 1: 10.1.1.2 ----> 10.1.1.1 ----> Websocket(IPdata) ----> Browser ---> newWebSocket(IPData) ----> SE.RV.ER.IP -> Internet
           10.1.2.2------------------------------------------------------------------------------------> 10.1.2.1 ----> Internet

Если вы знаете какие-либо ресурсы, которые помогут мне встать на верный путь, это было бы здорово!


person user974896    schedule 04.11.2012    source источник


Ответы (1)


Реализация NAT

Вы должны использовать уникальный порт для каждого подключения, а не один порт для каждого пользователя, именно по той причине, которую вы указали в своем вопросе: если вы этого не сделаете, вы можете (и будете!) В конечном итоге получить несколько подключений с использованием одних и тех же 5 -tuple (протокол, локальный адрес, локальный порт, удаленный адрес, удаленный порт), и вы не сможете устранить их неоднозначность.

Более того, если вы хотите поиграть с некоторыми протоколами, которые выполняют обход NAT, вам следует попытаться не переназначить исходный исходный порт, если это возможно, то есть переназначить его (на новый случайный порт) только в том случае, если он конфликтует с существующим соединением, которое вы отслеживаете.

Чтобы правильно реализовать NAT, вы должны отслеживать состояние каждого подключения.

Для TCP это означает наблюдение за флагами, установку нового состояния, когда вы видите SYN, и разрушение состояния, когда вы видите FINs с обеих сторон. Состояние, которое вы отслеживаете, должно содержать как минимум исходный исходный порт и переназначенный исходный порт (которые могут быть одинаковыми, см. Выше). Если вы хотите поддерживать FTP, вам также придется обнюхивать содержимое управляющих соединений FTP TCP и переписывать содержащиеся в нем IP-адреса (а это означает, что вам нужно будет отслеживать гораздо больше состояний, потому что иногда вам может потребоваться увеличить сегмент TCP, который означает, что вам нужно начать переназначение порядковых номеров). У вас также должен быть тайм-аут, связанный с каждым отслеживаемым соединением, чтобы вы могли избавиться от него в случае, если конечные точки исчезнут без надлежащего закрытия соединения.

Для UDP это означает наблюдение за комбинациями номеров локальных и удаленных портов и создание состояния для каждой уникальной комбинации (из 4-х кортежей адресов и портов), которую вы видите. Поскольку UDP не поддерживает соединение, вы должны истечь эту информацию о состоянии на основе тайм-аута. Этот тайм-аут будет намного короче, чем тот, который вы используете для TCP (порядка минут, а не часов), чтобы ваша таблица состояний не стала слишком большой.

Для эхо-запроса ICMP вы должны действовать аналогично UDP с icmp_id, играющим роль номера порта.

Для других типов ICMP, таких как пункт назначения недоступен, вы должны проверить пакет ICMP, чтобы увидеть, является ли он частью отслеживаемого соединения TCP или UDP, и попытаться перевести его обратно в исходный источник.

Чтобы предотвратить петли маршрутизации, вы также должны уменьшать TTL IP по мере пересылки транслированных пакетов.

Наверное, есть еще несколько важных моментов, о которых я забываю. Короче говоря, реализация NAT очень похожа на реализацию IP-стека для маршрутизатора! Вот почему виртуальный NAT всегда привязан к стеку IP в ядре, а не реализован в пользовательском пространстве.

Отправка и получение пакетов

Итак, архитектура, как я понимаю, такая:

  1. Клиент создает пакет, который поступает в интерфейс TUNTAP.
  2. Ваше программное обеспечение получает этот пакет, инкапсулирует его в сообщение Websocket и отправляет его.
  3. Ваш Twisted сервер получает это и творит чудеса
  4. Транслированный пакет уходит с сервера через необработанный сокет

Обратный путь:

  1. Ответ каким-то образом возвращается на ваш сервер (возможно, libpcap)
  2. Ваш код делает обратную магию
  3. Ваш сервер передает результат обратно клиенту через Websocket.
  4. Клиент видит, что полученный бэкет возвращается через интерфейс TUNTAP.

Я думаю, что самый простой способ обработать последний шаг на прямом пути и первый шаг на обратном пути - это второе устройство TUNTAP: tun интерфейс на сервере.

person Celada    schedule 05.11.2012
comment
Вот это да. Спасибо за подробный ответ. Возможно, я не очень хорошо это объяснил. Обычно клиент открывает туннель с самим собой, используя два частных IP-адреса (10.1.1.2 --- ›10.1.1.1). Принимающий конец туннеля направляет IP-пакет через веб-сокет на скрученный сервер. Данные скрученного получателя - ОТ общедоступного IP-адреса пользователя user1, однако сами данные представляют собой IP-пакет (скорее всего, из 10.1.1.2). Затем сервер выполняет NAT и затем отправляет его. - person user974896; 05.11.2012
comment
Затем я прослушивал входящий пакет, просматривал его в таблице NAT, возвращал частный IP-адрес клиента, упаковывал его в веб-сокет и отправлял обратно. Затем клиентский TUNTAP получит его (на 10.1.1.1) и отправит обратно на 10.1.1.2. Вы правы, второй TUNTAP на сервере было бы проще. Это возможно? - person user974896; 05.11.2012
comment
Есть ли способ создать мост TUN через веб-сокет и \ или каким-то образом использовать возможности NAT ядра вместо того, чтобы реализовывать свои собственные? Вместо IP через GRE это IP через WebSocket. РЕДАКТИРОВАТЬ: Кстати, я очень ценю помощь. - person user974896; 06.11.2012
comment
Безусловно, использование существующих возможностей NAT ядра на вашем сервере - это, я думаю, способ избавить себя от необходимости реализовывать многие вещи самостоятельно. Единственная важная проблема, о которой я могу думать, заключается в том, что ядро ​​не знает, что у вас есть несколько независимых пользователей, каждый из которых использует свое собственное соединение с веб-сокетом. Чтобы решить эту проблему, необходимо убедиться, что каждый пользователь использует уникальный частный IP-адрес (например, 10.1.1.2/30 для клиента A, 10.1.1.6/30 для клиента B), в отличие от того, чтобы каждый клиент повторно использовал один и тот же частный IP-адрес на стороне клиента. Благодаря этому серверное ядро ​​может различать клиентов. - person Celada; 07.11.2012
comment
Я просто не понимаю, как я могу выполнять сопоставление по этому случайному протоколу. У исходного клиента должен быть маршрут к конечной точке туннеля. Также, что сервер будет делать с входящим пакетом. Пожалуйста, посмотрите мои правки. - person user974896; 07.11.2012
comment
Я думаю, что дизайн этого проекта выходит за рамки вопроса о Stackoverflow ... Я понимаю, что 10.xxx/30 является частным для каждого клиента, но причина, по которой это имеет значение для сервера, заключается в том, что один из этих частных IP-адресов является исходный IP-адрес пакетов, которые отправляет клиент и которые прибывают (инкапсулированные в веб-сокеты) на сервер. В любом случае, когда сервер получает его, он может выстрелить в tun интерфейс на своем конце. Ядро сервера теперь получает исходный пакет и должно быть запрограммировано на NAT (это делается с iptables, если это Linux, в противном случае - с чем-то еще). - person Celada; 07.11.2012
comment
Спасибо, что предоставили мне хороший обзор. Я думаю, что получил это отсюда - person user974896; 07.11.2012
comment
Фрагментация - это еще одна вещь, которую следует учитывать в целом для NAT. За исключением первой дейтаграммы, все остальные дейтаграммы фрагментированных пакетов не будут содержать транспортный заголовок. Поэтому нам нужно отслеживать эти фрагментированные дейтаграммы (на какой порт отправлять), используя их поле IP ID. Также фрагментированная дейтаграмма может прибыть раньше дейтаграммы, содержащей транспортный заголовок, поэтому мы должны их буферизовать. Очень хороший подробный ответ Селады. - person user138645; 01.06.2013