Servidor DHCP nativo usando bash

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-server

Problema 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 ):

  1. O cliente descobre onde está o servidor DHCP ( D iscover)
  2. O servidor responde e oferece seu endereço (oferta)
  3. O cliente solicita o endereço proposto de um servidor específico ( R equest)
  4. 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 Wikipedia
O campoDescrição do produtoComprimento (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 2132

As opções de DHCP são codificadas da seguinte maneira:
NúmeroComprimentoDados

Por exemplo, parâmetro 3 (gateway proposto) com um valor de 10.0.0.1:
34100 00 01

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):
6811118844

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#$op)) 172 

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.

 #   >/tmp/dhcp.payload #     for i in ${msg[*]}; do printf "\x$i" >> /tmp/dhcp.payload done 

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


  1. DHCP - Wikipedia
  2. Parâmetros DHCP e BOOTP - IANA

Source: https://habr.com/ru/post/pt435148/


All Articles