Пример простого DHCP сервера на Python

В целях самообразования, написал скрипт на Python, который выдает запрашивающему IP адрес. Всё жестко прибито гвоздями. Делал для изучения работы DHCP /  Python не более того.

#!/usr/bin/env python3
# coding=utf-8
# Сервер DHCP на Python 3.5 с коннектором к БД MySQL
# Данный код создан и распространяется по лицензии GPL v3
# Изначальный автор данного кода - Грибов Павел
# http://грибовы.рф

import socket
import dhcp_parse_packet
from pprint import pprint
import threading

HOST = '0.0.0.0'    # какой интерфейс слушаем
PORT_IN = 67        # входящие пакеты UDP DHCP
PORT_OUT = 68       # исходящие пакеты UDP DHCP
LeaseTime=8600      # время жизни IP адреса
ThreadLimit=10      # количество потоков для обработки запросов

udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,socket.IPPROTO_UDP)
udp_socket.bind((HOST,PORT_IN))


def PacketWork(data,addr): 
    packet=dhcp_parse_packet.parsepacketIn(data)
    print("Пришел пакет:")
    pprint(packet)
    print("Address:")    
    pprint(addr)    
    if packet["op"]=="DHCPDISCOVER":
        print("--что нужно ответить этому товарищу...");
        packetoffer=dhcp_parse_packet.CreateDHCPOFFER(packet)
        print ("--делаем ему DHCPOFFER (выгодное предложение)")
        pprint(packetoffer)
        udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        rz=udp_socket.sendto(packetoffer, ("255.255.255.255",68))
        #pprint(rz)
        print("-ответили!");
        #verify=dhcp_parse_packet.parsepacketIn(packetoffer)
        #pprint(verify)
    if packet["op"]=="DHCPREQUEST":        
        print ("-- ура, господин выбрал меня любимой женой..и запрашивает IP адрес, который я уже предлагал..");
        print (packet["RequestedIpAddress"])
        print ("--делаем ему DHCPACK (да, всё так!)")
        packetack=dhcp_parse_packet.CreateDHCPACK(packet)
        pprint(packetack)
        udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        rz=udp_socket.sendto(packetack, ("255.255.255.255",68))
        
while True:
    data, addr = udp_socket.recvfrom(65000)
    print ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Новый UDP пакет пришел !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")    
    thread = threading.Thread(target=PacketWork, args=(data,addr,)).start()	
    while threading.active_count() >ThreadLimit:
       time.sleep(1)           
  
udp_socket.close()

dhcp_parse_packet.py:

# coding=utf-8
# Сервер DHCP на Python 3.5 с коннектором к БД MySQL
# Данный код создан и распространяется по лицензии GPL v3
# Изначальный автор данного кода - Грибов Павел
# http://грибовы.рф

import socket
from struct import *
from pprint import pprint

# парсим входящий пакет
def parsepacketIn(data): 
 try:
    print("Len data:",len(data))
    op="unknown"
    if data[0]==1: op="DHCPDISCOVER/DHCPREQUEST";
    if data[0]==2: op="DHCPOFFER/DHCPACK";    
    if data[1]==1: htype="MAC" 
    else : htype="unknown"    
    hlen=data[2]
    hops=data[3]
    xidhex=hex(data[4])[2:]+hex(data[5])[2:]+hex(data[6])[2:]+hex(data[7])[2:]
    xidbyte=data[4:8]
    secs=data[8]*256+data[9];
    flags=pack('BB',data[10],data[11])
    ciaddr=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));    
    siaddr=socket.inet_ntoa(pack('BBBB',data[16],data[17],data[18],data[19]));    
    giaddr=socket.inet_ntoa(pack('BBBB',data[20],data[21],data[22],data[23]));   
    chaddr=data[28:34].hex()
    magic_cookie=data[236:240]    
    print("Magic:",magic_cookie[0],magic_cookie[1],magic_cookie[2],magic_cookie[3])
    res={"op":op,"htype":htype,"hlen":hlen,"hops":hops,"xidbyte":xidbyte,"xidhex":xidhex,"secs":secs,"flags":flags,"ciaddr":ciaddr,"siaddr":siaddr,"giaddr":giaddr,"chaddr":chaddr,"magic_cookie":magic_cookie}        
    if magic_cookie==b'c\x82Sc':                
        # парсим опции
        print("--парсим опции");
        options=data[240:len(data)]
        print("Options:",options);        
        res["gpoz"]=240;
        while res["gpoz"]<len(data):
            print("Option:",data[res["gpoz"]]," hex",hex(data[res["gpoz"]])[2:])
            res=FindOptions(data,res)
            res["gpoz"]=res["gpoz"]+1
        res["result"]=True
    else:    
        res["result"]=False        
 except IndexError:
    res["result"]=False
 return res

# находим опции в пакете
def FindOptions(data,res):
    #Тип запроса
    if data[res["gpoz"]]==53: 
        res["option53"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]
        if data[res["gpoz"]+2]==1: res["op"]="DHCPDISCOVER"
        if data[res["gpoz"]+2]==3: res["op"]="DHCPREQUEST"
        if data[res["gpoz"]+2]==2: res["op"]="DHCPOFFER"
        if data[res["gpoz"]+2]==4: res["op"]="DHCPOFFER"
        if data[res["gpoz"]+2]==5: res["op"]="DHCPACK"
        res["gpoz"]=res["gpoz"]+ln+1;
        return res
    #MAC address клиента
    if data[res["gpoz"]]==61:                 
        res["option61"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]
        htype=data[res["gpoz"]+2]
        res["HType"]="unknown";
        if htype==1:res["HType"]="Ethernet";        
        res["ClientMacAddress"]=data[res["gpoz"]+3:res["gpoz"]+2+ln].hex()
        res["ClientMacAddressByte"]=data[res["gpoz"]+3:res["gpoz"]+2+ln]
        res["gpoz"]=res["gpoz"]+ln+1;
        return res
    #DHCP Auto
    if data[res["gpoz"]]==116:                 
        res["option116"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]
        res["DHCPAUTO"]=True;
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    #HostName - имя железки
    if data[res["gpoz"]]==12:                 
        res["option12"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]
        res["HostName"]=data[res["gpoz"]+2:res["gpoz"]+ln+2]
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    #Vendor - производитель
    if data[res["gpoz"]]==60:                 
        res["option60"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]
        res["Vendor"]=data[res["gpoz"]+2:res["gpoz"]+ln+2]
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    #Request List - список чего запрашивает железка
    if data[res["gpoz"]]==55:                 
        res["option55"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]
        preq=0;
        while preq<ln:
            if data[res["gpoz"]+2+preq]==1:res["ReqListSubnetMask"]=True;
            if data[res["gpoz"]+2+preq]==15:res["ReqListDomainName"]=True;
            if data[res["gpoz"]+2+preq]==3:res["ReqListRouter"]=True;
            if data[res["gpoz"]+2+preq]==6:res["ReqListDNS"]=True;
            if data[res["gpoz"]+2+preq]==31:res["ReqListPerfowmRouterDiscover"]=True;
            if data[res["gpoz"]+2+preq]==33:res["ReqListStaticRoute"]=True;
            if data[res["gpoz"]+2+preq]==43:res["ReqListVendorSpecInfo"]=43;
            preq=preq+1        
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # Запрошенный IP адрес
    if data[res["gpoz"]]==50:                 
        res["option50"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["RequestedIpAddress"]=socket.inet_ntoa(pack('BBBB',data[res["gpoz"]+2],data[res["gpoz"]+3],data[res["gpoz"]+4],data[res["gpoz"]+5]));    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # IP DHCP сервера
    if data[res["gpoz"]]==54:                 
        res["option54"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["DHCPServerIP"]=socket.inet_ntoa(pack('BBBB',data[res["gpoz"]+2],data[res["gpoz"]+3],data[res["gpoz"]+4],data[res["gpoz"]+5]));    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # IP Lease Time
    if data[res["gpoz"]]==51:                 
        res["option51"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["DHCPLeaseTime"]=data[res["gpoz"]+2]*256*256*256*256+data[res["gpoz"]+3]*256*256+data[res["gpoz"]+4]*256+data[res["gpoz"]+5];    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # Subnet Mask
    if data[res["gpoz"]]==1:                 
        res["option1"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["SubnetMask"]=socket.inet_ntoa(pack('BBBB',data[res["gpoz"]+2],data[res["gpoz"]+3],data[res["gpoz"]+4],data[res["gpoz"]+5]));    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # Router
    if data[res["gpoz"]]==3:                 
        res["option3"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["Router"]=socket.inet_ntoa(pack('BBBB',data[res["gpoz"]+2],data[res["gpoz"]+3],data[res["gpoz"]+4],data[res["gpoz"]+5]));    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # DNS
    if data[res["gpoz"]]==6:                 
        res["option6"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["DNS"]=socket.inet_ntoa(pack('BBBB',data[res["gpoz"]+2],data[res["gpoz"]+3],data[res["gpoz"]+4],data[res["gpoz"]+5]));    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    # NTPS сервер времени
    if data[res["gpoz"]]==42:                 
        res["option42"]=data[res["gpoz"]];
        ln=data[res["gpoz"]+1]        
        res["NTPS"]=socket.inet_ntoa(pack('BBBB',data[res["gpoz"]+2],data[res["gpoz"]+3],data[res["gpoz"]+4],data[res["gpoz"]+5]));    
        res["gpoz"]=res["gpoz"]+ln+1;                          
        return res
    
    return res

def padding0(cnt):
    res=b''
    pz=0
    while pz<cnt:        
        res=res+pack("B",0)
        pz=pz+1
    return res    
# Собираем предложение DHCPOFFER
def CreateDHCPOFFER(packet):
    print("---собираемся отвечать..")
    res=pack("B",2)     # тип ответа 
    res=res+pack("B",1) # тип железа Ethernet
    res=res+pack("B",6) # длина мас адреса
    res=res+pack("B",0) # количество шагов
    res=res+pack("BBBB",packet["xidbyte"][0],packet["xidbyte"][1],packet["xidbyte"][2],packet["xidbyte"][3]) # идентификатор посылки
    res=res+pack("BB",0,0) # сколько времени прошло?
    res=res+pack("BB",0,0) # флаги
    res=res+pack("BBBB",0,0,0,0) # кому отсылаем (всем)
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.19") # какой IP предлагает
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.71") # какой IP у DHCP сервера
    res=res+socket.inet_pton(socket.AF_INET, "0.0.0.0") # какой Relay
    res=res+pack("BBBBBB",packet["ClientMacAddressByte"][0],packet["ClientMacAddressByte"][1],packet["ClientMacAddressByte"][2],packet["ClientMacAddressByte"][3],packet["ClientMacAddressByte"][4],packet["ClientMacAddressByte"][5]) # MAC получателя
    res=res+padding0(202);
    res=res+packet["magic_cookie"]; # магическое число
    res=res+pack("BBB",53,1,2) # 53 опция, обозначем, что это пакет OFFER (предложение)
    res=res+pack("BB",54,4) # 54 опция, кто дает адрес?
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.71")
    res=res+pack("BBBBBB",51,4,0,0,1,255) # 51 опция, время жизни адреса
    res=res+pack("BB",1,4) # 1 опция Mask
    res=res+socket.inet_pton(socket.AF_INET, "255.255.255.0")
    res=res+pack("BB",3,4) # 1 опция Router
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.1")
    res=res+pack("BB",6,4) # 6 опция DNS
    res=res+socket.inet_pton(socket.AF_INET, "8.8.8.8")
    res=res+pack("B",255) # END
    res=res+padding0(28);
    #print ("LEN:",len(res));
    return res
def CreateDHCPACK(packet):
    print("---собираемся отвечать..")
    res=pack("B",2)     # тип ответа 
    res=res+pack("B",1) # тип железа Ethernet
    res=res+pack("B",6) # длина мас адреса
    res=res+pack("B",0) # количество шагов
    res=res+pack("BBBB",packet["xidbyte"][0],packet["xidbyte"][1],packet["xidbyte"][2],packet["xidbyte"][3]) # идентификатор посылки
    res=res+pack("BB",0,0) # сколько времени прошло?
    res=res+pack("BB",0,0) # флаги
    res=res+pack("BBBB",0,0,0,0) # кому отсылаем (всем)
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.19") # какой IP предлагает
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.71") # какой IP у DHCP сервера
    res=res+socket.inet_pton(socket.AF_INET, "0.0.0.0") # какой Relay
    res=res+pack("BBBBBB",packet["ClientMacAddressByte"][0],packet["ClientMacAddressByte"][1],packet["ClientMacAddressByte"][2],packet["ClientMacAddressByte"][3],packet["ClientMacAddressByte"][4],packet["ClientMacAddressByte"][5]) # MAC получателя
    res=res+padding0(202);
    res=res+packet["magic_cookie"]; # магическое число
    res=res+pack("BBB",53,1,5) # 53 опция, обозначем, что это пакет ACK (подтверждение)
    res=res+pack("BB",54,4) # 54 опция, кто дает адрес?
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.71")
    res=res+pack("BBBBBB",51,4,0,0,1,255) # 51 опция, время жизни адреса
    res=res+pack("BB",1,4) # 1 опция Mask
    res=res+socket.inet_pton(socket.AF_INET, "255.255.255.0")
    res=res+pack("BB",3,4) # 1 опция Router
    res=res+socket.inet_pton(socket.AF_INET, "192.168.0.1")
    res=res+pack("BB",6,4) # 6 опция DNS
    res=res+socket.inet_pton(socket.AF_INET, "8.8.8.8")
    res=res+pack("B",255) # END
    res=res+padding0(28);
    #print ("LEN:",len(res));
    return res

 

Комментарии:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.