O iptables REDIRECT funciona apenas para o primeiro pacote

2

Preciso redirecionar todos os pacotes UDP com a porta de destino 15000 para a porta 15001 se o pacote contiver, por exemplo, a string test. Eu tenho estas duas regras simples:

iptables -t nat -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j LOG --log-prefix='[netfilter] '
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j REDIRECT --to-ports 15001

Os comportamentos estranhos:

  • se o primeiro pacote contiver teststring, o redirecionamento será feito para todos os pacotes da conexão;
  • se o primeiro pacote da conexão não contiver test, o redirecionamento nunca será feito, mesmo que um pacote subsequente contenhatest

No entanto, todos os pacotes correspondentes à regra são registrados corretamente.

Tentei adicionar também as informações da faixa à regra:

-m state --state NEW,ESTABLISHED

mas o comportamento é o mesmo. Algumas ideias?

Este é o iptablesconjunto de regras completo :

tabela de filtro:

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination  

tabela nat:

Chain PREROUTING (policy ACCEPT)
 target     prot opt source               destination         
 LOG        udp  --  anywhere             anywhere             udp dpt:15000 STRING match  "test" ALGO name bm TO 65535 LOG level warning prefix "[netfilter] "
 REDIRECT   udp  --  anywhere             anywhere             udp dpt:15000 STRING match  "test" ALGO name bm TO 65535 redir ports 15001

 Chain INPUT (policy ACCEPT)
 target     prot opt source               destination         

 Chain OUTPUT (policy ACCEPT)
 target     prot opt source               destination         

 Chain POSTROUTING (policy ACCEPT)

mesa mangle:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination  

tabela bruta:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
MirkoBanchi
fonte

Respostas:

3

As regras da tabela nat sempre funcionam apenas para o primeiro pacote em conexão. Pacotes subsequentes da mesma conexão nunca atravessam a lista de regras nat e são suportados apenas pelo código conntrack

Como o UDP é sem conexão por natureza, "conexão" aqui é definida simplesmente por endereços, portas e tempo limite. Portanto, se o segundo pacote UDP com a mesma porta e endereço de origem e a mesma porta e endereço de destino chegar dentro do tempo limite, o Linux acredita que pertence à "conexão" estabelecida e não avalia a tabela de regras nat, reutilizando o veredicto emitido para versões anteriores. pacote.

Veja aqui: http://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html

Nikita Kipriyanov
fonte
11
Parece que a única maneira de limitar o armazenamento em cache é ajustando o nf_conntrack_udp_timeoutparâmetro para 1 a sysctl. A única coisa ruim é que este é um parâmetro global e afeta todos "conexões" UDP em cache
MirkoBanchi
11
No módulo conntrack, há o parâmetro ctexpire, que o afaik foi projetado exatamente para fazer o ajuste por conexão. Veja iptables -m conntrack --help. Também há o pacote userland do conntrack-tools, usado para sincronizar firewalls nas configurações de cluster, mas não investiguei o que mais ele poderia fazer.
Nikita Kipriyanov 06/12/2015
3

Isso é causado pelo fato de o iptables aplicar o rastreamento de conexão na PREROUTINGcadeia. Sempre que uma nova conexão é estabelecida, o iptables consultará o cache do conntrack. Se uma correspondência foi encontrada, nenhuma regra será aplicada a partir da tabela nat.

Se você deseja desativar a alteração desse comportamento, observe o NOTRACKdestino na tabela bruta.

Observe que isso se aplica mesmo ao UDP (que é um protocolo sem conexão). O primeiro pacote é considerado como conexão de abertura NEWe o outro é uma resposta ESTABLISHED.

Encontrei uma postagem relacionada no serverfault .

Khaled
fonte
Eu suspeitava disso, mas adicionando ESTABLISHEDestado à regra, o iptables deve ignorar o cache? Se não, existe uma maneira de forçar isso?
MirkoBanchi
11
@MirkoBanchi: Veja o post que estou me referindo na minha resposta
Khaled
-alvo NOTRACKnão funciona para mim, conexão ainda é monitorado
MirkoBanchi
Talvez, para ser testado, para obter o melhor dos dois mundos, em vez de -j NOTRACK/ -j CT --notrack, o mais novo -j CT --zonepossa ser configurado (ainda na tabela bruta): para que os fluxos de pacotes ainda possam ser rastreados, nados etc., mas todos os que tiverem teste terão seus zona separada conntrack, assim esperamos colocar em seu próprio fluxo
AB
0

iptables com zonas conntrack

O recurso de zonas do conntrack permite que dois conntrack idênticos de 5 Uples (ou parte de) sejam considerados distintos por sua propriedade de zona . Normalmente, isso é provavelmente usado com roteamento complexo baseado em políticas, manipulando IPs idênticos, mas fluindo por diferentes caminhos (rotas) para impedir que o conntrack mescle fluxos não relacionados dos diferentes caminhos.

Aqui, ele pode ser usado para resolver esse problema: considere que os pacotes UDP não possuem teste o pacote testdeve fazer parte de duas zonas de origem diferentes (usando CT --zone-orig): a zona normal e a zona redirecionada. Cada zona de origem permitirá que as entradas do conntrack no NEWestado não colidam e sejam consideradas separadas. Considere como se houvesse duas tabelas nat no PREROUTINGgancho: em vez de avaliar as natregras que acontecem uma vez para o primeiro pacote do fluxo, isso pode acontecer uma vez para o primeiro pacote normal e uma vez para o primeiro testpacote.

Embora não seja realmente necessário, para evitar ainda mais duplicação de regras, definirei uma marca e a reutilizarei mais tarde para simplificar.

iptables -t raw -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j MARK --set-mark 1
iptables -t raw -A PREROUTING -m mark --mark 1 -j CT --zone-orig 1   
iptables -t nat -A PREROUTING -m mark --mark 1 -j LOG --log-prefix='[netfilter] '
iptables -t nat -A PREROUTING -p udp -m mark --mark 1 -j REDIRECT --to-ports 15001

Teste (o servidor está no IP 10.0.3.66). Cronologicamente, as respostas digitadas após as consultas (resposta1 do termo1 e resposta2 do termo2):

serverterm1$ socat udp4-listen:15000,reuseaddr,fork -
query1normal
query3normal
answer1

serverterm2$ socat udp4-listen:15001,reuseaddr,fork -
query2test
query4test
answer2

client$ socat udp4:10.0.3.66:15000 -
query1normal
query2test
query3normal
query4test
answer1
answer2

serverterm3# conntrack -E -p udp --orig-port-dst 15000
    [NEW] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=15000 dport=33150
    [NEW] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 zone-orig=1 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=15001 dport=33150
 [UPDATE] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 src=10.0.3.66 dst=10.0.3.1 sport=15000 dport=33150
 [UPDATE] udp      17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 zone-orig=1 src=10.0.3.66 dst=10.0.3.1 sport=15001 dport=33150

Advertência: a stringcorrespondência é avaliada para cada pacote do fluxo. Considere, se possível, usar u32menos custo adicional.

AB
fonte
Levei 5 meses para lembrar que eu tinha essa idéia ...
AB