Serveur DHCP+Mysql en Python

Serveur DHCP+Mysql en Python

Le but de ce projet était :

  • Découverte du DHCP sur un réseau IPv4
  • Apprendre Python (un peu plus qu'à partir de zéro 😉)
  • remplacement du serveur DB2DHCP (ma fourchette), original ici, qui devient de plus en plus difficile à assembler pour le nouvel OS. Et je n'aime pas que ce soit un système binaire qu'il n'y a aucun moyen de "changer maintenant"
  • obtenir un serveur DHCP fonctionnel avec la possibilité de sélectionner l'adresse IP de l'abonné à l'aide du Mac de l'abonné ou de changer la combinaison mac+port (Option 82)
  • écrire un autre vélo (Oh ! c'est mon activité préférée)
  • recevoir des commentaires sur votre côté club sur Habrahabr (ou mieux encore, une invitation) 😉

Résultat : ça marche 😉 Testé sur FreeBSD et Ubuntu OS. Théoriquement, on peut demander au code de fonctionner sous n'importe quel système d'exploitation, car Il ne semble y avoir aucune liaison spécifique dans le code.
Soigneusement! Il y a beaucoup plus à venir.

Lien vers le référentiel pour les amateurs "toucher vivant".

Le processus d'installation, de configuration et d'utilisation du résultat de « l'étude du matériel » est bien inférieur, puis un peu de théorie sur le protocole DHCP. Pour moi-même. Et pour l'histoire 😉

Un peu de théorie

Qu'est-ce que DHCP

Il s'agit d'un protocole réseau qui permet à un appareil de connaître son adresse IP (et d'autres paramètres comme la passerelle, le DNS, etc.) à partir d'un serveur DHCP. Les paquets sont échangés via le protocole UDP. Le principe général de fonctionnement de l'appareil lors de la demande de paramètres réseau est le suivant :

  1. L'appareil (client) envoie une requête de diffusion UDP (DHCPDISCOVER) sur tout le réseau avec la requête « eh bien, quelqu'un me donne une adresse IP ». De plus, généralement (mais pas toujours), la demande provient du port 68 (source) et la destination est le port 67 (destination). Certains appareils envoient également des paquets depuis le port 67. L'adresse MAC du périphérique client est incluse dans le package DHCPDISCOVER.
  2. Tous les serveurs DHCP situés sur le réseau (et il peut y en avoir plusieurs) forment une offre DHCPOFFER avec les paramètres réseau de l'appareil qui a envoyé DHCPDISCOVER, et la diffusent également sur le réseau. L'identification de la personne à qui ce paquet est destiné est basée sur l'adresse MAC du client fournie précédemment dans la requête DHCPDISCOVER.
  3. Le client accepte les paquets avec des propositions de paramètres réseau, sélectionne le plus attractif (les critères peuvent être différents, par exemple l'heure de livraison des paquets, le nombre de routes intermédiaires) et fait une « demande officielle » DHCPREQUEST avec les paramètres réseau. du serveur DHCP qu'il aime. Dans ce cas, le paquet est dirigé vers un serveur DHCP spécifique.
  4. Le serveur qui a reçu le DHCPREQUEST envoie un paquet au format DHCPACK, dans lequel il liste à nouveau les paramètres réseau destinés à ce client.

Serveur DHCP+Mysql en Python

De plus, il existe des paquets DHCPINFORM provenant du client et dont le but est d'informer le serveur DHCP que le « client est vivant » et utilise les paramètres réseau émis. Dans l'implémentation de ce serveur, ces paquets sont ignorés.

Format du paquet

En général, une trame de paquet Ethernet ressemble à ceci :

Serveur DHCP+Mysql en Python

Dans notre cas, nous considérerons uniquement les données directement issues du contenu du paquet UDP, sans en-têtes de protocole de couche OSI, à savoir la structure DHCP :

DHCPDÉCOUVRIR

Ainsi, le processus d'obtention d'une adresse IP pour un périphérique commence par l'envoi par le client DHCP d'une demande de diffusion du port 68 au 255.255.255.255:67. Dans ce package, le client inclut son adresse MAC, ainsi que ce qu'il souhaite recevoir exactement du serveur DHCP. La structure du package est décrite dans le tableau ci-dessous.

Tableau de structure des paquets DHCPDISCOVER

Position dans le colis
Nom de la valeur
Exemple
Introduction
Octet
Clarification

1
Demande de démarrage
1
Hex
1
Type de message. 1 - demande du client au serveur, 2 - réponse du serveur au client

2
Type de matériel
1
Hex
1
Type d'adresse matérielle, dans ce protocole 1 - MAC

3
Longueur des adresses matérielles
6
Hex
1
Longueur de l'adresse MAC de l'appareil

4
houblon
1
Hex
1
Nombre d'itinéraires intermédiaires

5
ID
23:cf:de:1d
Hex
4
Identifiant unique de la transaction. Généré par le client au début d'une opération de requête

7
Seconde écoulée
0
Hex
4
Temps en secondes depuis le début du processus d'obtention d'une adresse

9
Indicateurs de démarrage
0
Hex
2
Certains drapeaux pouvant être définis pour indiquer les paramètres du protocole

11
Adresse IP du client
0.0.0.0
Rangée
4
Adresse IP du client (le cas échéant)

15
Adresse IP de votre client
0.0.0.0
Rangée
4
Adresse IP proposée par le serveur (si disponible)

19
Adresse IP du serveur suivant
0.0.0.0
Rangée
4
Adresse IP du serveur (si connue)

23
Adresse IP de l'agent relais
172.16.114.41
Rangée
4
Adresse IP de l'agent relais (par exemple, un switch)

27
Adresse MAC du client
14:d6:4d:a7:c9:55
Hex
6
Adresse MAC de l'expéditeur du paquet (client)

31
Remplissage de l'adresse matérielle du client
 
Hex
10
Siège réservé. Généralement rempli de zéros

41
Nom d'hôte du serveur
 
Rangée
64
Nom du serveur DHCP. Généralement non transmis

105
Nom du fichier de démarrage
 
Rangée
128
Nom du fichier sur le serveur utilisé par les stations sans disque lors du démarrage

235
Biscuit magique
63: 82: 53: 63
Hex
4
Numéro « magique », selon lequel, incl. vous pouvez déterminer que ce paquet appartient au protocole DHCP

Options DHCP. Peut aller dans n'importe quel ordre

236
Numéro d'option
53
Décembre
1
Option 53, qui spécifie le type de paquet DHCP

1 - DHCPDÉCOUVRIR
3 - DEMANDE DHCP
2 - OFFRE DHCP
5 - DHCPACK
8 - DHCPINFORMER

 
Longueur des options
1
Décembre
1

 
Valeur d'option
1
Décembre
1

 
Numéro d'option
50
Décembre
1
Quelle adresse IP le client souhaite-t-il recevoir ?

 
Longueur des options
4
Décembre
1

 
Valeur d'option
172.16.134.61
Rangée
4

 
Numéro d'option
55
 
1
Paramètres réseau demandés par le client. La composition peut varier

01 — Masque réseau
03 - Passerelle
06 -DNS
oc — Nom d'hôte
0f - nom de domaine réseau
1c - adresse de la demande de diffusion (diffusion)
42 - Nom du serveur TFTP
79 - Route statique sans classe

 
Longueur des options
8
 
1

 
Valeur d'option
01:03:06:0c:0f:1c:42:79
 
8

 
Numéro d'option
82
Décembre
 
Option 82, qui transmet l'adresse MAC du dispositif répéteur et quelques valeurs supplémentaires.

Le plus souvent, il s'agit du port du switch sur lequel s'exécute le client DHCP final. Cette option contient des paramètres supplémentaires. Le premier octet est le numéro de la « sous-option », le second est sa longueur, puis sa valeur.

Dans ce cas, dans l'option 82, les sous-options sont imbriquées :
Agent Circuit ID = 00:04:00:01:00:04, où les deux derniers octets correspondent au port client DHCP d'où provient la demande.

ID distant de l'agent = 00:06:c8:be:19:93:11:48 - Adresse MAC du périphérique répéteur DHCP

 
Longueur des options
18
Décembre
 

 
Valeur d'option
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
Fin du forfait
255
Décembre
1
255 symbolise la fin du paquet

OFFRE DHCP

Dès que le serveur reçoit le paquet DHCPDISCOVER et s'il voit qu'il peut offrir au client quelque chose parmi celui demandé, il génère une réponse pour celui-ci - DHCPDISCOVER. La réponse est envoyée au port « d'où elle vient », par diffusion, car à ce moment, le client ne dispose pas encore d'adresse IP, il ne peut donc accepter le paquet que s'il est envoyé par diffusion. Le client reconnaît qu'il s'agit d'un package pour lui par son adresse MAC à l'intérieur du package, ainsi que par le numéro de transaction qu'il génère au moment de la création du premier package.

Tableau de structure des paquets DHCPOFFER

Position dans le colis
Nom de la valeur (commune)
Exemple
Introduction
Octet
Clarification

1
Demande de démarrage
1
Hex
1
Type de message. 1 - demande du client au serveur, 2 - réponse du serveur au client

2
Type de matériel
1
Hex
1
Type d'adresse matérielle, dans ce protocole 1 - MAC

3
Longueur des adresses matérielles
6
Hex
1
Longueur de l'adresse MAC de l'appareil

4
houblon
1
Hex
1
Nombre d'itinéraires intermédiaires

5
ID
23:cf:de:1d
Hex
4
Identifiant unique de la transaction. Généré par le client au début d'une opération de requête

7
Seconde écoulée
0
Hex
4
Temps en secondes depuis le début du processus d'obtention d'une adresse

9
Indicateurs de démarrage
0
Hex
2
Certains drapeaux pouvant être définis pour indiquer les paramètres du protocole. Dans ce cas, 0 signifie le type de requête Unicast

11
Adresse IP du client
0.0.0.0
Rangée
4
Adresse IP du client (le cas échéant)

15
Adresse IP de votre client
172.16.134.61
Rangée
4
Adresse IP proposée par le serveur (si disponible)

19
Adresse IP du serveur suivant
0.0.0.0
Rangée
4
Adresse IP du serveur (si connue)

23
Adresse IP de l'agent relais
172.16.114.41
Rangée
4
Adresse IP de l'agent relais (par exemple, un switch)

27
Adresse MAC du client
14:d6:4d:a7:c9:55
Hex
6
Adresse MAC de l'expéditeur du paquet (client)

31
Remplissage de l'adresse matérielle du client
 
Hex
10
Siège réservé. Généralement rempli de zéros

41
Nom d'hôte du serveur
 
Rangée
64
Nom du serveur DHCP. Généralement non transmis

105
Nom du fichier de démarrage
 
Rangée
128
Nom du fichier sur le serveur utilisé par les stations sans disque lors du démarrage

235
Biscuit magique
63: 82: 53: 63
Hex
4
Numéro « magique », selon lequel, incl. vous pouvez déterminer que ce paquet appartient au protocole DHCP

Options DHCP. Peut aller dans n'importe quel ordre

236
Numéro d'option
53
Décembre
1
Option 53, qui définit le type de paquet DHCP 2 - DHCPOFFER

 
Longueur des options
1
Décembre
1

 
Valeur d'option
2
Décembre
1

 
Numéro d'option
1
Décembre
1
Option pour offrir au client DHCP un masque réseau

 
Longueur des options
4
Décembre
1

 
Valeur d'option
255.255.224.0
Rangée
4

 
Numéro d'option
3
Décembre
1
Option pour offrir au client DHCP une passerelle par défaut

 
Longueur des options
4
Décembre
1

 
Valeur d'option
172.16.12.1
Rangée
4

 
Numéro d'option
6
Décembre
1
Option pour offrir DHCP au client DNS

 
Longueur des options
4
Décembre
1

 
Valeur d'option
8.8.8.8
Rangée
4

 
Numéro d'option
51
Décembre
1
La durée de vie des paramètres réseau émis en secondes, après quoi le client DHCP doit les demander à nouveau

 
Longueur des options
4
Décembre
1

 
Valeur d'option
86400
Décembre
4

 
Numéro d'option
82
Décembre
1
Option 82, répète ce qui est venu dans DHCPDISCOVER

 
Longueur des options
18
Décembre
1

 
Valeur d'option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Décembre
18

 
Fin du forfait
255
Décembre
1
255 symbolise la fin du paquet

DEMANDE DHCP

Une fois que le client a reçu DHCPOFFER, il forme un paquet demandant des paramètres réseau non pas à tous les serveurs DHCP du réseau, mais seulement à un serveur spécifique, dont il a le plus « apprécié » l'offre DHCPOFFER. Les critères « J’aime » peuvent être différents et dépendent de l’implémentation DHCP du client. Le destinataire de la requête est spécifié à l'aide de l'adresse MAC du serveur DHCP. De plus, un paquet DHCPREQUEST peut être envoyé par le client sans générer au préalable DHCPDISCOVER, si l'adresse IP du serveur a déjà été obtenue précédemment.

Tableau de structure des paquets DHCPREQUEST

Position dans le colis
Nom de la valeur (commune)
Exemple
Introduction
Octet
Clarification

1
Demande de démarrage
1
Hex
1
Type de message. 1 - demande du client au serveur, 2 - réponse du serveur au client

2
Type de matériel
1
Hex
1
Type d'adresse matérielle, dans ce protocole 1 - MAC

3
Longueur des adresses matérielles
6
Hex
1
Longueur de l'adresse MAC de l'appareil

4
houblon
1
Hex
1
Nombre d'itinéraires intermédiaires

5
ID
23:cf:de:1d
Hex
4
Identifiant unique de la transaction. Généré par le client au début d'une opération de requête

7
Seconde écoulée
0
Hex
4
Temps en secondes depuis le début du processus d'obtention d'une adresse

9
Indicateurs de démarrage
8000
Hex
2
Certains drapeaux pouvant être définis pour indiquer les paramètres du protocole. Dans ce cas, « diffusion » est défini

11
Adresse IP du client
0.0.0.0
Rangée
4
Adresse IP du client (le cas échéant)

15
Adresse IP de votre client
172.16.134.61
Rangée
4
Adresse IP proposée par le serveur (si disponible)

19
Adresse IP du serveur suivant
0.0.0.0
Rangée
4
Adresse IP du serveur (si connue)

23
Adresse IP de l'agent relais
172.16.114.41
Rangée
4
Adresse IP de l'agent relais (par exemple, un switch)

27
Adresse MAC du client
14:d6:4d:a7:c9:55
Hex
6
Adresse MAC de l'expéditeur du paquet (client)

31
Remplissage de l'adresse matérielle du client
 
Hex
10
Siège réservé. Généralement rempli de zéros

41
Nom d'hôte du serveur
 
Rangée
64
Nom du serveur DHCP. Généralement non transmis

105
Nom du fichier de démarrage
 
Rangée
128
Nom du fichier sur le serveur utilisé par les stations sans disque lors du démarrage

235
Biscuit magique
63: 82: 53: 63
Hex
4
Numéro « magique », selon lequel, incl. vous pouvez déterminer que ce paquet appartient au protocole DHCP

Options DHCP. Peut aller dans n'importe quel ordre

236
Numéro d'option
53
Décembre
3
Option 53, qui définit le type de paquet DHCP 3 - DHCPREQUEST

 
Longueur des options
1
Décembre
1

 
Valeur d'option
3
Décembre
1

 
Numéro d'option
61
Décembre
1
ID client : 01 (pour Ehernet) + adresse MAC du client

 
Longueur des options
7
Décembre
1

 
Valeur d'option
01:2c:ab:25:ff:72:a6
Hex
7

 
Numéro d'option
60
Décembre
 
"Identifiant de classe du fournisseur". Dans mon cas, il indique la version du client DHCP. Peut-être que d'autres appareils renvoient quelque chose de différent. Windows par exemple rapporte MSFT 5.0

 
Longueur des options
11
Décembre
 

 
Valeur d'option
udcp 0.9.8
Rangée
 

 
Numéro d'option
55
 
1
Paramètres réseau demandés par le client. La composition peut varier

01 — Masque réseau
03 - Passerelle
06 -DNS
oc — Nom d'hôte
0f - nom de domaine réseau
1c - adresse de la demande de diffusion (diffusion)
42 - Nom du serveur TFTP
79 - Route statique sans classe

 
Longueur des options
8
 
1

 
Valeur d'option
01:03:06:0c:0f:1c:42:79
 
8

 
Numéro d'option
82
Décembre
1
Option 82, répète ce qui est venu dans DHCPDISCOVER

 
Longueur des options
18
Décembre
1

 
Valeur d'option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Décembre
18

 
Fin du forfait
255
Décembre
1
255 symbolise la fin du paquet

DHCPACK

Pour confirmer que "oui, c'est vrai, c'est votre adresse IP, et je ne la donnerai à personne d'autre" du serveur DHCP, un paquet au format DHCPACK du serveur au client est servi. Il est envoyé en diffusion comme les autres paquets. Bien que, dans le code ci-dessous pour un serveur DHCP implémenté en Python, juste au cas où, je duplique toute demande de diffusion en envoyant un paquet à une IP client spécifique, si elle est déjà connue. De plus, le serveur DHCP ne se soucie pas du tout de savoir si le paquet DHCPACK a atteint le client. Si le client ne reçoit pas DHCPACK, après un certain temps, il répète simplement DHCPREQUEST

Tableau de structure des paquets DHCPACK

Position dans le colis
Nom de la valeur (commune)
Exemple
Introduction
Octet
Clarification

1
Demande de démarrage
2
Hex
1
Type de message. 1 - demande du client au serveur, 2 - réponse du serveur au client

2
Type de matériel
1
Hex
1
Type d'adresse matérielle, dans ce protocole 1 - MAC

3
Longueur des adresses matérielles
6
Hex
1
Longueur de l'adresse MAC de l'appareil

4
houblon
1
Hex
1
Nombre d'itinéraires intermédiaires

5
ID
23:cf:de:1d
Hex
4
Identifiant unique de la transaction. Généré par le client au début d'une opération de requête

7
Seconde écoulée
0
Hex
4
Temps en secondes depuis le début du processus d'obtention d'une adresse

9
Indicateurs de démarrage
8000
Hex
2
Certains drapeaux pouvant être définis pour indiquer les paramètres du protocole. Dans ce cas, « diffusion » est défini

11
Adresse IP du client
0.0.0.0
Rangée
4
Adresse IP du client (le cas échéant)

15
Adresse IP de votre client
172.16.134.61
Rangée
4
Adresse IP proposée par le serveur (si disponible)

19
Adresse IP du serveur suivant
0.0.0.0
Rangée
4
Adresse IP du serveur (si connue)

23
Adresse IP de l'agent relais
172.16.114.41
Rangée
4
Adresse IP de l'agent relais (par exemple, un switch)

27
Adresse MAC du client
14:d6:4d:a7:c9:55
Hex
6
Adresse MAC de l'expéditeur du paquet (client)

31
Remplissage de l'adresse matérielle du client
 
Hex
10
Siège réservé. Généralement rempli de zéros

41
Nom d'hôte du serveur
 
Rangée
64
Nom du serveur DHCP. Généralement non transmis

105
Nom du fichier de démarrage
 
Rangée
128
Nom du fichier sur le serveur utilisé par les stations sans disque lors du démarrage

235
Biscuit magique
63: 82: 53: 63
Hex
4
Numéro « magique », selon lequel, incl. vous pouvez déterminer que ce paquet appartient au protocole DHCP

Options DHCP. Peut aller dans n'importe quel ordre

236
Numéro d'option
53
Décembre
3
Option 53, qui définit le type de paquet DHCP 5 - DHCPACK

 
Longueur des options
1
Décembre
1

 
Valeur d'option
5
Décembre
1

 
Numéro d'option
1
Décembre
1
Option pour offrir au client DHCP un masque réseau

 
Longueur des options
4
Décembre
1

 
Valeur d'option
255.255.224.0
Rangée
4

 
Numéro d'option
3
Décembre
1
Option pour offrir au client DHCP une passerelle par défaut

 
Longueur des options
4
Décembre
1

 
Valeur d'option
172.16.12.1
Rangée
4

 
Numéro d'option
6
Décembre
1
Option pour offrir DHCP au client DNS

 
Longueur des options
4
Décembre
1

 
Valeur d'option
8.8.8.8
Rangée
4

 
Numéro d'option
51
Décembre
1
La durée de vie des paramètres réseau émis en secondes, après quoi le client DHCP doit les demander à nouveau

 
Longueur des options
4
Décembre
1

 
Valeur d'option
86400
Décembre
4

 
Numéro d'option
82
Décembre
1
Option 82, répète ce qui est venu dans DHCPDISCOVER

 
Longueur des options
18
Décembre
1

 
Valeur d'option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Décembre
18

 
Fin du forfait
255
Décembre
1
255 symbolise la fin du paquet

Installation

L'installation consiste en fait à installer les modules python nécessaires au travail. On suppose que MySQL est déjà installé et configuré.

FreeBSD

pkg install python3 python3 -m assurerpip pip3 installer le connecteur mysql

Ubuntu

sudo apt-get install python3 sudo apt-get install pip3 sudo pip3 install mysql-connector

Nous créons une base de données MySQL, y téléchargeons le dump pydhcp.sql et configurons le fichier de configuration.

Configuration

Tous les paramètres du serveur sont dans un fichier XML. Dossier de référence :

1.0 0.0.0.0 255.255.255.255 192.168.0.71 8600 1 255.255.255.0 192.168.0.1 hôte local test test pydhcp option_8.8.8.8_hex:sw_port82:1:20 option_22_hex:sw_port82:2:16 option_18_hex:sw_mac:82:26 40 sélectionnez l'adresse IP, le masque, le routeur, le DNS des utilisateurs où upper(mac)=upper('{option_3_AgentRemoteId_hex}') et upper(port)=upper('{option_1_AgentCircuitId_port_hex}') sélectionnez l'adresse IP, le masque, le routeur, le DNS des utilisateurs où upper(mac)=upper('{sw_mac}') et upper(port)=upper('{sw_port82}') sélectionnez l'adresse IP, le masque, le routeur, le DNS des utilisateurs où upper(mac)=upper('{ClientMacAddress}') insérer dans l'historique (id,dt,mac,ip,comment) des valeurs (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')

Maintenant plus en détail sur les balises :

La section dhcpserver décrit les paramètres de base pour démarrer le serveur, à savoir :

  • hôte - quelle adresse IP le serveur écoute sur le port 67
  • diffusion - quelle adresse IP est la diffusion pour DHCPOFFER et DHCPACK
  • DHCPServer - quelle est l'adresse IP du serveur DHCP
  • Durée du bail LeaseTime de l'adresse IP émise
  • ThreadLimit - combien de threads s'exécutent simultanément pour traiter les paquets UDP entrants sur le port 67. Il est censé aider sur les projets à forte charge 😉
  • defaultMask,defaultRouter,defaultDNS - ce qui est proposé à l'abonné par défaut si une adresse IP est trouvée dans la base de données, mais qu'aucun paramètre supplémentaire n'est spécifié pour celle-ci

rubrique MySQL :

hôte, nom d'utilisateur, mot de passe, nom de base - tout parle de lui-même. Une structure approximative de la base de données est publiée sur GitHub

Section Requête : les demandes de réception d'OFFER/ACK sont décrites ici :

  • offer_count — le nombre de lignes avec des requêtes qui renvoient un résultat comme ip,mask,router,dns
  • offer_n — chaîne de requête. Si return est vide, exécute alors la demande d'offre suivante
  • history_sql - une requête qui écrit, par exemple, dans « l'historique des autorisations » d'un abonné

Les requêtes peuvent inclure n'importe quelle variable de la section options ou des options du protocole DHCP.

Rubrique Options. C'est là que ça devient plus intéressant. Ici, nous pouvons créer des variables que nous pourrons utiliser plus tard dans la section requête.

Par exemple:

option_82_hex:sw_port1:20:22

, cette ligne de commande prend la ligne entière fournie dans l'option de requête DHCP 82, au format hexadécimal, dans la plage de 20 à 22 octets inclus et la place dans la nouvelle variable sw_port1 (port de commutation d'où provient la requête)

option_82_hex:sw_mac:26:40

, définissez la variable sw_mac, en prenant l'hexagone dans la plage 26:40

Vous pouvez voir toutes les options possibles pouvant être utilisées dans les requêtes en démarrant le serveur avec le commutateur -d. Nous verrons quelque chose comme ce journal :

--un paquet DHCPINFORM est arrivé sur le port 67, depuis 0025224ad764 , b'x91xa5xe0xa3xa5xa9-x8fx8a' , ('172.30.114.25', 68) {'ClientMacAddress' : '0025224ad764', 'ClientMacAddressByte' : b'x00 7 %"Jxd91d' , ' HType' : 'Ethernet', 'HostName' : b'x5xa0xe3xa5xa9xa8-x8fx43a', 'ReqListDNS' : True, 'ReqListDomainName' : True, 'ReqListPerfowmRouterDiscover' : True, 'ReqListRouter' : True, 'ReqListStaticRoute' : True, 'ReqListSubnetM request' : True, 'ReqListVendorSpecInfo' : 0.0.0.0, 'RequestedIpAddress' : '5.0', 'Vendor' : b'MSFT 0025224', 'chaddr' : '764ad172.30.128.13', 'ciaddr' : '00' , 'flags' : b'x00x172.30.114.25', 'giaddr' : '308', 'gpoz' : 6, 'hlen' : 1, 'hops' : 82, 'htype' : 'MAC', 'magic_cookie' : b'cx12Sc ', 'op' : 'DHCPINFORM', 'option12' : 53, 'option53' : 55, 'option55' : 60, 'option60' : 61, 'option61' : 82, 'option82' : 82, ' option_12_byte' : b'x01x06x00x04x00x01x00x06x02x08x00x06' b'x00x1x9eXx2exb82xad', 'option_12010600040001000602080006001_hex' : '589e2eb82ad', 'option _18_len' : 82 12, 'option_01_str' : "b'x06x00x04x00x01x00x06x02x08x00x06x00x1x9x2eXx768exb0.0.0.0xad'", 'résultat' : Faux, 'secs' : 001, 'siaddr' : '589', 'sw_mac' : '2e1eb06ad', 'sw_port89' : '8', 'xidbyte' : b'

En conséquence, nous pouvons envelopper n'importe quelle variable dans {} et elle sera utilisée dans la requête SQL.

Enregistrons pour l'historique que le client a reçu l'adresse IP :

Serveur DHCP+Mysql en Python

Serveur DHCP+Mysql en Python

Démarrage du serveur

./pydhcpdb.py -d -c config.xml

— d mode de sortie de la console DEBUG
- c <nom de fichier> fichier de configuration

Débriefing

Et maintenant plus de détails sur l'implémentation du serveur en Python. C'est une douleur. Python a été appris à la volée. De nombreux moments sont réalisés dans le style de « wow, d'une manière ou d'une autre, j'ai fait en sorte que ça marche ». Pas optimisé du tout, et laissé sous cette forme principalement en raison du peu d'expérience en développement Python. Je m'attarderai sur les aspects les plus intéressants de l'implémentation du serveur en « code ».

analyseur de fichiers de configuration XML

Le module Python standard xml.dom est utilisé. Cela semble simple, mais lors de la mise en œuvre, il y a eu un manque notable de documentation claire et d'exemples sur le réseau utilisant ce module.

    tree = minidom.parse(gconfig["config_file"]) mconfig=tree.getElementsByTagName("mysql") pour elem dans mconfig : gconfig["mysql_host"]=elem.getElementsByTagName("host")[0].firstChild.data gconfig["mysql_username"]=elem.getElementsByTagName("username")[0].firstChild.data gconfig["mysql_password"]=elem.getElementsByTagName("password")[0].firstChild.data gconfig["mysql_basename"] =elem.getElementsByTagName("basename")[0].firstChild.data dconfig=tree.getElementsByTagName("dhcpserver") pour elem dans 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") pour elem dans qconfig : gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data pour num dans range(int(gconfig["offer_count"])): gconfig["offer_"+str(num+1)]=elem.getElementsByTagName("offer_"+str(num+1))[0].firstChild.data gconfig ["history_sql"]=elem.getElementsByTagName("history_sql")[0].firstChild.data options=tree.getElementsByTagName("options") pour les elem dans les options : node=elem.getElementsByTagName("option") pour les options dans le nœud : optionsMod.append(options.firstChild.data)

Multithreading

Curieusement, le multithreading en Python est implémenté de manière très claire et simple.

def PacketWork(data,addr): ... # implémentation de l'analyse du paquet entrant et de sa réponse ... while True: data, addr = udp_socket.recvfrom(1024) # attente du thread du paquet UDP = threading.Thread( target=PacketWork , args=(data,addr,)).start() # comme il est venu - nous lançons la fonction PacketWork définie précédemment en arrière-plan avec des paramètres pendant le threading.active_count() >gconfig["dhcp_ThreadLimit"]: time. sleep(1) # si le nombre Il y a plus de threads déjà en cours d'exécution que dans les paramètres, on attend qu'il y en ait moins

Recevoir/envoyer un paquet DHCP

Afin d'intercepter les paquets UDP transitant par la carte réseau, vous devez « relever » le socket :

udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP) udp_socket.bind((gconfig["dhcp_host"],67))

, où les drapeaux sont :

  • AF_INET - signifie que le format de l'adresse sera IP : port. Il peut également y avoir AF_UNIX - où l'adresse est donnée par le nom du fichier.
  • SOCK_DGRAM - signifie que nous n'acceptons pas un « paquet brut », mais un paquet déjà passé à travers le pare-feu, et avec un paquet partiellement coupé. Ceux. nous recevons uniquement un paquet UDP sans le composant « physique » du wrapper de paquet UDP. Si vous utilisez le drapeau SOCK_RAW, vous devrez également analyser ce « wrapper ».

L'envoi d'un paquet peut être comme une diffusion :

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) #passer le socket en mode diffusion rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, et à l'adresse « d'où vient le colis » :

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # passer le socket en mode multi-écouteur rz=udp_socket.sendto(packetack, addr)

, où SOL_SOCKET signifie le « niveau de protocole » pour définir les options,

, option SO_BROADCAST indiquant que le package du casque est « diffusé »

  L'option ,SO_REUSEADDR fait passer le socket en mode « plusieurs auditeurs ». En théorie, c'est inutile dans ce cas, mais sur un des serveurs FreeBSD sur lequel j'ai testé, le code ne fonctionnait pas sans cette option.

Analyser un paquet DHCP

C'est là que j'ai vraiment aimé Python. Il s'avère que, dès le départ, cela vous permet d'être assez flexible avec le bytecode. Ce qui lui permet d'être très facilement traduit en valeurs décimales, chaînes et hexadécimaux - c'est-à-dire c’est ce dont nous avons réellement besoin pour comprendre la structure du paquet. Ainsi, par exemple, vous pouvez obtenir une plage d'octets en HEX et uniquement des octets :

    res["xidhex"]=data[4:8].hex() res["xidbyte"]=data[4:8]

, regroupez les octets dans une structure :

res["flags"]=pack('BB',données[10],données[11])

Obtenez l'IP de la structure :

res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));

Et vice versa:

res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])

C'est tout pour l'instant 😉

Source: habr.com

Ajouter un commentaire