Используйте виртуальную сетевую карту TUN для реализации переадресации запросов ping

задняя часть

Часть содержания в статье, поскольку не было найдено особо авторитетной информации, смешано с большим количеством личного понимания.Если есть какие-либо ошибки, пожалуйста, укажите.

задний план

Из-за некоторых особых потребностей людей я хочу распределять свой собственный трафик mbp внутри.Простое описание состоит в том, что некоторые прямые подключения, некоторые используют корпоративный VPN, а некоторые используют прокси socks5.

Ознакомьтесь с некоторыми решениями на рынке:

  1. ПКК. Конфигурация относительно проста и удобна, но для многих приложений нельзя использовать PAC, особенно для терминальных приложений.
  2. мягкий. Я не знаю, почему, когда правил слишком много, некоторые правила не вступят в силу (я не нашел соответствующей проблемы, возможно, проблема с моей конфигурацией)
  3. всплеск. Конфигурация очень удобная, но есть три проблемы.Первая проблема-это дорого.Вторая проблема-что DNS-разрешение показывает DNS-адрес внутри всплеска(эта причина не имеет значения) и третья и самая главная проблема в том, что менее дружелюбная поддержка одновременного использования других VPN
  4. проксификатор. Я не знаю почему, но иногда мой компьютер зависает при его использовании, а иногда сеть хаотична

Итак, я хочу реализовать инструмент для распределения сетевого трафика самостоятельно.

Виртуальная сетевая карта 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-адреса.Нам может быть более любопытно.Это объясняется ниже.

image-20200410014229698

Точно так же давайте посмотрим на наши правила маршрутизации, и мы можем найти еще два правила маршрутизации устройств tun, среди которых192.168.7.2 -> 192.168.7.1Он создается автоматически при создании виртуальной сетевой карты, а другой добавляется вручную в коде.

image-20200410014607291

Далее пропингуем адрес 180.101.49.10 и посмотрим возвращенный результат.

image-20200410014918106

Из этой информации мы видим, что это пакет 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直接回写数据即可

Результат работы следующий:

image-20200410021203537

Здесь мы видим, что если мы хотим вернуть данные, мы можем напрямую записать данные обратно в 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.Результаты выполнения следующие:

image-20200410025133794

Конечно, когда мы не используем устройство TUN, результат будет таким же, как показано ниже:

image-20200410025258060

На данный момент мы выполнили простую функцию пересылки ping-пакетов с помощью устройства TUN.

Ссылаться на

[1] Управление виртуальными сетевыми картами с помощью Python

[2] Давайте напишем VPN вместе