nftables: duplica i pacchetti broadcast tra i segmenti

Aug 18 2020

Abbiamo una scatola Debian Buster (nftables 0.9.0, kernel 4.19) collegata a quattro diversi segmenti di rete. Tre di questi segmenti ospitano dispositivi che eseguono Syncthing, che esegue il proprio rilevamento locale tramite trasmissioni alla porta UDP 21027. I dispositivi quindi non possono "vedersi" l'un l'altro poiché le trasmissioni non attraversano i segmenti; la stessa casella Buster non partecipa al cluster di sincronizzazione.

Mentre potremmo risolvere questo problema eseguendo i server di rilevamento o di inoltro di Syncthing sulla casella Buster, è stato richiesto di non utilizzarli (motivi relativi alla configurazione e ai dispositivi che effettuano il roaming su altri siti). Quindi, stiamo esaminando una soluzione basata su nftables; la mia comprensione è che questo non è normalmente fatto, ma per farlo funzionare, dobbiamo:

  • Abbina i pacchetti in entrata su UDP 21027
  • Copia quei pacchetti nelle altre interfacce di segmento su cui devono essere visti
  • Modificare l'IP di destinazione dei nuovi pacchetti in modo che corrisponda all'indirizzo di trasmissione del nuovo segmento (preservando l'IP di origine poiché il protocollo di rilevamento può fare affidamento su di esso)
  • Emetti le nuove trasmissioni senza che vengano nuovamente duplicate

Solo tre dei segmenti collegati partecipano con i dispositivi; tutti sono subnet mascherati come /24.

  • Il segmento A (eth0, 192.168.0.1) non deve essere inoltrato
  • Il segmento B (eth1, 192.168.1.1) deve essere inoltrato solo al segmento A
  • Il segmento C (eth2, 192.168.2.1) deve essere inoltrato sia ad A che a B

Il più vicino che abbiamo a una regola di lavoro per questo finora è (altri DNAT/MASQ e regole di filtraggio locali omessi per brevità):

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
    }
}

I contatori mostrano che le regole vengono raggiunte, anche se senza le daddr setregole l'indirizzo di trasmissione rimane lo stesso del segmento di origine. nft monitor tracemostra almeno che alcuni pacchetti stanno raggiungendo l'interfaccia prevista con l'IP di destinazione corretto, ma poi atterrano nell'hook di input per la scatola stessa e non sono visti da altri dispositivi sul segmento.

Il risultato che stiamo cercando qui è realizzabile nella pratica e, in caso affermativo, con quali regole?

Risposte

1 A.B Aug 21 2020 at 03:53

È ancora possibile utilizzare nftables nella famiglia netdev (piuttosto che nella famiglia ip ) per questo caso, poiché è necessario solo l' ingresso (nftables non ha ancora l' uscita disponibile). Il comportamento di dupe fwdnell'ingress hook è esattamente lo stesso di tc - mirred e .mirrorredirect

Ho anche affrontato un dettaglio minore: riscrivere l'indirizzo di origine Ethernet nell'indirizzo MAC della nuova interfaccia Ethernet in uscita, come sarebbe stato fatto per un pacchetto veramente instradato, anche se funziona per te senza questo. Quindi gli indirizzi MAC delle interfacce devono essere conosciuti in anticipo. Ho inserito i due richiesti ( eth0 e eth1 ) nelle definizioni di variabili/macro, che dovrebbero essere modificate con i valori corretti.

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

Modifica: per chiunque lo trovi in ​​seguito, la risposta accettata da AB fornisce una soluzione puramente nft.

Grazie al suggerimento di AB, ora funziona usando tc piuttosto che regole puramente 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

La mia comprensione di questi filtri è che abbinano i pacchetti di trasmissione in entrata per la porta UDP 21027, li NAT all'indirizzo di trasmissione per ciascuna delle altre sottoreti previste ( ingress, per modificare l'IP di destinazione anziché l'IP di origine che nat egressaltererebbe), quindi duplicare/reindirizzare i pacchetti NAT alle code di output delle altre interfacce.

Essendo un principiante con tc, questo potrebbe non essere il modo migliore per risolvere il problema, ma funziona in termini di far viaggiare le trasmissioni di annunci attraverso i segmenti (e Syncthing sta scoprendo felicemente nuovi nodi).