Servidor DHCP + Mysql em Python



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:

  1. 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.
  2. 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.
  3. 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.
  4. 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 DHCPDISCOVER
Posição do pacoteNome do valorExemploSubmissãoByteExplicação
1Solicitação de inicialização1Hex1Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente
2Tipo de hardware1Hex1Tipo de endereço de hardware, neste protocolo 1 - MAC
3Hardware adrees comprimento6Hex1Comprimento do endereço MAC do dispositivo
4Lúpulo1Hex1Número de rotas intermediárias
5ID da transação23: cf: de: 1dHex4Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação
7Segundo decorrido0 0Hex4Tempo em segundos desde o início do processo de obtenção do endereço
9Sinalizadores de Bootp0 0Hex2Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo
11Endereço IP do cliente0.0.0.0String4Endereço IP do cliente (se houver)
15O endereço IP do seu cliente0.0.0.0String4Endereço IP proposto pelo servidor (se houver)
19Endereço IP do próximo servidor0.0.0.0String4Endereço IP do servidor (se conhecido)
23Endereço IP do agente de retransmissão172.16.114.41String4Endereço IP do agente de retransmissão (por exemplo, um comutador)
27Endereço MAC do cliente14: d6: 4d: a7: c9: 55Hex6Endereço MAC do remetente do pacote (cliente)
31Preenchimento de endereço de hardware do clienteHex10Lugar reservado. Geralmente zeros
41.Nome do host do servidorString64O nome do servidor DHCP. Geralmente não transmitido
105Nome do arquivo de inicializaçãoString128O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização
235Biscoito mágico63: 82: 53: 63Hex4O 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
236Número da opção53Dez1Opção 53 que especifica o tipo de pacote DHCP

1 - DHCPDISCOVER
3 - DHCPREQUEST
2 - DHCPOFFER
5 - DHCPACK
8 - DHCPINFORM
Comprimento da opção1Dez1
Valor da opção1Dez1
Número da opção50.Dez1Qual endereço IP o cliente deseja receber
Comprimento da opção4Dez1
Valor da opção172.16.134.61String4
Número da opção551Os 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ção81
Valor da opção01: 03: 06: 0c: 0f: 1c: 42: 798
Número da opção82DezOpçã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ção18Dez
Valor da opção01:06
00: 04: 00: 01: 00: 04
02:08
00: 06: c8: be: 19: 93: 11: 48
Hex
Fim do pacote255Dez1255 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 DHCPOFFER
Posição do pacoteNome do valor (comum)ExemploSubmissãoByteExplicação
1Solicitação de inicialização1Hex1Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente
2Tipo de hardware1Hex1Tipo de endereço de hardware, neste protocolo 1 - MAC
3Hardware adrees comprimento6Hex1Comprimento do endereço MAC do dispositivo
4Lúpulo1Hex1Número de rotas intermediárias
5ID da transação23: cf: de: 1dHex4Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação
7Segundo decorrido0 0Hex4Tempo em segundos desde o início do processo de obtenção do endereço
9Sinalizadores de Bootp0 0Hex2Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo. Nesse caso, 0 significa o tipo de solicitação Unicast
11Endereço IP do cliente0.0.0.0String4Endereço IP do cliente (se houver)
15O endereço IP do seu cliente172.16.134.61String4Endereço IP proposto pelo servidor (se houver)
19Endereço IP do próximo servidor0.0.0.0String4Endereço IP do servidor (se conhecido)
23Endereço IP do agente de retransmissão172.16.114.41String4Endereço IP do agente de retransmissão (por exemplo, um comutador)
27Endereço MAC do cliente14: d6: 4d: a7: c9: 55Hex6Endereço MAC do remetente do pacote (cliente)
31Preenchimento de endereço de hardware do clienteHex10Lugar reservado. Geralmente zeros
41.Nome do host do servidorString64O nome do servidor DHCP. Geralmente não transmitido
105Nome do arquivo de inicializaçãoString128O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização
235Biscoito mágico63: 82: 53: 63Hex4O 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
236Número da opção53Dez1Opção 53 que especifica o tipo de pacote DHCP 2 - DHCPOFFER
Comprimento da opção1Dez1
Valor da opção2Dez1
Número da opção1Dez1Opção que oferece máscara de rede do cliente DHCP
Comprimento da opção4Dez1
Valor da opção255.255.224.0String4
Número da opção3Dez1Opção que oferece gateway padrão do cliente DHCP
Comprimento da opção4Dez1
Valor da opção172.16.12.1String4
Número da opção6Dez1Opção que oferece DHCP ao cliente DNS
Comprimento da opção4Dez1
Valor da opção8.8.8.8String4
Número da opção51Dez1A vida útil dos parâmetros de rede emitidos em segundos, após os quais o cliente DHCP deve solicitá-los novamente
Comprimento da opção4Dez1
Valor da opção86400Dez4
Número da opção82Dez1A opção 82 repete o que veio no DHCPDISCOVER
Comprimento da opção18Dez1
Valor da opção01: 08: 00: 06: 00
01: 01: 00: 00: 01
02: 06: 00: 03: 0f
26: 4d: ec
Dez18
Fim do pacote255Dez1255 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 DHCPREQUEST
Posição do pacoteNome do valor (comum)ExemploSubmissãoByteExplicação
1Solicitação de inicialização1Hex1Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente
2Tipo de hardware1Hex1Tipo de endereço de hardware, neste protocolo 1 - MAC
3Hardware adrees comprimento6Hex1Comprimento do endereço MAC do dispositivo
4Lúpulo1Hex1Número de rotas intermediárias
5ID da transação23: cf: de: 1dHex4Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação
7Segundo decorrido0 0Hex4Tempo em segundos desde o início do processo de obtenção do endereço
9Sinalizadores de Bootp8000Hex2Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo. Nesse caso, "Broadcast"
11Endereço IP do cliente0.0.0.0String4Endereço IP do cliente (se houver)
15O endereço IP do seu cliente172.16.134.61String4Endereço IP proposto pelo servidor (se houver)
19Endereço IP do próximo servidor0.0.0.0String4Endereço IP do servidor (se conhecido)
23Endereço IP do agente de retransmissão172.16.114.41String4Endereço IP do agente de retransmissão (por exemplo, um comutador)
27Endereço MAC do cliente14: d6: 4d: a7: c9: 55Hex6Endereço MAC do remetente do pacote (cliente)
31Preenchimento de endereço de hardware do clienteHex10Lugar reservado. Geralmente zeros
41.Nome do host do servidorString64O nome do servidor DHCP. Geralmente não transmitido
105Nome do arquivo de inicializaçãoString128O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização
235Biscoito mágico63: 82: 53: 63Hex4O 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
236Número da opção53Dez3Opção 53 que especifica o tipo de pacote DHCP 3 - DHCPREQUEST
Comprimento da opção1Dez1
Valor da opção3Dez1
Número da opção61Dez1ID do cliente: 01 (para Ehernet) + endereço MAC do cliente
Comprimento da opção7Dez1
Valor da opção01: 2c: ab: 25: ff: 72: a6Hex7
Número da opção60Dez"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ção11Dez
Valor da opçãoudhcp 0.9.8String
Número da opção551Os 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ção81
Valor da opção01: 03: 06: 0c: 0f: 1c: 42: 798
Número da opção82Dez1A opção 82 repete o que veio no DHCPDISCOVER
Comprimento da opção18Dez1
Valor da opção01: 08: 00: 06: 00
01: 01: 00: 00: 01
02: 06: 00: 03: 0f
26: 4d: ec
Dez18
Fim do pacote255Dez1255 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 DHCPACK
Posição do pacoteNome do valor (comum)ExemploSubmissãoByteExplicação
1Solicitação de inicialização2Hex1Tipo de mensagem. 1 - solicitação do cliente para o servidor, 2 - resposta do servidor para o cliente
2Tipo de hardware1Hex1Tipo de endereço de hardware, neste protocolo 1 - MAC
3Hardware adrees comprimento6Hex1Comprimento do endereço MAC do dispositivo
4Lúpulo1Hex1Número de rotas intermediárias
5ID da transação23: cf: de: 1dHex4Identificador de transação exclusivo. Gerado pelo cliente no início da operação de solicitação
7Segundo decorrido0 0Hex4Tempo em segundos desde o início do processo de obtenção do endereço
9Sinalizadores de Bootp8000Hex2Alguns sinalizadores que podem ser definidos como uma indicação dos parâmetros do protocolo. Nesse caso, "Broadcast"
11Endereço IP do cliente0.0.0.0String4Endereço IP do cliente (se houver)
15O endereço IP do seu cliente172.16.134.61String4Endereço IP proposto pelo servidor (se houver)
19Endereço IP do próximo servidor0.0.0.0String4Endereço IP do servidor (se conhecido)
23Endereço IP do agente de retransmissão172.16.114.41String4Endereço IP do agente de retransmissão (por exemplo, um comutador)
27Endereço MAC do cliente14: d6: 4d: a7: c9: 55Hex6Endereço MAC do remetente do pacote (cliente)
31Preenchimento de endereço de hardware do clienteHex10Lugar reservado. Geralmente zeros
41.Nome do host do servidorString64O nome do servidor DHCP. Geralmente não transmitido
105Nome do arquivo de inicializaçãoString128O nome do arquivo no servidor usado pelas estações sem disco durante a inicialização
235Biscoito mágico63: 82: 53: 63Hex4O 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
236Número da opção53Dez3Opção 53 especificando pacote DHCP tipo 5 - DHCPACK
Comprimento da opção1Dez1
Valor da opção5Dez1
Número da opção1Dez1Opção que oferece máscara de rede do cliente DHCP
Comprimento da opção4Dez1
Valor da opção255.255.224.0String4
Número da opção3Dez1Opção que oferece gateway padrão do cliente DHCP
Comprimento da opção4Dez1
Valor da opção172.16.12.1String4
Número da opção6Dez1Opção que oferece DHCP ao cliente DNS
Comprimento da opção4Dez1
Valor da opção8.8.8.8String4
Número da opção51Dez1A vida útil dos parâmetros de rede emitidos em segundos, após os quais o cliente DHCP deve solicitá-los novamente
Comprimento da opção4Dez1
Valor da opção86400Dez4
Número da opção82Dez1A opção 82 repete o que veio no DHCPDISCOVER
Comprimento da opção18Dez1
Valor da opção01: 08: 00: 06: 00
01: 01: 00: 00: 01
02: 06: 00: 03: 0f
26: 4d: ec
Dez18
Fim do pacote255Dez1255 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 GitHub

Seçã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;)

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


All Articles