Python的网络编程[4] -> DHCP 协议 -> DHCP 的 Python 实现,,DHCP实现 / D


DHCP实现 / DHCP Implement


目录

DHCP 服务器建立过程DHCP 报文加码实现过程

下面介绍建立一个简单的DHCP服务器,主要用于对基本的DHCP请求进行响应,目前只提供一个IP为客户端使用,实现最基本的通信示例。理论内容可参考 DHCP 理论部分。

1 DHCP 服务器建立过程

首先是基本服务器的建立,这个服务器实现了最基本的对DISCOVER和REQUEST报文的响应,在验证时会对魔术字进行验证,此处未对BOOTP进行处理,验证通过后会对Options字段进行验证,此处利用生成器来对字段进行获取,最终处理完所有信息后结束。

  1 import socket  2 import struct  3 import binascii  4 import logging  5 from threading import Thread  6 from dhcp_offer import Offer  7   8 logging.basicConfig(level=logging.DEBUG, format=‘[%(asctime)s] %(threadName)s: %(message)s‘)  9  10 class DHCPServer(): 11     """ 12     This class implements parts of RFC-2131 13     Only DHCPDISCOVER and DHCPREQUEST allowed 14     """ 15     def __init__(self, boot_file=None, server_ip=None, offer_ip=None, tftp_ip=None): 16         Thread.__init__(self) 17         self._port = 67 18         self._boot_file = boot_file 19         self._file_index = 0 20         self._offer_ip = offer_ip 21         self._tftp_ip = tftp_ip 22         self.server_ip = server_ip 23  24     @property 25     def server_ip(self): 26         return self._server_ip 27  28     @server_ip.setter 29     def server_ip(self, server_ip): 30         self._server_ip = server_ip 31         self.send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 32         self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) 33         self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 34         self.send_socket.bind((self._server_ip, self._port)) 35  36     @property 37     def boot_file(self): 38         return self._boot_file 39  40     @boot_file.setter 41     def boot_file(self, boot_file): 42         if not isinstance(boot_file, list): 43             boot_file = [boot_file] 44         self._boot_file = boot_file 45         self._file_index = 0 46  47     @property 48     def offer_ip(self): 49         return self._offer_ip 50  51     @offer_ip.setter 52     def offer_ip(self, offer_ip): 53         self._offer_ip = offer_ip 54  55     def check_msg(self, m): 56         is_valid, msg_type, select_ip = False, None, None 57         if (m[0] == b‘\x01‘ and 58             m[1] == b‘\x01‘ and 59             m[2] == b‘\x06‘ and 60             m[3] == b‘\x00‘ and 61             m[10:12] == [b‘\x00‘, b‘\x00‘] and 62             m[12:16] == [b‘\x00‘, b‘\x00‘, b‘\x00‘, b‘\x00‘] and 63             m[16:20] == [b‘\x00‘, b‘\x00‘, b‘\x00‘, b‘\x00‘] and 64             m[20:24] == [b‘\x00‘, b‘\x00‘, b‘\x00‘, b‘\x00‘] and 65             m[236:240] == [b‘\x63‘, b‘\x82‘, b‘\x53‘, b‘\x63‘] 66             ): 67             logging.warning(‘Valid DHCP message‘) 68             # Valid DHCPDISCOVER 69             opt = (x for x in m[240:]) 70             while opt: 71                 try: 72                     func_code = next(opt) 73                     if func_code == b‘\x00‘: 74                         break 75                     length = next(opt) 76                     items = b‘‘ 77                     for i in range(ord(length)): 78                         items += next(opt) 79                 except StopIteration: 80                     break 81                 else: 82                     if func_code == b‘\x35‘ and length == b‘\x01‘: 83                         if items == b‘\x01‘: 84                             logging.warning(‘DHCP Discover‘) 85                             msg_type = ‘DSCV‘ 86                             is_valid = True 87                         if items == b‘\x03‘: 88                             logging.warning(‘DHCP Request‘) 89                             msg_type = ‘RQST‘ 90  91                     # Assure DHCP server selected 92                     if func_code == b‘\x36‘ and msg_type == ‘RQST‘: 93                         logging.warning(‘DHCP Server Identifier check‘) 94                         select_ip = socket.inet_ntoa(items) 95  96                     # Double check DHCP offer ip 97                     if func_code == b‘\x32‘ and select_ip == self._server_ip: 98                         offer_ip = socket.inet_ntoa(items) 99                         if offer_ip == self._offer_ip:100                             is_valid = True101                         else:102                             logging.warning(‘Offer ip double check failed‘)103 104         return is_valid, msg_type105 106     def serve_forever(self):107 108         self.recv_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)109         self.recv_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)110         self.recv_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)111         self.recv_socket.bind((‘‘, self._port))112         113         if self._boot_file:114             if self._file_index >= len(self._boot_file):115                 self._file_index = 0116 117         def handle_msg(msg, addr):118             send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)119             send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)120             send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)121             send_socket.bind((self._server_ip, self._port))122             m = list(struct.unpack(‘c‘*len(msg), msg))123             is_valid, msg_type = self.check_msg(m)124             if is_valid:125                 logging.warning(‘Valid %s message, try to response‘ % msg_type)126                 pass_sec = ord(m[8]) * 256 + ord(m[9])127                 transaction_id = ‘‘.join([‘%02x‘ % ord(x) for x in m[4:8]])128                 client_mac_id = ‘:‘.join([‘%02x‘ % ord(x) for x in m[28:34]])129                 if msg_type:130                     offer = Offer(transaction_id=transaction_id,131                                   client_ip_offer=self._offer_ip,132                                   server_ip=self._server_ip,133                                   client_mac_id=client_mac_id,134                                   file_path=self._boot_file,135                                   pass_sec=pass_sec,136                                   msg_type=msg_type,137                                   tftp_ip = self._tftp_ip)138                     send_socket.sendto(offer.packet, (‘<broadcast>‘, 68))139                     self._file_index += 1140                 logging.warning(‘Respone done‘)141             else:142                 logging.warning(‘Invalid message, discard...‘)143             send_socket.close()144         145         while True:146             logging.warning(‘Waiting discovery...‘)147             msg, addr = self.recv_socket.recvfrom(8192)148             logging.warning(‘Receive message from %s, port %s‘ % addr)149             handler = Thread(target=handle_msg, args=(msg, addr))150             handler.start()151 152 if __name__ == ‘__main__‘:153     dhcp = DHCPServer(server_ip=‘127.0.0.1‘, offer_ip=‘127.0.0.10‘)154     dhcp.serve_forever()

2 DHCP 报文加码实现过程

Note: 此处为做示例,对网关等信息都设置为固定值,匹配服务器ip。

 1 import binascii 2 import struct 3 import socket 4  5 class Offer(): 6     def __init__(self, transaction_id, client_ip_offer, server_ip, client_mac_id, file_path, pass_sec, msg_type, tftp_ip=None, lease_time=172800): 7         SERVER_NAME = ‘‘ 8         self._server_ip = server_ip 9         self._offer_ip = client_ip_offer10         self._lease_time = lease_time11         if not tftp_ip:12             tftp_ip = server_ip13         self._tftp_ip = tftp_ip14         if not file_path:15             file_path = ‘‘16         pass_sec = struct.pack(‘!H‘, pass_sec)17         client_mac_id = binascii.unhexlify(client_mac_id.replace(‘:‘, ‘‘))18         transaction_id = binascii.unhexlify(transaction_id)19 20         self.packet = b‘‘21         self.packet += b‘\x02‘  # op22         self.packet += b‘\x01‘  # htype23         self.packet += b‘\x06‘  # hlen24         self.packet += b‘\x00‘  # hops25         self.packet += transaction_id26         self.packet += pass_sec # secs27         self.packet += b‘\x00\x00‘  # flags28         self.packet += b‘\x00\x00\x00\x00‘  # current client ip29         self.packet += socket.inet_aton(client_ip_offer) # offer ip30         self.packet += socket.inet_aton(server_ip)  # server ip31         self.packet += b‘\x00\x00\x00\x00‘  # gateway ip32         self.packet += client_mac_id # client mac id33         self.packet += b‘\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00‘  # client mac id padding34         self.packet += SERVER_NAME.encode(‘utf-8‘)35         self.packet += b‘\x00‘*(64-len(SERVER_NAME))36         self.packet += file_path.encode(‘utf-8‘)37         self.packet += b‘\x00‘*(128-len(file_path))38         self.packet += self.optional(msg_type)39 40     def optional(self, msg_type):41         magic = b‘\x63\x82\x53\x63‘42         opt = b‘‘43         # Message type44         if msg_type == ‘DSCV‘:45             opt += self.encode_int(53, 1, 2)46         if msg_type == ‘RQST‘:47             opt += self.encode_int(53, 1, 5)48         # Server identifier49         opt += self.encode_ip(54, 4, self._server_ip)50         # Subnet mask51         opt += self.encode_ip(1, 4, ‘255.255.255.0‘)52         # Router53         opt += self.encode_ip(3, 4, ‘127.0.0.1‘)54         # DNS server55         opt += self.encode_ip(6, 4, ‘127.0.0.1‘)56         # NetBios server57         opt += self.encode_ip(44, 4, ‘127.0.0.1‘)58         # IP address lease time59         opt += self.encode_int(51, 4, self._lease_time)60         # Extend lease time T161         opt += self.encode_int(58, 4, int(self._lease_time*0.5))62         # Extend lease time T263         opt += self.encode_int(59, 4, int(self._lease_time*0.8))64         # Log server65         opt += self.encode_ip(7, 4, ‘127.0.0.1‘)66         # TFTP server name67         opt += bytes([66, 11]) + self._tftp_ip.encode()68         # Tail69         # TODO: find out why a b‘\xff‘ for end70         opt += b‘\xff‘71         return magic+opt72 73     def encode_int(self, func, length, item):74         m = {1: ‘!B‘, 2: ‘!H‘, 4: ‘!I‘}75         s = b‘‘76         s += (bytes([func, length]) + struct.pack(m[length], item))77         return s78 79     def encode_ip(self, func, length, item):80         s = b‘‘81         s += bytes([func, length])82         s += socket.inet_aton(item)83         return s

相关阅读


1. DHCP 理论

2. 生成器

Python的网络编程[4] -> DHCP 协议 -> DHCP 的 Python 实现

评论关闭