Часть содержания в статье, поскольку не было найдено особо авторитетной информации, смешано с большим количеством личного понимания.Если есть какие-либо ошибки, пожалуйста, укажите.
задний план
Из-за некоторых особых потребностей людей я хочу распределять свой собственный трафик mbp внутри.Простое описание состоит в том, что некоторые прямые подключения, некоторые используют корпоративный VPN, а некоторые используют прокси socks5.
Ознакомьтесь с некоторыми решениями на рынке:
- ПКК. Конфигурация относительно проста и удобна, но для многих приложений нельзя использовать PAC, особенно для терминальных приложений.
- мягкий. Я не знаю, почему, когда правил слишком много, некоторые правила не вступят в силу (я не нашел соответствующей проблемы, возможно, проблема с моей конфигурацией)
- всплеск. Конфигурация очень удобная, но есть три проблемы.Первая проблема-это дорого.Вторая проблема-что DNS-разрешение показывает DNS-адрес внутри всплеска(эта причина не имеет значения) и третья и самая главная проблема в том, что менее дружелюбная поддержка одновременного использования других VPN
- проксификатор. Я не знаю почему, но иногда мой компьютер зависает при его использовании, а иногда сеть хаотична
Итак, я хочу реализовать инструмент для распределения сетевого трафика самостоятельно.
Виртуальная сетевая карта TUN/TAP
Что касается проблемы распределения сетевого трафика, я видел много решений в Интернете, лично меня больше интересует использование виртуальных сетевых карт TUN/TAP для ее решения. Из-за ограниченных возможностей и процесса обучения в этом выпуске реализована только переадресация пинг-запросов.
Вкратце опишите, что TUN/TAP — это сетевое устройство, моделируемое программным обеспечением, обеспечивающее те же функции, что и сетевое устройство. Среди них TAP имитирует устройство Ethernet, то есть управляет вторым уровнем пакетов данных. TUN имитирует устройство сетевого уровня, то есть пакет данных третьего уровня.
Затем давайте посмотрим, как мы работаем с виртуальной сетевой картой, поскольку пакет IP прост, поэтому я решил использовать устройство TUN. Вся эта статья основана на операционной системе Mac, в качестве примера взято устройство TUN и используется язык python для написания кода.
Для macOS управлять устройством tun относительно просто, но сначала необходимо установить TUN/TAP.
brew cask install tuntap
После установки вы можете увидеть наш/devВ каталог добавляются следующие файлы: tap0 --- tap15, tun0 --- tun15
В linux все по-другому, но информации о TUN/TAP в linux в Интернете по-прежнему относительно много, поэтому здесь подробно объясняться не будем.
Далее, как управлять устройством tun.Для Mac вся операция очень проста, как и непосредственное управление файлом.Код выглядит следующим образом:
import os
import subprocess
from scapy.layers.inet import *
tun_fd = os.open('/dev/tun11', os.O_RDWR)
subprocess.check_call('ifconfig tun11 192.168.7.1 192.168.7.2 up', shell=True)
# 添加路由规则,用于测试
subprocess.check_call('route -n add -net 180.101.49.0 -netmask 255.255.255.0 192.168.7.2', shell=True)
while True:
packet = os.read(tun_fd, 2048)
ip = IP(packet) # 此处使用了scapy包来处理网络协议
ip.show()
Запустите программу (для запуска требуется sudo). Мы можем сначала использоватьifconfigДавайте посмотрим на состояние нашего сетевого оборудования.Мы видим, что в нашем сетевом оборудовании есть еще одно устройство tun11, но у этого устройства два IP-адреса.Нам может быть более любопытно.Это объясняется ниже.
Точно так же давайте посмотрим на наши правила маршрутизации, и мы можем найти еще два правила маршрутизации устройств tun, среди которых192.168.7.2 -> 192.168.7.1Он создается автоматически при создании виртуальной сетевой карты, а другой добавляется вручную в коде.
Далее пропингуем адрес 180.101.49.10 и посмотрим возвращенный результат.
Из этой информации мы видим, что это пакет ICMP, что более интересно, мы можем обнаружить, что исходный IP-адрес пакета соответствует исходному адресу устройства TUN.
Имитация ответа на пинг с помощью устройства TUN
Далее пытаемся ответить на этот ICMP пакет, нам просто нужно поменять местами src_ip и dst_ip в пакете, а затем установить тип ICMP наecho-reply, конечно, нужно еще пересчитывать контрольную сумму в конце. код показывает, как показано ниже:
import os
import subprocess
from scapy.layers.inet import *
tun_fd = os.open('/dev/tun11', os.O_RDWR)
subprocess.check_call('ifconfig tun11 192.168.7.1 192.168.7.2 up', shell=True)
# 添加路由规则,用于测试
subprocess.check_call('route -n add -net 180.101.49.0 -netmask 255.255.255.0 192.168.7.2', shell=True)
while True:
packet = os.read(tun_fd, 2048)
ip = IP(packet) # 此处使用了scapy包来处理网络协议
reply_icmp = ICMP(ip.payload)
reply_icmp.setfieldval('type', 0) # 0代表echo-reply
reply_icmp.setfieldval('chksum', None) # 设为None,scapy包会自动计算
reply_ip = IP(packet)
reply_ip.setfieldval('src', ip.dst)
reply_ip.setfieldval('dst', ip.src)
reply_ip.setfieldval('len', None)
reply_ip.setfieldval('chksum', None)
reply_ip.setfieldval('payload', reply_icmp)
os.write(tun_fd, bytes(reply_ip)) # 此处就直接往tun_fd直接回写数据即可
Результат работы следующий:
Здесь мы видим, что если мы хотим вернуть данные, мы можем напрямую записать данные обратно в tun_fd.Здесь мы можем фактически угадать роль двух IP-адресов сетевой карты TUN.Когда трафик приложения проходит через устройство TUN, исходный IP-адрес пакета, отправленного приложением, соответствует исходному IP-адресу устройства TUN, а целевой IP-адрес устройства TUN подобен IP-адресу этой виртуальной сетевой карты.
Пересылка запросов ping с использованием устройств TUN
До сих пор мы представили базовую работу устройства TUN и ответ на смоделированный запрос ping на основе устройства TUN. Но тогда мы еще больше увеличим сложность.Теперь нам нужно перенаправить пинг-запрос.Вот проблема,то есть бесконечный цикл запроса,потому что конфигурация 180.101.49.11 будет маршрутизировать на устройство TUN,а мы перенаправляем запрос.Этот запрос должен быть отправлен, но когда он будет отправлен, он будет перенаправлен на устройство TUN, что сформирует бесконечный цикл. Как решить эту проблему?
В linux мы можем решить это с помощью iptable, но под mac соответствующей информации не так много. После некоторых исследований я обнаружил, что мы можем отправлять запросы на указание сетевой карты, чтобы мы не могли пройти маршрутизацию. (Этот ответ действительно показатель IQ, но этот вопрос меня реально давно озадачил)
Затем следующая операция относительно проста, но следует отметить, что мы не можем пересылать пинг-пакет как есть, потому что тогда пинг-пакет получит два ответа и появится отметка DUP. Один из них — из внешнего реального ответа, а второй — из ответа, возвращенного нашим прокси.
import select
import uuid
from scapy.all import *
from scapy.layers.inet import *
DEFAULT_NET = 'en0'
BUFFER_SIZE = 2048
# 主程序
tun_fd = os.open('/dev/tun11', os.O_RDWR)
subprocess.check_call('ifconfig tun11 192.168.7.1 192.168.7.2 up', shell=True)
subprocess.check_call('route -n add -net 180.101.49.0 -netmask 255.255.255.0 192.168.7.2', shell=True)
icmp_info_map = {}
icmp_socket_list = []
while True:
read_fds = [tun_fd]
read_fds.extend(icmp_socket_list)
r_fds = select(read_fds, [], [], 10)[0] # 由于kqueue不会写,此处就使用select,简单一点
if r_fds:
for fd in r_fds:
if fd == tun_fd: # 如果是来自虚拟网卡的数据
data = os.read(tun_fd, BUFFER_SIZE)
ip = IP(data)
src_ip = ip.src
dst_ip = ip.dst
if ip.payload.name == 'ICMP':
info = {
'src_ip': src_ip,
'dst_ip': dst_ip,
'data': ip
}
mark_key = str(uuid.uuid1())
icmp_info_map[mark_key] = info # 这里对收到的ping与发送的ping做了一个映射
send_to_remote_ip = IP(dst=dst_ip) / ICMP() / mark_key
icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
icmp_socket.settimeout(5)
icmp_socket.bind(('192.168.199.111', 0)) # 我电脑的en0网卡的地址
icmp_socket.connect((dst_ip, 1))
icmp_socket_list.append(icmp_socket)
sendp(bytes(ICMP(send_to_remote_ip.payload)), socket=icmp_socket)
elif fd in icmp_socket_list: # 如果是来自外部响应数据
data, addr = fd.recvfrom(BUFFER_SIZE)
icmp_socket_list.remove(fd)
fd.close()
recv_ip = IP(data)
recv_icmp = ICMP(recv_ip.payload)
mark_key = str(bytes(recv_icmp.payload), 'utf-8')
if mark_key not in icmp_info_map:
continue
info = icmp_info_map[mark_key]
icmp_info_map.pop(mark_key)
send_icmp = ICMP(info['data'].payload)
send_icmp.setfieldval('type', recv_icmp.type)
send_icmp.setfieldval('chksum', None)
send_ip = IP(dst=info['src_ip'], src=info['dst_ip']) / send_icmp
os.write(tun_fd, bytes(send_ip))
Этот код относительно прост, и все еще есть некоторые проблемы, такие как освобождение тайм-аута сокета, разумность mark_key и так далее. Но в принципе можно грубо выразить простую идею пересылки пакетов ping.Результаты выполнения следующие:
Конечно, когда мы не используем устройство TUN, результат будет таким же, как показано ниже:
На данный момент мы выполнили простую функцию пересылки ping-пакетов с помощью устройства TUN.
Ссылаться на
[1] Управление виртуальными сетевыми картами с помощью Python