nftables : dupliquer les paquets de diffusion entre les segments
Nous avons une boîte Debian Buster (nftables 0.9.0, noyau 4.19) attachée à quatre segments de réseau différents. Trois de ces segments hébergent des appareils exécutant Syncthing, qui exécute sa propre découverte locale via des diffusions vers le port UDP 21027. Les appareils ne peuvent donc pas tous se "voir" car les diffusions ne traversent pas les segments ; la boîte Buster elle-même ne participe pas au cluster de synchronisation.
Bien que nous puissions résoudre ce problème en exécutant les serveurs de découverte ou de relais de Syncthing sur la boîte Buster, il nous a été demandé de ne pas les utiliser (raisons liées à la configuration et aux appareils qui se déplacent vers d'autres sites). Par conséquent, nous envisageons une solution basée sur nftables ; je crois comprendre que cela ne se fait pas normalement, mais pour que cela fonctionne, nous devons :
- Faire correspondre les paquets entrants sur UDP 21027
- Copiez ces paquets sur les autres interfaces de segment sur lesquelles ils doivent être vus
- Modifiez l'adresse IP de destination du ou des nouveaux paquets pour qu'elle corresponde à l'adresse de diffusion du nouveau segment (tout en préservant l'adresse IP source car le protocole de découverte peut en dépendre)
- Émettez les nouvelles émissions sans qu'elles ne soient à nouveau dupliquées
Seuls trois des segments attachés participent avec des appareils ; tous sont masqués sous-réseau comme /24.
- Le segment A (eth0, 192.168.0.1) ne doit pas être transféré
- Le segment B (eth1, 192.168.1.1) doit être transmis au segment A uniquement
- Le segment C (eth2, 192.168.2.1) doit être transmis à la fois à A et à B
Le plus proche que nous ayons d'une règle de travail pour cela jusqu'à présent est (autres règles de filtrage DNAT/MASQ et locales omises pour des raisons de brièveté) :
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
}
}
Les compteurs montrent que les règles sont atteintes, bien que sans les daddr set
règles, l'adresse de diffusion reste la même que sur le segment d'origine. nft monitor trace
montre au moins que certains paquets atteignent l'interface prévue avec l'adresse IP de destination correcte, mais atterrissent ensuite dans le crochet d'entrée de la boîte elle-même et ne sont pas vus par d'autres appareils sur le segment.
Le résultat que nous recherchons ici est-il réalisable dans la pratique, et si oui, avec quelles règles ?
Réponses
Il est toujours possible d'utiliser nftables dans la famille netdev (plutôt que dans la famille ip ) dans ce cas, car seule l' entrée est nécessaire (nftables n'a toujours pas de sortie disponible). Le comportement de dup
et fwd
dans le hook ingress est exactement le même que celui de tc - mirred et .mirror
redirect
J'ai également abordé un détail mineur : réécrivez l'adresse source Ethernet à l'adresse MAC de la nouvelle interface Ethernet sortante, comme cela aurait été fait pour un paquet véritablement routé, même si cela fonctionne pour vous sans cela. Les adresses MAC des interfaces doivent donc être connues au préalable. J'ai mis les deux requis ( eth0 et eth1 ) dans les définitions de variables/macro, qui doivent être modifiées avec les valeurs correctes.
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
}
}
Edit : pour tous ceux qui trouveront cela plus tard, la réponse acceptée d'AB donne une solution purement nft.
Grâce à la suggestion d'AB, cela fonctionne maintenant en utilisant tc plutôt que des règles purement 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
Ma compréhension de ces filtres est qu'ils correspondent aux paquets de diffusion entrants pour le port UDP 21027, les NAT à l'adresse de diffusion pour chacun des autres sous-réseaux prévus ( ingress
, pour changer l'adresse IP de destination plutôt que l'adresse IP source qui nat egress
modifierait), puis dupliquer/rediriger les paquets NAT vers les files d'attente de sortie des autres interfaces.
Étant un novice avec tc, ce n'est peut-être pas la meilleure façon de résoudre le problème, mais cela fonctionne en termes de diffusion des annonces à travers les segments (et Syncthing découvre avec plaisir de nouveaux nœuds).