
O objetivo deste projeto foi:
- Aprendendo DHCP através de uma rede IPv4
- Aprendendo Python (um pouco mais que do zero;))
- substituindo o servidor DB2DHCP (meu fork), o original aqui , que é cada vez mais difícil de montar no novo sistema operacional. Sim, e eu não gosto que o binário, que não é possível "mudar agora"
- obtenção de um servidor DHCP em funcionamento com a capacidade de selecionar o endereço IP do assinante pelo mac do assinante ou um monte de comutadores mac + porta (opção 82)
- escrevendo outra bicicleta (Oh! esse é meu passatempo favorito)
- recebendo Lyuli sobre seus estrabismo em Habrahabr (ou melhor, convite);)
Resultado: funciona;) Testado no FreeBSD e Ubuntu OS. Teoricamente, pode ser solicitado que o código funcione em qualquer sistema operacional, porque não há ligações específicas no código.
Cuidado Muito mais longe.
Link para o repositório para que os fãs
"toquem vivos" .
O processo de instalação, configuração e uso do resultado do "estudo do hardware" é muito mais baixo e, em seguida, um pouco de teoria sobre o protocolo DHCP. Para mim. E para a história;)
Um pouco de teoria
O que é DHCP?
Este é um protocolo de rede que permite que um dispositivo descubra seu endereço IP (bem, outros parâmetros como um gateway, DNS etc.) de um servidor DHCP. Pacotes são trocados por UDP. O princípio geral de operação do dispositivo ao solicitar parâmetros de rede é o seguinte:
- O dispositivo (cliente) envia uma solicitação de transmissão UDP (DHCPDISCOVER) pela rede com a solicitação "Bem, alguém, me dê um endereço IP". E geralmente (mas nem sempre) a solicitação é da porta 68 (origem) e o destino é a porta 67 (destino). Alguns dispositivos também enviam pacotes da porta 67. Dentro do pacote DHCPDISCOVER, o endereço MAC do dispositivo cliente está incluído.
- Todos os servidores DHCP localizados na rede (e podem existir vários) para o dispositivo que enviou a DHCPDISCOVER uma sentença DHCPOFFER com as configurações de rede e também a transmite pela rede. A identificação a quem este pacote se destina vai para o endereço MAC do cliente fornecido anteriormente na solicitação DHCPDISCOVER.
- O cliente recebe pacotes com ofertas de configurações de rede, seleciona os mais atraentes (os critérios podem ser diferentes, por exemplo, no momento da entrega dos pacotes, no número de rotas intermediárias) e faz com que o servidor DHCP “solicite oficialmente” o DHCPREQUEST com as configurações de rede. Nesse caso, o pacote vai para um servidor DHCP específico.
- O servidor que recebeu DHCPREQUEST envia um pacote DHCPACK no qual lista novamente as configurações de rede para este cliente

Além disso, existem pacotes DHCPINFORM provenientes do cliente, cuja finalidade é informar ao servidor DHCP que o "cliente está ativo" e usa as configurações de rede emitidas. Na implementação deste servidor, esses pacotes são ignorados.
Formato de pacote
Em geral, o quadro de pacotes Ethernet é mais ou menos assim:

No nosso caso, consideraremos apenas os dados diretamente do conteúdo do pacote UDP, sem os cabeçalhos de protocolo da camada OSI, a saber, a estrutura DHCP:
DHCPDISCOVER
Portanto, o processo de obtenção do endereço IP do dispositivo começa com o fato de o cliente DHCP enviar uma solicitação de transmissão da porta 68 para 255.255.255.255:67. Neste pacote, o cliente inclui seu endereço MAC, bem como o que deseja receber do servidor DHCP. A estrutura do pacote é descrita na tabela abaixo.
Tabela de estrutura de pacotes DHCPDISCOVERPosição do pacote | Nome do valor | Exemplo | Submissão | Byte | Explicação |
1 | Solicitação de inicialização | 1 | Hex | 1 | Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente |
2 | Tipo de hardware | 1 | Hex | 1 | Tipo de endereço de hardware, neste protocolo 1 - MAC |
3 | Hardware adrees comprimento | 6 | Hex | 1 | Comprimento do endereço MAC do dispositivo |
4 | Lúpulo | 1 | Hex | 1 | Número de rotas intermediárias |
5 | ID da transação | 23: cf: de: 1d | Hex | 4 | Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação |
7 | Segundo decorrido | 0 0 | Hex | 4 | Tempo em segundos desde o início do processo de obtenção do endereço |
9 | Sinalizadores de Bootp | 0 0 | Hex | 2 | Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo |
11 | Endereço IP do cliente | 0.0.0.0 | String | 4 | Endereço IP do cliente (se houver) |
15 | O endereço IP do seu cliente | 0.0.0.0 | String | 4 | Endereço IP proposto pelo servidor (se houver) |
19 | Endereço IP do próximo servidor | 0.0.0.0 | String | 4 | Endereço IP do servidor (se conhecido) |
23 | Endereço IP do agente de retransmissão | 172.16.114.41 | String | 4 | Endereço IP do agente de retransmissão (por exemplo, um comutador) |
27 | Endereço MAC do cliente | 14: d6: 4d: a7: c9: 55 | Hex | 6 | Endereço MAC do remetente do pacote (cliente) |
31 | Preenchimento de endereço de hardware do cliente | | Hex | 10 | Lugar reservado. Geralmente zeros |
41. | Nome do host do servidor | | String | 64 | O nome do servidor DHCP. Geralmente não transmitido |
105 | Nome do arquivo de inicialização | | String | 128 | O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização |
235 | Biscoito mágico | 63: 82: 53: 63 | Hex | 4 | O número "mágico", pelo qual incl. você pode determinar que este pacote pertence ao protocolo DHCP |
Opções de DHCP. Pode ir em qualquer ordem |
236 | Número da opção | 53 | Dez | 1 | Opção 53 que especifica o tipo de pacote DHCP
1 - DHCPDISCOVER 3 - DHCPREQUEST 2 - DHCPOFFER 5 - DHCPACK 8 - DHCPINFORM |
| Comprimento da opção | 1 | Dez | 1 |
| Valor da opção | 1 | Dez | 1 |
| Número da opção | 50. | Dez | 1 | Qual endereço IP o cliente deseja receber |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 172.16.134.61 | String | 4 |
| Número da opção | 55 | | 1 | Os parâmetros de rede solicitados pelo cliente. A composição pode ser diferente
01 - Máscara de rede 03 - Gateway 06 - DNS oc - nome do host 0f - nome de domínio da rede 1c - endereço da solicitação de transmissão (Broadcast) 42 - nome do servidor TFTP 79 - Rota estática sem classe |
| Comprimento da opção | 8 | | 1 |
| Valor da opção | 01: 03: 06: 0c: 0f: 1c: 42: 79 | | 8 |
| Número da opção | 82 | Dez | | Opção 82, na qual o endereço MAC do dispositivo repetidor e alguns valores adicionais são transmitidos.
Na maioria das vezes, a porta do switch na qual o cliente final do DHCP é executado. Opções adicionais são "incorporadas" nessa opção.O primeiro byte é o número da "subopção", o segundo é o comprimento e, em seguida, o valor.
Nesse caso, na opção 82, as subopções são aninhadas: ID do circuito do agente = 00: 04: 00: 01: 00: 04, em que os dois últimos bytes são a porta do cliente DHCP da qual a solicitação veio
ID remoto do agente = 00: 06: c8: be: 19: 93: 11: 48 - endereço MAC do dispositivo de retransmissão DHCP |
| Comprimento da opção | 18 | Dez | |
| Valor da opção | 01:06 00: 04: 00: 01: 00: 04 02:08 00: 06: c8: be: 19: 93: 11: 48 | Hex | |
| Fim do pacote | 255 | Dez | 1 | 255 simboliza o final da embalagem |
DHCPOFFER
Assim que o servidor recebe o pacote DHCPDISCOVER e se vê que ele pode oferecer algo ao cliente solicitado, então forma uma resposta para ele - DHCPOFFER. A resposta é enviada para a porta "de onde veio", Broadcast, porque neste momento, o cliente ainda não possui um endereço IP; portanto, ele pode receber um pacote apenas se for enviado para difusão. O cliente reconhece que é um pacote para ele pelo MAC em seu endereço dentro do pacote, bem como o número da transação que ele gera no momento da criação do primeiro pacote.
Tabela de estrutura de pacotes DHCPOFFERPosição do pacote | Nome do valor (comum) | Exemplo | Submissão | Byte | Explicação |
1 | Solicitação de inicialização | 1 | Hex | 1 | Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente |
2 | Tipo de hardware | 1 | Hex | 1 | Tipo de endereço de hardware, neste protocolo 1 - MAC |
3 | Hardware adrees comprimento | 6 | Hex | 1 | Comprimento do endereço MAC do dispositivo |
4 | Lúpulo | 1 | Hex | 1 | Número de rotas intermediárias |
5 | ID da transação | 23: cf: de: 1d | Hex | 4 | Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação |
7 | Segundo decorrido | 0 0 | Hex | 4 | Tempo em segundos desde o início do processo de obtenção do endereço |
9 | Sinalizadores de Bootp | 0 0 | Hex | 2 | Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo. Nesse caso, 0 significa o tipo de solicitação Unicast |
11 | Endereço IP do cliente | 0.0.0.0 | String | 4 | Endereço IP do cliente (se houver) |
15 | O endereço IP do seu cliente | 172.16.134.61 | String | 4 | Endereço IP proposto pelo servidor (se houver) |
19 | Endereço IP do próximo servidor | 0.0.0.0 | String | 4 | Endereço IP do servidor (se conhecido) |
23 | Endereço IP do agente de retransmissão | 172.16.114.41 | String | 4 | Endereço IP do agente de retransmissão (por exemplo, um comutador) |
27 | Endereço MAC do cliente | 14: d6: 4d: a7: c9: 55 | Hex | 6 | Endereço MAC do remetente do pacote (cliente) |
31 | Preenchimento de endereço de hardware do cliente | | Hex | 10 | Lugar reservado. Geralmente zeros |
41. | Nome do host do servidor | | String | 64 | O nome do servidor DHCP. Geralmente não transmitido |
105 | Nome do arquivo de inicialização | | String | 128 | O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização |
235 | Biscoito mágico | 63: 82: 53: 63 | Hex | 4 | O número "mágico", pelo qual incl. você pode determinar que este pacote pertence ao protocolo DHCP |
Opções de DHCP. Pode ir em qualquer ordem |
236 | Número da opção | 53 | Dez | 1 | Opção 53 que especifica o tipo de pacote DHCP 2 - DHCPOFFER |
| Comprimento da opção | 1 | Dez | 1 |
| Valor da opção | 2 | Dez | 1 |
| Número da opção | 1 | Dez | 1 | Opção que oferece máscara de rede do cliente DHCP |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 255.255.224.0 | String | 4 |
| Número da opção | 3 | Dez | 1 | Opção que oferece gateway padrão do cliente DHCP |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 172.16.12.1 | String | 4 |
| Número da opção | 6 | Dez | 1 | Opção que oferece DHCP ao cliente DNS |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 8.8.8.8 | String | 4 |
| Número da opção | 51 | Dez | 1 | A vida útil dos parâmetros de rede emitidos em segundos, após os quais o cliente DHCP deve solicitá-los novamente |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 86400 | Dez | 4 |
| Número da opção | 82 | Dez | 1 | A opção 82 repete o que veio no DHCPDISCOVER |
| Comprimento da opção | 18 | Dez | 1 |
| Valor da opção | 01: 08: 00: 06: 00 01: 01: 00: 00: 01 02: 06: 00: 03: 0f 26: 4d: ec | Dez | 18 |
| Fim do pacote | 255 | Dez | 1 | 255 simboliza o final da embalagem |
DHCPREQUEST
Depois que o cliente recebe o DHCPOFFER, ele forma um pacote com uma solicitação de parâmetros de rede, não para todos os servidores DHCP da rede, mas apenas para um específico, cuja proposta do DHCPOFFER mais "gostou". Os critérios para "curtir" podem ser diferentes e dependem da implementação do cliente DHCP. O destinatário da solicitação é indicado usando o endereço MAC do servidor DHCP. Além disso, o pacote DHCPREQUEST pode ser enviado pelo cliente sem formar o DHCPDISCOVER anteriormente, se o endereço IP do servidor tiver sido recebido anteriormente anteriormente.
Tabela de estrutura de pacotes DHCPREQUESTPosição do pacote | Nome do valor (comum) | Exemplo | Submissão | Byte | Explicação |
1 | Solicitação de inicialização | 1 | Hex | 1 | Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente |
2 | Tipo de hardware | 1 | Hex | 1 | Tipo de endereço de hardware, neste protocolo 1 - MAC |
3 | Hardware adrees comprimento | 6 | Hex | 1 | Comprimento do endereço MAC do dispositivo |
4 | Lúpulo | 1 | Hex | 1 | Número de rotas intermediárias |
5 | ID da transação | 23: cf: de: 1d | Hex | 4 | Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação |
7 | Segundo decorrido | 0 0 | Hex | 4 | Tempo em segundos desde o início do processo de obtenção do endereço |
9 | Sinalizadores de Bootp | 8000 | Hex | 2 | Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo. Nesse caso, "Broadcast" |
11 | Endereço IP do cliente | 0.0.0.0 | String | 4 | Endereço IP do cliente (se houver) |
15 | O endereço IP do seu cliente | 172.16.134.61 | String | 4 | Endereço IP proposto pelo servidor (se houver) |
19 | Endereço IP do próximo servidor | 0.0.0.0 | String | 4 | Endereço IP do servidor (se conhecido) |
23 | Endereço IP do agente de retransmissão | 172.16.114.41 | String | 4 | Endereço IP do agente de retransmissão (por exemplo, um comutador) |
27 | Endereço MAC do cliente | 14: d6: 4d: a7: c9: 55 | Hex | 6 | Endereço MAC do remetente do pacote (cliente) |
31 | Preenchimento de endereço de hardware do cliente | | Hex | 10 | Lugar reservado. Geralmente zeros |
41. | Nome do host do servidor | | String | 64 | O nome do servidor DHCP. Geralmente não transmitido |
105 | Nome do arquivo de inicialização | | String | 128 | O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização |
235 | Biscoito mágico | 63: 82: 53: 63 | Hex | 4 | O número "mágico", pelo qual incl. você pode determinar que este pacote pertence ao protocolo DHCP |
Opções de DHCP. Pode ir em qualquer ordem |
236 | Número da opção | 53 | Dez | 3 | Opção 53 que especifica o tipo de pacote DHCP 3 - DHCPREQUEST |
| Comprimento da opção | 1 | Dez | 1 |
| Valor da opção | 3 | Dez | 1 |
| Número da opção | 61 | Dez | 1 | ID do cliente: 01 (para Ehernet) + endereço MAC do cliente |
| Comprimento da opção | 7 | Dez | 1 |
| Valor da opção | 01: 2c: ab: 25: ff: 72: a6 | Hex | 7 |
| Número da opção | 60 | Dez | | "Identificador da classe do fornecedor." No meu caso, a versão do cliente DHCP é relatada. Talvez outros dispositivos retornem outra coisa. O Windows, por exemplo, relata o MSFT 5.0 |
| Comprimento da opção | 11 | Dez | |
| Valor da opção | udhcp 0.9.8 | String | |
| Número da opção | 55 | | 1 | Os parâmetros de rede solicitados pelo cliente. A composição pode ser diferente
01 - Máscara de rede 03 - Gateway 06 - DNS oc - nome do host 0f - nome de domínio da rede 1c - endereço da solicitação de transmissão (Broadcast) 42 - nome do servidor TFTP 79 - Rota estática sem classe |
| Comprimento da opção | 8 | | 1 |
| Valor da opção | 01: 03: 06: 0c: 0f: 1c: 42: 79 | | 8 |
| Número da opção | 82 | Dez | 1 | A opção 82 repete o que veio no DHCPDISCOVER |
| Comprimento da opção | 18 | Dez | 1 |
| Valor da opção | 01: 08: 00: 06: 00 01: 01: 00: 00: 01 02: 06: 00: 03: 0f 26: 4d: ec | Dez | 18 |
| Fim do pacote | 255 | Dez | 1 | 255 simboliza o final da embalagem |
DHCPACK
Como confirmação do fato de que "sim, é o seu endereço IP e eu não o darei a mais ninguém" do servidor DHCP, há um pacote no formato DHCPACK do servidor para o cliente. É o mesmo que o resto dos pacotes enviados transmitidos. Embora, no código abaixo do servidor DHCP implementado em Python, por via das dúvidas, duplique qualquer solicitação de transmissão enviando um pacote para um IP de cliente específico, se ele já for conhecido. Além disso, o servidor DHCP não se importa se o pacote DHCPACK chegou ao cliente. Se o cliente não receber DHCPACK, depois de um tempo, ele simplesmente repetirá o DHCPREQUEST
Tabela de estrutura de pacotes DHCPACKPosição do pacote | Nome do valor (comum) | Exemplo | Submissão | Byte | Explicação |
1 | Solicitação de inicialização | 2 | Hex | 1 | Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente |
2 | Tipo de hardware | 1 | Hex | 1 | Tipo de endereço de hardware, neste protocolo 1 - MAC |
3 | Hardware adrees comprimento | 6 | Hex | 1 | Comprimento do endereço MAC do dispositivo |
4 | Lúpulo | 1 | Hex | 1 | Número de rotas intermediárias |
5 | ID da transação | 23: cf: de: 1d | Hex | 4 | Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação |
7 | Segundo decorrido | 0 0 | Hex | 4 | Tempo em segundos desde o início do processo de obtenção do endereço |
9 | Sinalizadores de Bootp | 8000 | Hex | 2 | Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo. Nesse caso, "Broadcast" |
11 | Endereço IP do cliente | 0.0.0.0 | String | 4 | Endereço IP do cliente (se houver) |
15 | O endereço IP do seu cliente | 172.16.134.61 | String | 4 | Endereço IP proposto pelo servidor (se houver) |
19 | Endereço IP do próximo servidor | 0.0.0.0 | String | 4 | Endereço IP do servidor (se conhecido) |
23 | Endereço IP do agente de retransmissão | 172.16.114.41 | String | 4 | Endereço IP do agente de retransmissão (por exemplo, um comutador) |
27 | Endereço MAC do cliente | 14: d6: 4d: a7: c9: 55 | Hex | 6 | Endereço MAC do remetente do pacote (cliente) |
31 | Preenchimento de endereço de hardware do cliente | | Hex | 10 | Lugar reservado. Geralmente zeros |
41. | Nome do host do servidor | | String | 64 | O nome do servidor DHCP. Geralmente não transmitido |
105 | Nome do arquivo de inicialização | | String | 128 | O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização |
235 | Biscoito mágico | 63: 82: 53: 63 | Hex | 4 | O número "mágico", pelo qual incl. você pode determinar que este pacote pertence ao protocolo DHCP |
Opções de DHCP. Pode ir em qualquer ordem |
236 | Número da opção | 53 | Dez | 3 | Opção 53 especificando pacote DHCP tipo 5 - DHCPACK |
| Comprimento da opção | 1 | Dez | 1 |
| Valor da opção | 5 | Dez | 1 |
| Número da opção | 1 | Dez | 1 | Opção que oferece máscara de rede do cliente DHCP |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 255.255.224.0 | String | 4 |
| Número da opção | 3 | Dez | 1 | Opção que oferece gateway padrão do cliente DHCP |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 172.16.12.1 | String | 4 |
| Número da opção | 6 | Dez | 1 | Opção que oferece DHCP ao cliente DNS |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 8.8.8.8 | String | 4 |
| Número da opção | 51 | Dez | 1 | A vida útil dos parâmetros de rede emitidos em segundos, após os quais o cliente DHCP deve solicitá-los novamente |
| Comprimento da opção | 4 | Dez | 1 |
| Valor da opção | 86400 | Dez | 4 |
| Número da opção | 82 | Dez | 1 | A opção 82 repete o que veio no DHCPDISCOVER |
| Comprimento da opção | 18 | Dez | 1 |
| Valor da opção | 01: 08: 00: 06: 00 01: 01: 00: 00: 01 02: 06: 00: 03: 0f 26: 4d: ec | Dez | 18 |
| Fim do pacote | 255 | Dez | 1 | 255 simboliza o final da embalagem |
Instalação
A instalação consiste na instalação dos módulos python necessários para o trabalho. Supõe-se que o MySQL já esteja instalado e configurado.
Freebsd
pkg install python3
python3 -m surepip
instalar o pip3 mysql-connector
Ubuntu
sudo apt-get install python3
sudo apt-get install pip3
sudo pip3 instala o mysql-connector
Criamos o banco de dados MySQL, preenchemos o dump pydhcp.sql, configuramos o arquivo de configuração.
Configuração
Todas as configurações do servidor estão no formato de arquivo xml. Arquivo de referência:
<? xml version = "1.0"?>
<config>
<dhcpserver>
<host> 0.0.0.0 </host>
<broadcast> 255.255.255.255 </broadcast>
<DHCPServer> 192.168.0.71 </DHCPServer>
<LeaseTime> 8600 </LeaseTime>
<ThreadLimit> 1 </ThreadLimit>
<defaultMask> 255.255.255.0 </defaultMask>
<defaultRouter> 192.168.0.1 </defaultRouter>
<defaultDNS> 8.8.8.8 </defaultDNS>
</dhcpserver>
<mysql>
<host> localhost </host>
<username> test </username>
<senha> teste </senha>
<basename> pydhcp </basename>
</mysql>
<opções>
<opção> option_82_hex: sw_port1: 20:22 </option>
<opção> option_82_hex: sw_port2: 16:18 </option>
<opção> option_82_hex: sw_mac: 26: 40 </option>
</options>
<query>
<offer_count> 3 </offer_count>
<offer_1> selecione IP, máscara, roteador, DNS dos usuários em que superior (mac) = superior ('{option_82_AgentRemoteId_hex}') e superior (porta) = superior ('{option_82_AgentCircuitId_port_hex}') </offer_1>
<offer_2> selecione ip, mascara, roteador, dns dos usuários onde superior (mac) = superior ('{sw_mac}') e superior (porta) = superior ('{sw_port2}') </offer_2>
<offer_3> selecione ip, mask, router, dns dos usuários em que upper (mac) = upper ('{ClientMacAddress}') </offer_3>
<history_sql> insere no histórico (id, dt, mac, ip, comment) valores (null, now (), '{ClientMacAddress}', '{RequestedIpAddress}', 'DHCPACK / INFORM') </history_sql>
</query>
</config>
Agora com mais detalhes nas tags:
A seção dhcpserver descreve as configurações básicas para iniciar o servidor, a saber:
- host - qual endereço IP o servidor está escutando na porta 67
- broadcast - qual ip é um Broadcast para DHCPOFFER e DHCPACK
- DHCPServer - qual é o ip do servidor DHCP
- Hora de concessão do endereço IP emitido
- ThreadLimit - quantos threads estão executando simultaneamente para processar pacotes UDP de entrada na porta 67. Supõe-se que isso ajude em projetos altamente carregados;)
- defaultMask, defaultRouter, defaultDNS - o que é oferecido ao assinante por padrão se o IP for encontrado no banco de dados, mas nenhum parâmetro adicional for especificado para ele
Seção Mysql:
host, nome de usuário, senha, nome de base - tudo fala por si. Estrutura de banco de dados de amostra postada no
GitHubSeção de consulta: Esta seção descreve os pedidos de OFERTA / ACK:
- offer_count - o número de linhas com solicitações que retornam um resultado do formulário ip, mask, router, dns
- offer_n é a string de consulta. Se o retorno estiver vazio, a seguinte solicitação de oferta será executada
- history_sql - solicitação de gravação, por exemplo, no "histórico de autorização" do assinante
Quaisquer variáveis da seção de opções ou opções do protocolo DHCP podem participar de solicitações.
Opções de seção. Aqui já é mais interessante. Aqui, podemos criar variáveis que podemos usar posteriormente na seção de consulta.
Por exemplo:
option_82_hex:sw_port1:20:22
, esta linha é o comando para pegar toda a linha que veio na solicitação DHCP da opção 82, em formato hexadecimal, no intervalo de 20 a 22 bytes, inclusive, e colocá-la na nova variável sw_port1 (a porta do switch de onde a solicitação veio)
option_82_hex:sw_mac:26:40
, defina a variável sw_mac, usando hexadecimal do intervalo 26:40
Você pode ver todas as opções possíveis que podem ser usadas nas consultas iniciando o servidor com a opção -d. Veremos algo como este log:
- o pacote DHCPINFORM veio na porta 67, de 0025224ad764, b '\ x91 \ xa5 \ xe0 \ xa3 \ xa5 \ xa9- \ x8f \ x8a', ('172.30.114.25', 68)
{'ClientMacAddress': '0025224ad764',
'ClientMacAddressByte': b '\ x00% "J \ xd7d',
'HType': 'Ethernet',
'HostName': b '\ x91 \ xa5 \ xe0 \ xa3 \ xa5 \ xa9- \ x8f \ x8a',
'ReqListDNS': True,
'ReqListDomainName': True,
'ReqListPerfowmRouterDiscover': True,
'ReqListRouter': True,
'ReqListStaticRoute': True,
'ReqListSubnetMask': True,
'ReqListVendorSpecInfo': 43,
'RequestedIpAddress': '0.0.0.0',
'Fornecedor': b'MSFT 5.0 ',
'chaddr': '0025224ad764',
«ciaddr»: «172.30.128.13»,
'sinalizadores': b '\ x00 \ x00',
«giaddr»: «172.30.114.25»,
«gpoz»: 308,
«hlen»: 6,
«lúpulo»: 1,
'htype': 'MAC',
'magic_cookie': b'c \ x82Sc ',
'op': 'DHCPINFORM',
'opção12': 12,
«opção53»: 53,
«opção55»: 55,
«opção60»: 60,
«opção61»: 61,
«opção82»: 82,
'opção_82_byte': b '\ x12 \ x01 \ x06 \ x00 \ x04 \ x00 \ x01 \ x00 \ x06 \ x02 \ x08 \ x00'
b '\ x06 \ x00 \ x1eX \ x9e \ xb2 \ xad',
'option_82_hex': '12010600040001000602080006001e589eb2ad',
'option_82_len': 18,
'option_82_str': "b '\\ x12 \\ x01 \\ x06 \\ x00 \\ x04 \\ x00 \\ x01 \\ x00 \\ x06 \\ x02 \\ x08 \\ x00 \\ x06 \\ x00 \ \ x1eX \\ x9e \\ xb2 \\ xad '",
'resultado': falso,
«secs»: 768,
'siaddr': '0.0.0.0',
'sw_mac': '001e589eb2ad',
'sw_port1': '06',
'xidbyte': b '<\ x89} \ x8c',
'xidhex': '3c897d8c',
'yiaddr': '0.0.0.0'}
Assim, podemos agrupar qualquer variável em {} e ela será usada na consulta SQL.
Vamos capturar para o histórico que o cliente recebeu o endereço IP:


Início do servidor
./pydhcpdb.py -d -c config.xml
- d modo de saída para o console DEBUG
- c <file_name> arquivo de configuração
Debriefing
E agora mais sobre a implementação do servidor em Python. Isso é uma dor. Python foi estudado em tempo real. Muitos momentos são feitos no estilo de: "uau, de alguma maneira eu fiz o que funciona". Não é otimizado e é deixado nessa forma principalmente devido à pequena experiência de desenvolvimento em python. Vou abordar os momentos mais interessantes da implementação do servidor no "código".
Analisador de arquivo de configuração XML
O módulo Python padrão xml.dom é usado. Parece ser simples, mas durante a implementação houve uma notável falta de documentação e exemplos sensatos na rede usando este módulo.
árvore = minidom.parse (gconfig ["config_file"])
mconfig = tree.getElementsByTagName ("mysql")
para elem no mconfig:
gconfig ["mysql_host"] = elem.getElementsByTagName ("host") [0] .firstChild.data
gconfig ["mysql_username"] = elem.getElementsByTagName ("nome de usuário") [0] .firstChild.data
gconfig ["mysql_password"] = elem.getElementsByTagName ("senha") [0] .firstChild.data
gconfig ["mysql_basename"] = elem.getElementsByTagName ("basename") [0] .firstChild.data
dconfig = tree.getElementsByTagName ("dhcpserver")
para elem no dconfig:
gconfig ["broadcast"] = elem.getElementsByTagName ("broadcast") [0] .firstChild.data
gconfig ["dhcp_host"] = elem.getElementsByTagName ("host") [0] .firstChild.data
gconfig ["dhcp_LeaseTime"] = elem.getElementsByTagName ("LeaseTime") [0] .firstChild.data
gconfig ["dhcp_ThreadLimit"] = int (elem.getElementsByTagName ("ThreadLimit") [0] .firstChild.data)
gconfig ["dhcp_Server"] = elem.getElementsByTagName ("DHCPServer") [0] .firstChild.data
gconfig ["dhcp_defaultMask"] = elem.getElementsByTagName ("defaultMask") [0] .firstChild.data
gconfig ["dhcp_defaultRouter"] = elem.getElementsByTagName ("defaultRouter") [0] .firstChild.data
gconfig ["dhcp_defaultDNS"] = elem.getElementsByTagName ("defaultDNS") [0] .firstChild.data
qconfig = tree.getElementsByTagName ("query")
para elem no qconfig:
gconfig ["offer_count"] = elem.getElementsByTagName ("offer_count") [0] .firstChild.data
para num no intervalo (int (gconfig ["offer_count"])):
gconfig ["oferta _" + str (num + 1)] = elem.getElementsByTagName ("oferta _" + str (num + 1)) [0] .firstChild.data
gconfig ["history_sql"] = elem.getElementsByTagName ("history_sql") [0] .firstChild.data
options = tree.getElementsByTagName ("opções")
para elem nas opções:
node = elem.getElementsByTagName ("opção")
para opções no nó:
optionsMod.append (options.firstChild.data)
Multithreading
Curiosamente, o multithreading no Python é implementado de maneira muito clara e simples.
def PacketWork (dados, endereço):
...
# implementação da análise do pacote recebido e a resposta a ele
...
enquanto True:
data, addr = udp_socket.recvfrom (1024) # aguarde o pacote UDP
thread = threading.Thread (target = PacketWork, args = (dados, endereço,)). start () # como veio - execute a função PacketWork definida anteriormente no fundo com parâmetros
enquanto threading.active_count ()> gconfig ["dhcp_ThreadLimit"]:
time.sleep (1) # se o número de threads já em execução for maior que nas configurações, aguarde até que se tornem menos
Receber / enviar pacote DHCP
Para interceptar pacotes UDP vindos da placa de rede, você precisa "aumentar" o soquete:
udp_socket = socket.socket (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
udp_socket.bind ((gconfig ["dhcp_host"], 67))
onde as bandeiras estão:
- AF_INET - significa que o formato do endereço será IP: port. Talvez AF_UNIX - onde o endereço é dado pelo nome do arquivo.
- SOCK_DGRAM - significa que estamos aceitando não um "pacote bruto", mas já passamos por um firewall e com pacote parcialmente cortado. I.e. obtemos apenas o pacote UDP sem o componente "físico" do wrapper de pacote UDP. Se você usar o sinalizador SOCK_RAW, ainda precisará analisar esse "wrapper".
O envio de um pacote pode ser como um Broadcast:
udp_socket.setsockopt (socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # alterna o soquete para o modo Broadcast
rz = udp_socket.sendto (pacote de pacote, (gconfig ["broadcast"], 68))
e para o endereço "de onde veio o pacote":
udp_socket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # alterne o soquete para o modo "muitos ouvintes"
rz = udp_socket.sendto (pacote, endereço)
em que SOL_SOCKET significa "nível de protocolo" para definir opções,
A opção SO_BROADCAST é que o pacote de capacete Broadcast
A opção SO_REUSEADDR alterna o soquete para o modo de ouvinte múltiplo. Em teoria, é desnecessário neste caso, mas em um dos servidores FreeBSD em que testei, o código não funcionou sem essa opção.
Análise de pacotes DHCP
É aqui que eu realmente gostei de Python. Acontece que a "caixa" permite que você lide facilmente com o bytecode. Permitindo que seja muito simples traduzir para valores decimais, seqüências de caracteres e hexadecimal - ou seja, o que realmente precisamos entender a estrutura do pacote. Por exemplo, você pode obter um intervalo de bytes em HEX e apenas bytes:
res ["xidhex"] = dados [4: 8] .hex ()
res ["xidbyte"] = dados [4: 8]
, compactar bytes em uma estrutura:
res ["flags"] = pacote ('BB', dados [10], dados [11])
Obter IP da estrutura:
res ["ciaddr"] = socket.inet_ntoa (pacote ('BBBB', dados [12], dados [13], dados [14], dados [15]));
E vice-versa:
res = res + socket.inet_pton (socket.AF_INET, gconfig ["dhcp_Server"])
Isso é tudo;)