1,662 24/03/2026 07/04/2026 8 min

In un firewall Linux con due uplink, il problema non è quasi mai il blocco totale. Più spesso è un degrado silenzioso.

Il caso tipico è questo: il link primario cade, il router passa sul backup, nftables continua a filtrare correttamente, ma alcune richieste HTTPS si impantanano. DNS sembra vivo. Ping funziona. Il sito, però, carica a metà o va in timeout.

Il colpevole è spesso una combinazione di routing, MTU e resolver DNS non aggiornati. Se il failover cambia interfaccia, ma il path MTU discovery viene spezzato dal firewall, appare il classico sintomo Packet too big oppure, lato client, pagine che non finiscono di caricarsi.

Qui vediamo un controllo ripetibile e automatico. L’obiettivo è semplice: dopo ogni failover o ogni mattina, verificare che il firewall stia inoltrando bene, che la MTU sia coerente e che il DNS risponda dal percorso giusto. Usiamo nftables, ip route, tracepath e un systemd timer con logging chiaro.

Prerequisiti

Serve un host Linux che faccia da gateway o firewall. Gli esempi assumono due WAN:

  • wan0 come linea primaria.
  • wan1 come backup.
  • Una LAN interna, ad esempio lan0.

Servono anche questi strumenti:

  • nft per leggere le regole.
  • ip per route e policy routing.
  • tracepath o ping con DF per test MTU.
  • resolvectl o dig per la verifica DNS.
  • systemd per timer e service.

Note: se il nodo non usa systemd-resolved, adatta il test DNS a dig @resolver o a un controllo verso il resolver configurato in /etc/resolv.conf.

Step 1: definire il sintomo reale prima di toccare il firewall

Il primo errore è inseguire il firewall senza osservare il guasto. Qui cerchiamo tre segnali insieme: routing, MTU e DNS.

Questo blocco non cambia nulla. Serve a fotografare lo stato quando il failover è già avvenuto.

ip route show default
ip rule show
nft list ruleset | sed -n '1,160p'
tracepath -n 1.1.1.1
resolvectl query example.com

# Output:

Atteso: una default route attiva sulla WAN corretta, nessun salto anomalo nelle policy rule, e tracepath che non si ferma su MTU incoerenti.

Se tracepath mostra una riga come pmtu 1500 su una linea che dovrebbe stare a 1492 o 1472, il path non coincide con la realtà.

Warning: se il router fa NAT e conntrack, non limitarti a guardare il ping. Il ping può passare anche quando MSS e frammentazione stanno rompendo HTTPS.

Step 2: separare il controllo di routing dal controllo DNS

Un failover pulito cambia route, ma non deve cambiare DNS casualmente. Il timer deve verificare entrambe le cose in modo distinto.

Usiamo una mini-checklist shell. Il punto non è essere eleganti. Il punto è avere log leggibili.

#!/usr/bin/env bash
set -euo pipefail

GW=$(ip route show default | awk 'NR==1 {print $3}')
DEV=$(ip route show default | awk 'NR==1 {print $5}')
DNS_OK=0
ROUTE_OK=0
MTU_OK=0

if ip route get 1.1.1.1 | grep -q "dev ${DEV}"; then
  ROUTE_OK=1
fi

if resolvectl query example.com >/dev/null 2>&1; then
  DNS_OK=1
fi

if tracepath -n 1.1.1.1 2>/dev/null | grep -Eq 'pmtu [0-9]+'; then
  MTU_OK=1
fi

echo "route=${ROUTE_OK} dns=${DNS_OK} mtu=${MTU_OK} gw=${GW} dev=${DEV}"
exit $(( ROUTE_OK && DNS_OK && MTU_OK ? 0 : 1 ))

# Output:

Atteso: una riga tipo route=1 dns=1 mtu=1 gw=203.0.113.1 dev=wan1.

Se il comando termina con exit 1, il timer deve segnalarlo come anomalia, non come semplice warning.

Step 3: rendere nftables compatibile con il path MTU

Il problema più comune è bloccare gli ICMP necessari al path MTU discovery. Il firewall sembra sicuro. In realtà tronca sessioni TCP grandi.

Nel caso reale conviene consentire i messaggi ICMP essenziali in ingresso e inoltro, ma solo quelli utili al PMTU.

table inet filter {
  chain input {
    type filter hook input priority 0;
    policy drop;

    ct state established,related accept
    iif "lo" accept

    ip protocol icmp icmp type destination-unreachable accept
    ip protocol icmp icmp type time-exceeded accept
    ip protocol icmp icmp type parameter-problem accept

    ip6 nexthdr icmpv6 icmpv6 type packet-too-big accept
    ip6 nexthdr icmpv6 icmpv6 type destination-unreachable accept
  }

  chain forward {
    type filter hook forward priority 0;
    policy drop;

    ct state established,related accept
    ip protocol icmp icmp type destination-unreachable accept
    ip protocol icmp icmp type time-exceeded accept
    ip protocol icmp icmp type parameter-problem accept
  }
}

# Output:

Atteso: il traffico di ritorno e i messaggi ICMP legati a MTU e diagnostica passano senza aprire tutto il resto.

Note: su IPv6 il messaggio packet-too-big non è opzionale. Bloccarlo significa rompere molte connessioni in modo intermittente.

Step 4: verificare che la route scelta corrisponda alla WAN attiva

Con doppia WAN, il routing può restare puntato sulla linea morta anche quando il link fisico sembra su.

Qui il controllo deve guardare il percorso effettivo, non solo l’interfaccia up.

ip route get 8.8.8.8
ip route get 1.1.1.1 from 192.0.2.10
ping -c 1 -M do -s 1472 1.1.1.1

# Output:

Atteso: ip route get mostra la WAN attiva. Il ping con -M do deve riuscire solo se la MTU consente quel payload.

Se il ping fallisce con frammentazione, il valore non è un bug del comando. È un segnale utile per capire dove si rompe il percorso.

Step 5: automatizzare la verifica con un systemd service e un timer

Il controllo manuale funziona una volta. In produzione serve un controllo ripetuto, con uscita chiara nei log.

Separiamo script, service e timer. Così il test si può lanciare anche a mano, ma resta schedulato in automatico.

# /etc/systemd/system/net-path-check.service
[Unit]
Description=Check routing, MTU and DNS on firewall uplink

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/net-path-check.sh
# /etc/systemd/system/net-path-check.timer
[Unit]
Description=Run network path verification every 10 minutes

[Timer]
OnBootSec=2m
OnUnitActiveSec=10m
AccuracySec=30s
Persistent=true

[Install]
WantedBy=timers.target
systemctl daemon-reload
systemctl enable --now net-path-check.timer
systemctl list-timers net-path-check.timer
journalctl -u net-path-check.service -n 20 --no-pager

# Output:

Atteso: il timer risulta attivo e il journal mostra una riga con route=1 dns=1 mtu=1.

Warning: se lo script gira ogni dieci minuti ma non salva il contesto, i falsi positivi diventano inutili. Il log deve includere interfaccia, gateway e risultato di ogni check.

Step 6: aggiungere una verifica di regressione dopo il failover

Il timer non deve limitarsi a dire “tutto ok”. Deve anche intercettare il cambio di linea. Quando la WAN cambia, il test deve essere più severo per alcuni minuti.

Un approccio pratico è confrontare il gateway attuale con l’ultimo gateway salvato su disco.

STATE=/run/net-path-check.last
NOW=$(ip route show default | awk 'NR==1 {print $3" "$5}')
OLD=$(cat "$STATE" 2>/dev/null || true)

if [ "$NOW" != "$OLD" ]; then
  echo "$NOW" > "$STATE"
  logger -t net-path-check "gateway changed: old='${OLD}' new='${NOW}'"
  tracepath -n 1.1.1.1
fi

# Output:

Atteso: al cambio di WAN compare un messaggio syslog con il vecchio e il nuovo gateway. Subito dopo parte un test aggiuntivo di path MTU.

Questo aiuta a distinguere il guasto vero dal normale riassestamento del routing durante il failover.

Verifica finale

La verifica finale deve rispondere a tre domande precise.

  • Il traffico esce dalla WAN giusta?
  • Gli ICMP necessari al PMTU sono consentiti?
  • Il DNS risponde ancora dopo il cambio di percorso?

Esegui questo blocco quando il timer è attivo e il failover è già stato simulato o osservato.

systemctl status net-path-check.timer --no-pager
systemctl status net-path-check.service --no-pager
journalctl -u net-path-check.service --since "30 min ago" --no-pager
tracepath -n 9.9.9.9
resolvectl query trgtkls.it

# Output:

Atteso: nessun errore nel service, nessun timeout nel journal, e una traceroute coerente con la MTU attesa del collegamento.

Note: se il DNS funziona solo con IP diretti ma non con nomi, il problema non è il firewall in uscita. Può essere il resolver usato dal gateway o un upstream filtrato.

Troubleshooting

1) Packet too big nei log del kernel

Causa: l’ICMP necessario al PMTU discovery è bloccato o alterato da nftables.

Fix: abilita i messaggi ICMP/ICMPv6 essenziali e ricontrolla il path.

nft add rule inet filter forward ip protocol icmp icmp type destination-unreachable accept
nft add rule inet filter forward ip6 nexthdr icmpv6 icmpv6 type packet-too-big accept
tracepath -n 1.1.1.1

Se il messaggio sparisce ma le app restano lente, verifica anche l’MSS clamp sul NAT.

2) resolvectl: Failed to resolve example.com: Temporary failure in name resolution

Causa: il failover ha cambiato la route, ma il resolver non è raggiungibile o il DNS è legato alla vecchia WAN.

Fix: testa il resolver effettivo e forza un server noto per isolare il problema.

resolvectl status
resolvectl query example.com --legend=no
resolvectl dns wan1 1.1.1.1 9.9.9.9

Se usi una VPN o un DNS interno, controlla che la policy routing non stia mandando le query fuori dal tunnel.

3) RTNETLINK answers: Network is unreachable

Causa: la default route è sparita durante il failover, oppure la tabella di routing non riceve la route corretta.

Fix: reinserisci la route e verifica il gateway con ip route get.

ip route replace default via 203.0.113.1 dev wan1 metric 100
ip route get 8.8.8.8
systemctl restart net-path-check.service

Se il problema torna, il ripristino manuale non basta. Serve sistemare il trigger del failover, non solo la route finale.

Conclusione

Un firewall con doppia WAN non si considera sano solo perché le regole nftables sono corrette. Deve essere sano nel percorso reale, con MTU, DNS e routing allineati.

Il controllo automatico con systemd timer riduce i falsi allarmi e scopre i degradi silenziosi prima degli utenti.

Prossimo passo concreto: integra il journal del timer nel tuo monitoraggio, così un cambio di gateway o un errore Packet too big genera un alert immediato.