nftables: дублирование широковещательных пакетов между сегментами

Aug 18 2020

У нас есть модуль Debian Buster (nftables 0.9.0, ядро ​​4.19), подключенный к четырем различным сегментам сети. Три из этих сегментов являются домом для устройств, на которых работает Syncthing, которое выполняет собственное локальное обнаружение через широковещательную рассылку на UDP-порт 21027. Таким образом, все устройства не могут «видеть» друг друга, поскольку широковещательные рассылки не пересекают сегменты; Сам Buster Box не участвует в кластере синхронизации.

Хотя мы могли бы решить эту проблему, запустив серверы обнаружения или ретрансляции Syncthing на панели Buster, мы просили не использовать их (причины, связанные с конфигурацией и устройствами, которые перемещаются на другие сайты). Следовательно, мы смотрим на решение на основе nftables; Насколько я понимаю, это обычно не делается, но для того, чтобы это работало, мы должны:

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

Только три из подключенных сегментов участвуют с устройствами; все подсети замаскированы как / 24.

  • Сегмент A (eth0, 192.168.0.1) не должен пересылаться
  • Сегмент B (eth1, 192.168.1.1) должен быть перенаправлен только на сегмент A
  • Сегмент C (eth2, 192.168.2.1) должен быть перенаправлен как на A, так и на B

На данный момент наиболее близким к рабочему правилу для этого является (другие правила DNAT / MASQ и локальной фильтрации для краткости опущены):

table ip mangle {
    chain repeater {
        type filter hook prerouting priority -152; policy accept;
        ip protocol tcp return
        udp dport != 21027 return
        iifname "eth1" ip saddr 192.168.2.0/24 counter ip daddr set 192.168.1.255 return
        iifname "eth0" ip saddr 192.168.2.0/24 counter ip daddr set 192.168.0.255 return
        iifname "eth0" ip saddr 192.168.1.0/24 counter ip daddr set 192.168.0.255 return
        iifname "eth2" ip saddr 192.168.2.0/24 counter dup to 192.168.0.255 device "eth0" nftrace set 1
        iifname "eth2" ip saddr 192.168.2.0/24 counter dup to 192.168.1.255 device "eth1" nftrace set 1
        iifname "eth1" ip saddr 192.168.1.0/24 counter dup to 192.168.0.255 device "eth0" nftrace set 1
    }
}

Счетчики показывают, что правила выполняются, хотя без daddr setправил широковещательный адрес остается таким же, как в исходном сегменте. nft monitor traceпоказывает, что по крайней мере некоторые пакеты достигают предполагаемого интерфейса с правильным IP-адресом назначения, но затем попадают в ловушку ввода для самого блока и не видны другим устройствам в сегменте.

Достижим ли на практике желаемый результат, и если да, то по каким правилам?

Ответы

1 A.B Aug 21 2020 at 03:53

В этом случае по-прежнему можно использовать nftables в семействе netdev (а не в семействе ip ), поскольку необходим только вход (nftables по-прежнему не имеет выхода ). Поведение dupи fwdв входной крючок точно такой же , как и дц-mirred «S mirrorи redirect.

Я также обратился к незначительной детали: переписать адрес источника Ethernet на MAC-адрес нового исходящего интерфейса Ethernet, как это было бы сделано для действительно маршрутизируемого пакета, даже если он работает для вас без этого. Поэтому необходимо заранее знать MAC-адреса интерфейсов. Я поместил два требуемых ( eth0 и eth1 ) в определения переменных / макросов, которые должны быть отредактированы с правильными значениями.

define eth0mac = 02:0a:00:00:00:01
define eth1mac = 02:0b:00:00:00:01

table netdev statelessnat
delete table netdev statelessnat

table netdev statelessnat {
    chain b { type filter hook ingress device eth1 priority 0;
        pkttype broadcast ether type ip ip daddr 192.168.1.255 udp dport 21027 jump b-to-a
        
    }

    chain c { type filter hook ingress device eth2 priority 0;
        pkttype broadcast ether type ip ip daddr 192.168.2.255 udp dport 21027 counter jump c-to-b-a
    }

    chain b-to-a {
        ether saddr set $eth0mac ip daddr set 192.168.0.255 fwd to eth0 } chain c-to-b-a { ether saddr set $eth1mac ip daddr set 192.168.1.255 dup to eth1 goto b-to-a
    }
}
1 T2PS Aug 19 2020 at 21:44

Изменить: для тех, кто обнаружит это позже, принятый ответ от AB дает чисто nft-решение.

Благодаря предложению AB, теперь это работает с использованием правил tc, а не только правил nftables:

tc qdisc add dev eth2 ingress
tc filter add dev eth2 ingress \
    protocol ip u32 \
    match ip dst 192.168.2.255 \
    match ip protocol 17 0xff \
    match ip dport 21027 0xffff \
    action nat ingress 192.168.2.255/32 192.168.0.255 \
    pipe action mirred egress mirror dev eth0 \
    pipe action nat ingress 192.168.0.255/32 192.168.1.255 \
    pipe action mirred egress redirect dev eth1

tc qdisc add dev eth1 ingress
tc filter add dev eth1 ingress \
    protocol ip u32 \
    match ip dst 192.168.1.255 \
    match ip protocol 17 0xff \
    match ip dport 21027 0xffff \
    action nat ingress 192.168.1.255/32 192.168.0.255 \
    pipe action mirred egress redirect dev eth0

Насколько я понимаю, эти фильтры соответствуют входящим широковещательным пакетам для UDP-порта 21027, NAT их к широковещательному адресу для каждой из других предполагаемых подсетей ( ingressдля изменения целевого IP-адреса, а не исходного IP-адреса, который nat egressможет измениться), а затем дублировать / перенаправлять пакеты с NAT в очереди вывода других интерфейсов.

Если вы новичок в tc, это может быть не лучшим способом решения проблемы, но он работает с точки зрения передачи широковещательных объявлений по сегментам (и Syncthing с радостью обнаруживает новые узлы).