Настройка одинаковых адресов TUN-устройства на MacOS

Вопрос или проблема

Первый случай (разные адреса интерфейса)

У меня есть эта простая программа на Go для инициализации интерфейса и его чтения/записи. Простая труба:

package main

import (
    "fmt"
    "log"
    "log/slog"
    "slices"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/songgao/water"
)

func main() {
    // Создание сетевого интерфейса utun{номер}, имя назначается автоматически
    ifce, err := water.New(water.Config{DeviceType: water.TUN})
    if err != nil {
        panic(fmt.Errorf("новый tun: %w", err))
    }

    slog.Info("создан интерфейс", "имя", ifce.Name())
    for {
        fmt.Printf("\n")

        // Ожидание и чтение пакета из интерфейса DestAddr(?) в buf
        buf := make([]byte, 1024)
        n, err := ifce.Read(buf)
        if err != nil {
            panic(fmt.Errorf("чтение ifce: %w", err))
        }

        p := gopacket.NewPacket(buf[:n], layers.LayerTypeIPv4, gopacket.Default)
        icmpPacket, isICMP := p.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4)
        if !isICMP {
            slog.Warn("не ICMP пакет")
            continue
        }
        slog.Info("Получено сообщение", "байты", n, "тип", icmpPacket.TypeCode, "последовательность", icmpPacket.Seq)

        // Запись пакета на Addr(?)
        n, err = ifce.Write(buf[:n])
        if err != nil {
            panic(fmt.Errorf("запись ifce: %w", err))
        }
        slog.Info("Записан пакет", "байты", n, "тип", icmpPacket.TypeCode, "последовательность", icmpPacket.Seq)
    }
}

После запуска процесса и получения имени интерфейса, я настроил адреса:

ifconfig utun6 10.1.0.10 10.1.0.20 up
ifconfig -v utun6
#    utun6: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500 index 25
#        eflags=1002080<TXSTART,NOAUTOIPV6LL,ECN_ENABLE>
#        xflags=4<NOAUTONX>
#        inet 10.1.0.10 --> 10.1.0.20 netmask 0xff000000
#        generation id: 958
#        state availability: 0 (true)
#        scheduler: FQ_CODEL
#        qosmarking enabled: no mode: none
#        low power mode: disabled
#        multi layer packet logging (mpklog): disabled
#        routermode4: disabled
#        routermode6: disabled
Что я вижу

Если я пингую адрес назначения, я получаю таймауты без ответов, и моя программа получает ICMP сообщение:

ping -c 1 10.1.0.20
# PING 10.1.0.20 (10.1.0.20): 56 data bytes
#
# --- 10.1.0.20 ping statistics ---
# 1 packets transmitted, 0 packets received, 100.0% packet loss

вывод программы go:
# INFO Получено сообщение байты=84 тип=EchoRequest seq=0
# INFO Записан пакет байты=84 тип=EchoRequest seq=0

Итак, мы получили пакет и отправили его куда-то (10.1.0.10) без ответа, он был потерян.


Второй случай (одинаковые адреса)

Давайте изменим шаг настройки utun6 на это:

sudo ifconfig utun6 10.1.0.20 10.1.0.20 up
ifconfig utun6                         
#    utun6: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
#        inet 10.1.0.20 --> 10.1.0.20 netmask 0xff000000

ping -c 2 10.1.0.20
# PING 10.1.0.20 (10.1.0.20): 56 data bytes
# 64 bytes from 10.1.0.20: icmp_seq=0 ttl=64 time=0.422 ms
# 64 bytes from 10.1.0.20: icmp_seq=1 ttl=64 time=0.476 ms

вывод программы go:
# INFO Получено сообщение байты=84 тип=EchoRequest seq=0
# INFO Записан пакет байты=84 тип=EchoRequest seq=0
# INFO Получено сообщение байты=84 тип=EchoReply seq=0
# INFO Записан пакет байты=84 тип=EchoReply seq=0
# INFO Получено сообщение байты=84 тип=EchoRequest seq=1
# INFO Записан пакет байты=84 тип=EchoRequest seq=1
# INFO Получено сообщение байты=84 тип=EchoReply seq=1
# INFO Записан пакет байты=84 тип=EchoReply seq=1

Вопросы

Первый случай (разные адреса)

Мое предположение:

  1. ping отправляет ICMP пакет на 10.1.0.20
  2. Интерфейс/сокет слушает на 10.1.0.20 и получает пакет
  3. Мы отправляем его куда-то (10.1.0.10)??? (Я предполагаю, что из-за отсутствия маршрутного правила ядро не знает, что делать, и эти пакеты просто отбрасываются никуда)

Что на самом деле происходит на шаге 3? Почему я не могу прослушивать сообщения с 10.1.0.10 (или могу, на той же машине, не создавая обратный интерфейс)? Я предполагаю, что я мог бы настроить маршрут обратно к интерфейсу, чтобы смоделировать поведение второго случая с помощью route add).

Второй случай (одинаковые адреса)

Итак, как я понимаю:

  1. ping отправляет ICMP пакет на 10.1.0.20
  2. Интерфейс/сокет слушает на 10.1.0.20 и получает пакет (8 EchoRequest)
  3. Пакет отправляется на тот же интерфейс, и мы получаем это отправленное сообщение в нашей программе на go на втором цикле (я предполагаю, что оба адреса указывают на наш интерфейс, поэтому мы получаем сообщение обратно, как при петлевой проверке)
  4. Мы получаем сообщение с шага 3, но с другим типом (0 EchoReply)
  5. Как-то мы возвращаем сообщение обратно команде ping

Почему мы получаем разные данные (тип), отправляя их через/в тот же интерфейс? Я предполагаю, это связано с тем, как ядро внутренне их преобразует, но почему? И почему ping получает ответ, все что мы делаем – это записываем его обратно на наш интерфейс, почему у нас не происходит бесконечная пересылка пакетов? Почему EchoRequest возвращается в нашу программу, но EchoReply распознается ping?

Я пытался разобраться в страницах man и route/ifconfig/netstats/tcpdump/гуглил в течение нескольких дней, но это все еще слишком запутано, а документация слишком неопределённая, чтобы правильно понять, что здесь происходит. Можете ли вы предложить какой-либо материал для чтения, чтобы понять весь этот хаос?

Ответ или решение

На данный момент в данной проблеме мы имеем два случая работы с TUN-интерфейсами в macOS, где первый случай используют разные IP-адреса, а второй — одинаковые. Давайте рассмотрим каждый случай подробнее и ответим на ваши вопросы.

Первый случай: Разные адреса (10.1.0.10 и 10.1.0.20)

В этом сценарии при настройке интерфейса с разными адресами (10.1.0.10 для локального адреса и 10.1.0.20 для удаленного) вы сталкиваетесь с проблемой, когда ICMP-пакеты, отправленные на 10.1.0.20, не возвращаются. Рассмотрим ваши утверждения по шагам:

  1. Отправка пакета: Когда вы пингуете 10.1.0.20, пакет ICMP действительно отправляется на этот адрес, и ваш интерфейс, слушающий на 10.1.0.20, получает его.

  2. Прослушивание интерфейса: Так как интерфейс настроен на 10.1.0.20, он корректно принимает пакеты, направленные на этот адрес. Вы это наблюдаете в выводе вашего Go-приложения, где вы получаете EchoRequest.

  3. Проблема с маршрутизацией: На этом этапе происходит недоразумение. Когда ваше приложение пытается отправить пакет обратно по адресу 10.1.0.10, проблема заключается в том, что системы маршрутизации может не знать, как достать до 10.1.0.10, так как на этой же машине нет маршрута, ведущего обратно на себя через utun6. Это происходит из-за отсутствия маршрутных записей, и как следствие, пакеты просто теряются.

Чтобы «разрешить» ситуацию, вы можете использовать команду route add для создания маршрута, который укажет, что пакеты к этому адресу следует возвращать через интерфейс utun6. Это позволит вашему приложению получать пакеты обратно.

Второй случай: Одинаковые адреса (10.1.0.20 и 10.1.0.20)

Во втором случае, когда вы задали одинаковые IP-адреса для вашего интерфейса, ситуация изменяется:

  1. Отправка пакета: Аналогично первому случаю, вы отправляете пинг на 10.1.0.20.

  2. Прослушивание адреса: Интерфейс получает EchoRequest и обрабатывает его.

  3. Отправка пакета обратно: Так как вы написали этот же пакет обратно на тот же IP-адрес (10.1.0.20), это ведет к вопросу о том, как работает механизм обработки. Пакет перехватывается и обрабатывается вашей программой опять, возвращая ICMP-пакет, который теперь получает тип EchoReply.

  4. Тип пакета: Обратите внимание, что при возврате пакета тип ICMP изменяется на EchoReply. Это поведение реализовано в стекe сетевых протоколов, где конечные точки (клиент и сервер) различают типы ICMP-сообщений в зависимости от их состояния («запрос» или «ответ»). Таким образом, передавая ответ клиенту (в данном случае ping), система отличает, что это ответ на предыдущий запрос.

Причины отсутствия бесконечной передачи пакетов

Процесс передачи организован так, чтобы избежать зацикливания. Операционная система не пересылает один и тот же пакет обратно, если он уже был обработан, что предотвращает создание зацикливания и застревания в одном состоянии.

Рекомендации по чтению

Чтобы глубже понять, как работают TUN/GRE интерфейсы и управление маршрутизацией в macOS, рекомендуется изучить следующие материалы:

  1. «TCP/IP Illustrated, Volume 1: The Protocols» — книга, предоставляющая надежное основание в понимании протоколов TCP/IP.
  2. man pages на macOS по командным утилитам, связанным с сетью: ifconfig, route, ping, и tcpdump.
  3. Документация и статьи по gopacket и Go — изучите, как библиотека работает на низком уровне.

Эти ресурсы помогут вам лучше ориентироваться в сетевых протоколах и архитектуре, что, в свою очередь, станет основой для дальнейшего исследования вопросов маршрутизации и управления трафиком в сети.

Оцените материал
Добавить комментарий

Капча загружается...