Tenho uma pergunta de 'entendimento', mas ela tem um caso do mundo real que eu simplifiquei. Considere esta rede de exemplo (com IPs simplificados): Da esquerda para a direita:
- Três hosts S1-S3 (IPs, executando três programas R1-R3 respectivamente; Um switch com 3 VLANS e uma porta trunk conectada à eth0 de um quarto host S4. Todos são Ubuntu 20.04 LTS, mas isso provavelmente é irrelevante.
- O S4 está executando o P1 que se vincula a três soquetes UDP, um em cada uma das três interfaces VLAN nas respectivas redes S1,2,3
- P1 Envia mensagens UDP unicast e multicast para várias portas em cada um desses sockets a taxas de 10-100Hz. Há uma
OUTPUT
cadeia netfilter controlando esses fluxos.
Cada uma das áreas vermelhas representa algo que pode estar 'inativo'. Em todos esses casos, exceto um, os datagramas UDP são imediatamente descartados:
- Interface virtual
V10
está com link inativo; processoR2
não está em execução; ouOUTPUT
encadeiaDROP
o fluxo - Em cada caso, os pacotes são descartados silenciosamente por S2 ou um erro é relatado de volta para P1 e o pacote descartado. Todos os outros mcast continuam a fluir. - No caso do S3, após a remoção do
OUTPUT
bloco, otx
buffer do soquetev12
rapidamente se enche com pacotes unicast destinados12.1
e isso bloqueia qualquer gravação futura de unicast e multicast.
Neste estado, um strace
on P1
pode se parecer com isto (aparado):
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:27 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = -1 EAGAIN (Resource temporarily unavailable)
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = 31674
15:38:30 sendto(9, ... {sa_family=AF_INET, sin_port=htons(2347), sin_addr=inet_addr("12.1")}, 16) = -1 EAGAIN (Resource temporarily unavailable)
E ss
mostra o problema:
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
UNCONN 0 148992 0.0.0.0:51047 0.0.0.0:* users:(("task",pid=381888,fd=9))
skmem:(r0,rb212992,t148992,tb131070,f0,w0,o0,bl0,d0)
Então temos 4 * ~30kb de gravações no buffer de socket (padrão 120kb) com o 4º reportando EAGAIN
quando o buffer está cheio. O buffer está enchendo repetidamente enquanto o SO está esperando por uma ARP
resposta S3
que nunca virá.
Duas perguntas depois de tudo isso:
- UDP não é confiável por natureza. Nossa aplicação fica muito feliz que pacotes sejam descartados de outra forma, então por que seriam
tx
enfileirados dessa forma enquanto o kernel tenta resolver ARP? (Considere o caso em que o S3 pode estar conectado apenas ocasionalmente, mas outros hostsv12
ainda podem ser alcançáveis.) - O buffer parece ser esvaziado (e então rapidamente preenchido com novas gravações) a cada 3 segundos. Um dos resultados é que qualquer multicast que entra na mesma
tx
fila sai em pequenas rajadas em vez de na taxa de envio constante. Claro que podemos abrir mais soquetes de envio, mas onde esse valor é definido e esse comportamento é ajustávelsysctl
?