
Adoro automatizar o processo e escrever minhas próprias bicicletas para estudar este ou aquele material. Meu novo objetivo era um servidor DHCP que emitisse um endereço em pequenas redes para que fosse possível realizar a configuração inicial do equipamento.
Neste artigo, falarei um pouco sobre o protocolo DHCP e algumas sutilezas do bash.
Resultado final
Vamos começar do final, para que fique claro o que estamos lutando.
Demonstração de trabalho:

Repositório com script:
firemoon777 / bash-dhcp-serverProblema inicial
A configuração que eu preciso é feita desta maneira: nós nos conectamos diretamente via par trançado ao equipamento, emitimos um endereço temporário via DHCP e o configuramos com um script já criado. E assim dez a vinte vezes seguidas.
Para muitos, o conhecido isc-dhcp-server faz seu trabalho perfeitamente, mas, infelizmente, ele não notifica meu script que o endereço foi emitido; portanto, você precisa bloquear a execução até que o endereço seja emitido.
A solução, ao que parece, está na superfície: faça ping até ficar azul na cara, até que o equipamento responda:
while ! ping -c1 -W1 "$DHCP" | grep -q "time=" do echo "Waiting for $DHCP..." done
Mas essa decisão definitivamente não tem aventureiro.
Parte teórica
Obtendo um Endereço com um Único Servidor DHCP
O protocolo DHCP funciona em UDP nas portas 67 e 68. O servidor sempre funciona apenas em 67 e o cliente em 68. Como o cliente não possui um endereço (possui o endereço 0.0.0.0), os pacotes DHCP são transmitidos. I.e. o cliente sempre envia pacotes para o endereço 255.255.255.255:67 do endereço 0.0.0.0:68 e o servidor envia do endereço: 67 para o endereço 255.255.255.255:68.
O cliente
recebe o endereço em quatro pacotes (
DORA ):
- O cliente descobre onde está o servidor DHCP ( D iscover)
- O servidor responde e oferece seu endereço (oferta)
- O cliente solicita o endereço proposto de um servidor específico ( R equest)
- O servidor concorda e emite o endereço ( A ck)
Visualmente, o esquema pode ser representado da seguinte maneira:

Obtendo um endereço com vários servidores DHCP
Quando o cliente envia o Discover, todos os servidores que podem ouvir enviam sua Oferta ao cliente. Mas o cliente deve escolher um. A seleção do cliente é anunciada na mensagem Solicitar com a opção 54 (servidor DHCP), que contém o endereço IP do servidor DHCP preferido. Embora a solicitação também seja enviada a todos na rede, apenas o servidor DHCP cujo IP é especificado na opção 54 responderá.

Conteúdo do pacote DHCP
Um pacote DHCP consiste em duas partes: uma constante, 236 bytes de tamanho, e uma variável que carrega opções (opção DHCP).
Tabela com todos os campos do pacote DHCP da WikipediaO campo | Descrição do produto | Comprimento (em bytes) |
---|
op
| Tipo de mensagem. Por exemplo, ele pode aceitar valores: BOOTREQUEST (0x01, solicitação do cliente para o servidor) e BOOTREPLY (0x02, resposta do servidor para o cliente).
| 1
|
htype
| Tipo de endereço de hardware. Os valores válidos para este campo são definidos nos Números atribuídos da RFC 1700. Por exemplo, para um endereço MAC Ethernet, este campo é definido como 0x01.
| 1
|
hlen
| O comprimento do endereço do hardware em bytes. O endereço MAC Ethernet é 0x06.
| 1
|
lúpulo
| O número de roteadores intermediários (os chamados agentes de retransmissão DHCP ) pelos quais a mensagem passou. O cliente define esse campo para 0x00.
| 1
|
xid
| Um identificador de transação exclusivo de 4 bytes gerado pelo cliente no início do processo de obtenção do endereço.
| 4
|
segundos
| O tempo em segundos desde o início do processo de obtenção do endereço. Não pode ser usado (neste caso, é definido como 0x0000).
| 2
|
bandeiras
| O campo para sinalizadores são parâmetros especiais do protocolo DHCP.
| 2
|
ciaddr
| Endereço IP do cliente. Ele é preenchido apenas se o cliente já tiver seu próprio endereço IP e puder responder a solicitações de ARP (isso é possível se o cliente executar o procedimento para atualizar o endereço após o término da concessão).
| 4
|
yiaddr
| O novo endereço IP do cliente proposto pelo servidor.
| 4
|
siaddr
| Endereço IP do servidor. Retornado na cláusula DHCP (veja abaixo).
| 4
|
giaddr
| O endereço IP do agente de retransmissão, se houver algum envolvido no processo de entrega da mensagem DHCP ao servidor.
| 4
|
chaddr
| O endereço de hardware (geralmente o endereço MAC) do cliente.
| 16
|
roncar
| Nome opcional do servidor como uma sequência terminada nula.
| 64
|
arquivo
| Um nome de arquivo do servidor opcional usado por estações de trabalho sem disco ao fazer o download remotamente. Como sname , ele é representado como uma sequência terminada em nulo.
| 128
|
opções
| Campo de opções DHCP . Várias opções de configuração adicionais são indicadas aqui. No início desse campo, quatro bytes especiais com os valores 99, 130, 83, 99 ("números mágicos") são indicados, permitindo que o servidor determine a presença desse campo. O campo tem um comprimento variável, mas o cliente DHCP deve estar pronto para receber uma mensagem DHCP de 576 bytes (nesta mensagem, o campo de opções tem 340 bytes).
| variável
|
Lista de todas as opções de DHCP no RFC 2132As opções de DHCP são codificadas da seguinte maneira:
Por exemplo, parâmetro 3 (gateway proposto) com um valor de 10.0.0.1:
Caso você precise passar vários parâmetros, o comprimento do parâmetro aumenta.
Por exemplo, no parâmetro 6 (servidor DNS), transmitiremos dois endereços (1.1.1.1 e 8.8.4.4):
Um sinal do final do campo de opção é um parâmetro com o número 255 (0xFF) e um comprimento 0.
Na maioria das vezes, o cliente coloca o parâmetro 55 (uma lista de parâmetros que ele deseja receber em resposta) no DHCP Discover, no entanto, temos o direito de não fornecer tudo o que ele solicitou.
Parte prática
Foi originalmente planejado escrever o servidor em uma linguagem mais adequada (C) para isso, no entanto, seria mundano e simples. É uma questão de escrever um script que assuma as funções do servidor dhcp.
Simplificação
Como o servidor em desenvolvimento deveria ser usado em redes de dois nós conectados por um patch, foram adotadas as seguintes simplificações:
- garantido que um cliente na rede;
- é garantido que não há mais servidores DHCP na rede
- o iniciador decide qual endereço emitir
- A liberação do DHCP e o declínio do DHCP são ignorados
Ouvinte
Primeiro de tudo, você precisa aprender como receber pacotes. Isso requer um ouvinte
compreensivo certificado , por exemplo, nc. Mas nem todo NC é adequado para esses fins. O OpenBSD netcat 1.130 do Debian é adequado, mas o 1.105 com Ubuntu se foi. Execute nc para ouvir todos os pacotes UDP que chegam na porta 67.
nc -l 0.0.0.0 -up 67 -w0
O OpenBSD netcat também é necessário devido à opção -w com um valor 0. Após receber um pacote (UDP Broadcast), o nc tradicional não recebe mais pacotes, mas não termina.
Manipulação de bytes brutos
No shell, é muito difícil trabalhar com caracteres não imprimíveis, como um caractere nulo: ele simplesmente o ignora. Um pacote DHCP contém muitos bytes 0x00 (por exemplo, o campo de arquivo). A solução para o problema vem na forma de hex-dump:
nc -l 0.0.0.0 -up 67 -w0 | stdbuf -o0 od -v -w1 -t x1 -An
Um byte por linha, sem saída do endereço, sem pular bytes duplicados. Você também pode incrementar stdbuf -o0 para que a saída não seja armazenada em buffer.
Pacotes de recebimento, armazenamento e processamento
No comando od stdout, os bytes são obtidos pelo comando read e adicionados a uma matriz.
msg=() for i in {0..235}; do read -r tmp msg[$i]=$tmp done
Embora todos os valores sejam transmitidos em notação hexadecimal, o número da opção DHCP e o comprimento da opção são exibidos melhor na tela / nos logs na forma decimal usual. Para fazer isso, você pode usar uma entrada curta bash'a:
$ op=AC $ echo $((16
O pacote recebido é editado de acordo com o tipo de solicitação (Discover ou Request) e enviado de volta.
Resposta
No entanto, enviar um pacote não é uma tarefa tão fácil. Primeiro você precisa converter bytes do despejo em bytes brutos e enviar tudo de uma só vez com um pacote.
A conversão pode ser feita com o utilitário printf usando seqüências de escape. E para que nada se perca, escreva bytes imediatamente em um arquivo.
O OpenBSD netcat também é usado para o envio. No entanto, se a versão 1.105 com Ubuntu é adequada como ouvinte, não é adequada para a transmissão de mensagens UDP: obtemos o erro de protocolo não disponível.
cat /tmp/dhcp.payload | nc -ub 255.255.255.255 68 -s $SERVER -p 67 -w0
A opção -b permite que mensagens de broadcast sejam enviadas e esse é o segundo motivo pelo qual o servidor deve ser executado sob o superusuário.
Quais são as limitações?
Este servidor DHCP foi projetado com simplificações como um único cliente na rede. No entanto, ele funcionará com vários clientes. Basta obter o endereço mais rápido.

Conclusão
Embora os scripts bash dificilmente possam ser chamados de linguagem de programação completa, no entanto, com o devido desejo, você pode até resolver problemas como emitir um endereço IP na rede sem usar software especialmente projetado para isso. E resolver problemas específicos não apenas traz alegria, mas também novos conhecimentos que surgiram no momento da solução.
Fontes
- DHCP - Wikipedia
- Parâmetros DHCP e BOOTP - IANA