nftables: дублирование широковещательных пакетов между сегментами
У нас есть модуль 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-адресом назначения, но затем попадают в ловушку ввода для самого блока и не видны другим устройствам в сегменте.
Достижим ли на практике желаемый результат, и если да, то по каким правилам?
Ответы
В этом случае по-прежнему можно использовать 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
}
}
Изменить: для тех, кто обнаружит это позже, принятый ответ от 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 с радостью обнаруживает новые узлы).