Как заставить ping действительно отправлять пакеты через сетевой интерфейс?

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

Я работаю над устройством для захвата пакетов и временной метки на базе FPGA. Чтобы протестировать его, я хочу попробовать отправить ping. На моем компьютере установлены две двухпортовые 10G сетевые карты, и у меня подключены 3 порта – один это ‘мониторируемый’ порт, где пакеты захватываются в обе стороны, один это ‘пропускной’ порт, который подключен к ‘мониторируемому’ порту, и третий это ‘дамп’ порт, куда отправляются захваченные и помеченные временными метками пакеты.

Вывод команды ip addr с удаленной нерелевантной информацией:

3: enp4s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP group default qlen 1000
    link/ether 00:60:dd:xx:xx:f8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.99.1/24 scope global enp4s0
       valid_lft forever preferred_lft forever
    inet6 fe80::260:ddff:fexx:xxf8/64 scope link 
       valid_lft forever preferred_lft forever
5: enp6s0f0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000
    link/ether 90:e2:ba:xx:xx:e0 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::92e2:baff:fexx:xxe0/64 scope link 
       valid_lft forever preferred_lft forever
6: enp5s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc mq state UP group default qlen 1000
    link/ether 00:60:dd:xx:xx:f9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.99.2/24 scope global enp5s0
       valid_lft forever preferred_lft forever
    inet6 fe80::260:ddff:fexx:xxf9/64 scope link 
       valid_lft forever preferred_lft forever
7: enp6s0f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 90:e2:ba:xx:xx:e1 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::92e2:baff:fexx:xxe1/64 scope link 
       valid_lft forever preferred_lft forever

enp4s0 и enp5s0 – это мониторируемый и пропускной порты, enp6s0f1 – это порт захвата. Я присвоил адреса 192.168.99.1/24 и 192.168.99.2/24 интерфейсам enp4s0 и enp5s0. У меня запущено 3 инстанса Wireshark, по одному на каждом интерфейсе.

Если я открываю сырой сокет на Python и отправляю данные, я вижу это, как и ожидалось, во всех 3 инстансах Wireshark. Однако, когда я запускаю ping (ping -I enp4s0 192.168.99.2), он ‘работает’ (потерь пакетов нет), но ничего не отображается в любом из инстансов Wireshark. Я пытался добавить адреса в ARP-таблицу с правильными интерфейсами, но это не помогло. Моё предположение состоит в том, что поскольку ядро может видеть адрес назначения на другом интерфейсе, пакеты никогда на самом деле не отправляются на сетевую карту, чтобы выйти на провод. Есть ли простой способ заставить пакеты действительно выходить на провод, не пролаживая 500 препятствий и не возясь с iptables, NAT и т.д.?

Чтобы ответить на свой собственный вопрос: окончательное решение – использовать сетевые пространства имен. Сетевые пространства имен специально предназначены для изоляции сетевого трафика, сетевой трафик может быть маршрутизирован только между компонентами в одном пространстве имен. Таким образом, чтобы запустить что-то вроде ping между двумя сетевыми портами на одной машине, по крайней мере один из этих портов должен быть перемещен в свое собственное сетевое пространство имен, а затем утилиты пользовательского пространства, такие как ping, iperf и т.д., должны запускаться в соответствующем пространстве имен.

Я создал скрипт для автоматизации настройки, отключения и выполнения действий внутри сетевых пространств имен для каждого интерфейса: https://github.com/alexforencich/verilog-ethernet/blob/master/scripts/dev-netns-shell.sh

С помощью этого скрипта вы можете легко открыть оболочку на каждом интерфейсе, при необходимости назначить IP-адреса, а затем запустить ping, iperf и т.д., и весь трафик будет маршрутизироваться через разные сетевые карты вместо того, чтобы обрабатываться внутри стека сетевой подсистемы Linux.

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

Для решения проблемы с отправкой пакетов с использованием утилиты ping в среде сетевого стека Linux, когда вы работаете с FPGA-устройством для захвата и таймстампинга пакетов, необходимо учитывать, что стандартный сетевой стек Linux не может корректно обрабатывать трафик между несколькими интерфейсами, если они находятся в одном пространстве имен (namespace). Ваша ситуация предполагает, что вы хотите отправить эхо-запросы (ping) через одну сетевую карту в другую, и в таком случае ядро Linux может не отправить пакеты на сетевую карту, поскольку оно уже видит адрес назначения в своей маршрутизационной таблице.

Проблема

Когда вы выполняете команду ping -I enp4s0 192.168.99.2, происходит следующее:

  1. Утилита ping отправляет ICMP-запрос на указанный IP-адрес.
  2. Даже если вы указываете явно, какой интерфейс использовать, если оба интерфейса находятся в одном пространстве имен, Linux может решить, что отправка пакетов не требуется, так как он может перенаправить трафик локально, не проходя через физическую сетевую карту.

Решение с использованием пространств имен

Наиболее эффективным способом обойти данную проблему является использование сетевых пространств имен (network namespaces). Это изолированные контексты, в которых можно настроить сетевые интерфейсы так, чтобы трафик между ними обрабатывался по отдельности. Таким образом, вы сможете запустить несколько процессов с их собственными сетевыми конфигурациями.

Пример сценария

  1. Создайте пространство имен:

    ip netns add test_ns
  2. Переместите интерфейс в пространство имен:

    ip link set enp5s0 netns test_ns
  3. Настройте интерфейс:

    ip netns exec test_ns ip addr add 192.168.99.2/24 dev enp5s0
    ip netns exec test_ns ip link set enp5s0 up
  4. Пинг из пространства имен:
    Выполните команду ping из конкретного пространства имен:

    ip netns exec test_ns ping -I enp4s0 192.168.99.1

Скрипт для автоматизации

Чтобы упростить этот процесс, можно использовать скрипт для автоматизации настроек, как вы упомянули. Например, скрипт для работы с пространствами имен может включать в себя функции, которые создают пространство имен, перемещают сети и запускают команды в изолированном контексте. Примерный скрипт может выглядеть следующим образом:

#!/bin/bash
# Создаем пространство имен
ip netns add my_namespace

# Перемещаем интерфейс
ip link set enp5s0 netns my_namespace

# Настраиваем интерфейс в пространстве имен
ip netns exec my_namespace ip addr add 192.168.99.2/24 dev enp5s0
ip netns exec my_namespace ip link set enp5s0 up

# Запускаем ping
ip netns exec my_namespace ping -I enp4s0 192.168.99.1

Заключение

Использование сетевых пространств имен является элегантным решением для ваших проблем с тестированием пакетов через FPGA. Это позволяет избежать сложных манипуляций с iptables и NAT, и во многом упрощает отладку сетевых приложений. Ваше решение, основанное на сетевых пространствах имен, значительно повысит гибкость ваших тестов с использованием инструмента ping и других утилит, позволяя полностью контролировать маршрутизацию пакетов через исследования и пакеты захвата.

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

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