nftables: дублировать UDP-пакеты для конкретного IP:порт назначения на (второй) IP:порт назначения

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

Я хотел бы отразить только очень ограниченный пакетный трафик, направленный на один IP и UDP-порт, на тот же IP, но другой порт. Я смотрел на https://superuser.com/questions/1593995/iptables-nftables-forward-udp-data-to-multiple-targets, но, похоже, что оператор дублирования nftables позволяет дублировать только на другой IP, а не на тот же IP с другим портом.

Например, трафик на 127.0.0.1 порт 123 должен дублироваться на 127.0.0.1 порт 456. Поскольку это единственное дублирование, у меня нет никаких проблем с тем, что оригинальный номер порта теряется. Теперь я задаюсь вопросом, возможно ли вообще такое дублирование, потому что 127.0.0.1 не является “сливом”/исходящим интерфейсом, а является конечным адресом дублируемого пакета. Может ли быть способ объединить это с DNAT?

Существует ли какой-то другой механизм, кроме привязки eBPF-пробы к сетевому устройству с входящим UDP-трафиком?

Это можно сделать с помощью nftables и семейства netdev с цепочкой ingress и оператором dup. Это требует использования метки, чтобы избежать бесконечного цикла. В зависимости от конкретного варианта использования, дублирование также, вероятно, можно сделать на egress (поскольку это на loopback интерфейсе, дублированный пакет egress появится как ingress), но это потребует ядра >= 5.17 для поддержки, в то время как ingress доступен уже долгое время.

Требуется ядро >= 4.10 (для поддержки статической модификации UDP с правильной контрольной суммой).

# nft -f - <<'EOF'
table netdev t_dup        # для идемпотентности
delete table netdev t_dup # для идемпотентности

table netdev t_dup {
    chain c_ingress {
        type filter hook ingress device "lo" priority filter; policy accept;
        iif lo udp dport 123 meta mark != 1 meta mark set 1 dup to lo udp dport set 456
    }
}
EOF
  • Кандидат-пакет проверяется на наличие метки и обрабатывается только в том случае, если метка отсутствует, начиная с задания метки: это предотвратит возникновение цикла позже.

  • Он дублируется. Метка, являясь частью дублированного sk_buff, также дублируется

    Дубликат фактически будет неизмененным пакетом: отправленным в то же место (lo) и также на порт 123

  • Оператор dup, в отличие от любой цели iptables, включая ее цель TEE, не является оператором rule, завершающим оператором. Правило продолжается с безстатической модификацией пакета: UDP-порт изменяется на 456

  • Дублированный пакет также прибывает в ingress, но так как он имеет метку, правило игнорирует его: цикл предотвращается

Дублированный порт можно протестировать с помощью socat:

socat -u udp4-recv:456,bind=127.0.0.1 -

Заметки:

  • Если никто не слушает на дублированном порту, будет отправлен ICMP-порт недоступен, но поскольку этот порт (456) не совпадает с портом, на который отправляет приложение (123), он будет проигнорирован стеком сети.

  • Сетевой фильтр ingress происходит после AF_PACKET, tcpdump не захватит измененный порт и не дублированный пакет.

Мне нужно было сделать это для пакетов netflow/IPFix. Я пробовал ваше решение, но оно не сработало для меня как есть.

Тем не менее, вдохновленный вашим и другим ответом, я в конечном итоге получил рабочий вариант. Протестировано на Debian 11 с ядром 5.10:

table ip mangle
delete table ip mangle

table ip mangle {
    chain prerouting {
        type filter hook prerouting priority mangle; policy accept;
        iifname "eth0" udp dport 6660 \
      dup to 127.0.0.1 device lo udp dport set 6666 notrack \
      dup to 127.0.0.1 device lo udp dport set 6667 notrack \
      dup to 127.0.0.1 device lo udp dport set 6668 notrack
    }

    chain input {
        type filter hook input priority mangle; policy accept;
        iifname lo udp dport 6666 ip daddr set 127.0.0.1 notrack
        iifname lo udp dport 6667 ip daddr set 127.0.0.1 notrack
        iifname lo udp dport 6668 ip daddr set 127.0.0.1 notrack
    }
}

Это дублирует все, что поступает на eth0 для UDP/6660, на порты UDP локального хоста 6666, 6667 и 6668 и устанавливает адрес назначения на 127.0.0.1. Последнее необходимо, потому что некоторые программы иначе с этим не справляются.

Я протестировал это с Fastnetmon, nfdump/nfsen и AS-Stats.

Для моего случая использования я хотел захватить и отразить трафик, генерируемый процессом на локальной машине. Таким образом, захват, используя фильтр на prerouting, input или ingress, не имеет смысла – Netfilter видит пакеты, сгенерированные локальным процессом, только в output, а затем в postrouting (см. эту хорошую графику в вики nftables).

Мое решение моим требованиям – это комбинация ответов @A.B и @Alfredo, где первое правило захватывает в postrouting, а второе правило переписывает IP-заголовки после того, как дублированный пакет попадает в вход lo.

Оригинальное использование заключается в захвате отчетов HEP (инкапсулированные захваты пакетов) от локальной службы, которые обычно отправляются в внешнее мониторинговое учреждение, и зеркалировании их на локальный процесс sngrep, который используется для отладки на машине – поэтому такие специфические названия и адреса портов. “17” просто потому, что мне нравится это число.

table ip t_sngrep
delete table ip t_sngrep
table ip t_sngrep {
  chain c_dup {
    type filter hook postrouting priority filter; policy accept;
    udp dport 9060 meta mark != 17 meta mark set 17 dup to 127.0.0.17 device "lo"
  }
  chain c_rewrite {
    type filter hook input priority filter; policy accept;
    # meta mark 17 meta nftrace set 1 # необязательная трассировка, используя nft monitor
    meta mark 17 udp dport set 8060 ip saddr set 127.0.0.1 ip daddr set 127.0.0.17
    # meta mark 17 log prefix "sgrep_filter " # необязательное для логирования результата
   }
  }'

Правило переписывания необходимо, потому что в противном случае вы увидите пакеты на lo от {some-real-ip} к {some-other-real-ip}, и sngrep педантично (и правильно) не будет обрабатывать эти пакеты – поэтому мы переписываем исходные и конечные адреса на что-то, что можно ожидать увидеть на lo, а также переписываем конечный HEP-порт – для верности и чтобы предотвратить путаницу.

sngrep затем можно настроить для разбора отраженного HEP-трафика с помощью

sngrep -EL udp:127.0.0.17:8060

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

Дублирование UDP-пакетов с помощью nftables: Полное Решение

Введение

Современные решения в области сетевых технологий требуют высокой гибкости в обработке трафика. Одной из таких задач является необходимость дублирования UDP-пакетов, направленных на определённый IP-адрес и порт, на другой порт, оставаясь при этом на том же IP. В данной статье мы рассмотрим, как достичь этой цели с помощью nftables и каких шагов следует придерживаться.

Задача

Наша цель — дублировать трафик, направленный на адрес 127.0.0.1 и порт 123, на тот же адрес 127.0.0.1, но на порт 456. Это может понадобиться для мониторинга или сбора статистики, когда требуется, чтобы копии пакетов отправлялись на другой порт для обработки.

Подход

Для решения данной задачи мы будем использовать механизм ingress и конструкцию dup в nftables, что позволяет делать дубликаты пакетов. Важно отметить, что необходимо установить маркер для предотвращения зацикливания.

Пример Конфигурации

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

# nft -f - <<EOF
table netdev t_dup {
    chain c_ingress {
        type filter hook ingress device "lo" priority filter; policy accept;
        iif lo udp dport 123 meta mark != 1 meta mark set 1 dup to lo udp dport set 456
    }
}
EOF

Объяснение Конфигурации

  1. Создание таблицы: Мы создаём таблицу netdev с названием t_dup, что обеспечивает изоляцию конфигурации.

  2. Цепочка ingress: В цепочке c_ingress происходит проверка входящего трафика, который связан с интерфейсом lo (loopback).

  3. Условие: Если пакет нацелен на порт 123 и не имеет установленного маркера (то есть не обрабатывался ранее), мы устанавливаем маркер и дублируем пакет на порт 456.

  4. Дублирование: Конструкция dup позволяет отправить дубликат пакета обратно на тот же интерфейс lo, но с изменённым портом.

Тестирование и Применение

Вы можете протестировать данную конфигурацию с помощью утилиты socat:

socat -u udp4-recv:456,bind=127.0.0.1 -

Этот пример позволит вам проследить за дублированными пакетами, направленными на порт 456.

Замечания

  • Проблемы с прослушиванием: Если на дублируемом порту (в нашем случае 456) нет приложения, то может быть выпущено ICMP сообщение о недостижимом порте. Однако это не повлияет на основное приложение, отправляющее пакеты на порт 123.
  • Учет версий ядра: Убедитесь, что версия ядра вашего Linux >= 4.10 для корректной обработки дублируемых UDP пакетов.

Выводы

Использование nftables для дублирования UDP-пакетов — это мощное средство, позволяющее тонко настраивать сетевую инфраструктуру. Применяя предложенное решение, вы сможете значительно улучшить свой мониторинг сетевого трафика. Убедитесь в правильности реализации и проведите тестирование, чтобы гарантировать функциональность в вашем конкретном окружении.

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

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