O bootpc e o bootpd atuais do Debian (do bootp ) não parecem realmente funcionar juntos no ambiente do kernel Linux de hoje. Parece haver um problema 🐔/🥚; bootpd
envia as respostas como pacotes UDP unicast para o endereço IP ainda não configurado. O kernel do cliente então os descarta sem entregar ao bootpc
soquete do cliente, porque esse endereço IP não é (ainda) um endereço local válido no host.
Como isso funcionou?
- Existe algum parâmetro do kernel ou outra modificação que pode fazer com que o kernel envie esses pacotes ao
bootpc
processo? - Existe uma configuração
bootpd
que fará com que ele use todos os endereços IP de destino 1 ou 0 em vez do endereço IP unicast do cliente ainda não configurado?
Temos um cliente em potencial com uma grande infraestrutura bootp não-DHCP. Suporte bootp old-school é um dos requisitos deles.
Detalhes do problema
bootpd
envia seus pacotes de resposta como pacotes unicast para o endereço MAC do cliente bootpc, usando um valor de endereço IP de destino do endereço que também está na carga útil, informando ao cliente para se configurar.- O kernel então descarta esses pacotes, em vez de entregá-los ao
bootpc
processo que os solicitou. bootpc
abriu corretamente um soquete de escuta para0.0.0.0:68 0.0.0.0:*
(veja a saída netstat abaixo)- Verifiquei essa análise de várias maneiras:
- Eu corri
tcpdump
e posso ver as respostas chegando ao NIC - Eu executei
dropwatch
e vi os pacotes sendo descartados com o motivoIPINADDRERROR
, que basicamente significa "endereço IP inválido" - Posso enganar
bootpd
para usar 0.0.0.0 como o endereço IP de destino omitindo uma atribuição de IP real; quando faço isso,bootpc
obtenho as respostas e as processa. No entanto, isso não ajuda porque o cliente não obtém um endereço IP - Eu tentei adicionar o endereço IP à interface enquanto
bootpc
ela faz requisições. Depois que eu adiciono, a próxima resposta chega ao processo.
- Eu corri
- Eu também tentei a
bootpc --serverbcast
opção. Isso falha por um motivo similar:bootpd
envia as respostas para o endereço de transmissão da sub-rede (por exemplo, 10.0.43.255)- Como o endereço IP e a máscara de sub-rede ainda não estão configurados em uma interface, o kernel não tem motivos para considerar isso um endereço válido para si mesmo.
- Aqui está nossa
bootptab
configuração atual:
.vs-default:\
:sm=255.255.254.0:\
:gw=10.0.42.1:\
:ds=10.0.42.1:\
:hn:
client-ad02-vs:\
ht=1:\
ha=0xea4a1fad0002:\
ip=10.0.42.31:\
tc=.vs-default:
Saída do Netstat mostrando bootpc's listening socket
:
$ sudo netstat -unlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
udp 0 0 0.0.0.0:68 0.0.0.0:* 2295/bootpc
Solução alternativa para o Hacky
Eu inventei uma solução alternativa que parece hackeada. Em particular, acho que ela aproveita um comportamento não intencional, talvez não possamos contar com ela para funcionar no futuro:
- o módulo iptables conntrack tem uma correspondência
ctstate
com um valor deDNAT
. - Isso pode ser usado para aceitar pacotes que tenham um endereço IP de destino não reconhecido
- Não creio que a intenção seja corresponder a IPs de horário de verão arbitrários:
- Ele só pode ser usado com pacotes que foram NAT'd pelo kernel do mesmo host
- Acredito que seu propósito seja corresponder a pacotes que tiveram seus endereços reescritos (o que não estamos fazendo), mas é uma implementação "barata" que ignora a tabela de mapeamento NAT em vez de verificar se há uma entrada que corresponde a esse pacote específico.
- Estou preocupado que isso seja removido ou reescrito para ser menos promíscuo no futuro
- Para que isso funcione, preciso mapear a porta desses pacotes para a porta DST existente, apenas para obter dados de estado da conexão NAT no pacote. Caso contrário,
--ctstate DNAT
não se aplica ao pacote.
Histórico/Due Diligence
- Nosso ambiente de testes usa o Debian 12 no Linux 6.1.0.
- Nossos hosts de teste são de imagens de VM prontas para uso do UTM Debian 12
bootpd
tem muito poucas opções de cli, nenhuma das quais envolve respostas, portas ou endereços. A página do manual não menciona "broadcast" ou "unicast", e referências a "address" ou "destination" são poucas e não relevantes.- Nosso produto atual é o Debian 12 em um kernel Linux 5.19.9 personalizado (e se comporta da mesma maneira)
- A conectividade IP está funcionando totalmente entre o cliente e o servidor do ambiente de teste
bootpc
se recusa a enviar solicitações, a menos que o seguinte seja verdadeiro:- Não há nenhum endereço IP roteável na interface (169.254/16 é permitido, embora eu tenha tentado sem ele)
- Há uma rota padrão 0/0 apontando para a interface onde o servidor bootp é esperado
- Caso contrário, diz
network unreachable
- Nota: especificar
--dev <iface>
nabootpc
linha de comando não ajuda com isso
- Caso contrário, diz
- Ainda estou trabalhando na avaliação de
bootptab
opções de configuração para ver se há alguma que afete isso. Não encontrei uma boa referência para elas, abootptab
página man do man é muito concisa sobre elas.
Solução alternativa
Consegui fazer isso funcionar com uma iptables
solução menos complicada: adicionei uma regra NAT que corresponde à porta UDP 68 (bootpc) e mapeia o endereço IP de destino para 255.255.255.255 (e mantém a mesma porta UDP).
Isso funciona, mas considero isso hacky. Claramente não é assim que o protocolo deve funcionar, então eu preferiria uma solução "real", se possível.
O DHCP se comporta da mesma forma; a maioria dos clientes DHCP, e espero que também os clientes BOOTP, usam soquetes brutos para receber pacotes IP inteiros antes do processamento usual do kernel (bem como para enviar pacotes IP sem ainda ter nenhum endereço IP para enviá-los). O próprio software cliente constrói o cabeçalho IP e UDP completo ao enviar a descoberta ou solicitação e, em seguida, usa o BPF¹ para filtrar pacotes de entrada em vez do bind()-to-local-port usual.
(Entre outras coisas, isso também ignora a filtragem de entrada do iptables e as correções de soma de verificação UDP.)
O cliente deve ser reescrito para usar soquetes brutos (
AF_PACKET
,PF_LINK
, ou similar).Por exemplo, systemd-networkd usa
socket(AF_PACKET, SOCK_DGRAM, ...)
como nesta fonte . O cliente dhcpcd usaAF_LINK
em alguns sistemas,AF_PACKET
em outros, e da mesma forma filtra pacotes usando BPF¹ em vez de fazer o kernel fazer isso.Para simplificar as coisas, algumas partes podem ser manipuladas via libpcap.
¹ (Este é o BPF "clássico" – Berkeley Packet Filter – em vez do eBPF moderno, portanto não é específico do Linux e não requer nenhum recurso incomum.)
O servidor deve honrar um sinalizador "wants broadcast" na solicitação BOOTP ou DHCP.
bootpc
O cliente do Debian o define quando você passa--serverbcast
(porque ele aparentemente não usa sockets raw):Mas o
bootp
servidor só honra esse sinalizador quando compilado com aDHCP
opção que o Debian corrige nele:Se você estiver tentando integrar seu novo servidor Bootp em uma rede de clientes Bootp existentes, então o ISC
dhcpd
pode funcionar melhor para você, pois parece ter um modo de compatibilidade com Bootp. (Sim, aquele que agora é EOL, mas pelo menos é menos EOL do que o servidor do Debianbootp
– que parece ter sido tocado pela última vez em 1995).Da mesma forma, se você estiver tentando fazer seu sistema cliente funcionar com a infraestrutura Bootp existente, considere se você pode usar algum cliente DHCP no modo Bootp.