DHCP实现 / DHCP Implement


目录

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

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

1 DHCP 服务器建立过程

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

 import socket
import struct
import binascii
import logging
from threading import Thread
from dhcp_offer import Offer logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] %(threadName)s: %(message)s') class DHCPServer():
"""
This class implements parts of RFC-2131
Only DHCPDISCOVER and DHCPREQUEST allowed
"""
def __init__(self, boot_file=None, server_ip=None, offer_ip=None, tftp_ip=None):
Thread.__init__(self)
self._port = 67
self._boot_file = boot_file
self._file_index = 0
self._offer_ip = offer_ip
self._tftp_ip = tftp_ip
self.server_ip = server_ip @property
def server_ip(self):
return self._server_ip @server_ip.setter
def server_ip(self, server_ip):
self._server_ip = server_ip
self.send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
self.send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.send_socket.bind((self._server_ip, self._port)) @property
def boot_file(self):
return self._boot_file @boot_file.setter
def boot_file(self, boot_file):
if not isinstance(boot_file, list):
boot_file = [boot_file]
self._boot_file = boot_file
self._file_index = 0 @property
def offer_ip(self):
return self._offer_ip @offer_ip.setter
def offer_ip(self, offer_ip):
self._offer_ip = offer_ip def check_msg(self, m):
is_valid, msg_type, select_ip = False, None, None
if (m[0] == b'\x01' and
m[1] == b'\x01' and
m[2] == b'\x06' and
m[3] == b'\x00' and
m[10:12] == [b'\x00', b'\x00'] and
m[12:16] == [b'\x00', b'\x00', b'\x00', b'\x00'] and
m[16:20] == [b'\x00', b'\x00', b'\x00', b'\x00'] and
m[20:24] == [b'\x00', b'\x00', b'\x00', b'\x00'] and
m[236:240] == [b'\x63', b'\x82', b'\x53', b'\x63']
):
logging.warning('Valid DHCP message')
# Valid DHCPDISCOVER
opt = (x for x in m[240:])
while opt:
try:
func_code = next(opt)
if func_code == b'\x00':
break
length = next(opt)
items = b''
for i in range(ord(length)):
items += next(opt)
except StopIteration:
break
else:
if func_code == b'\x35' and length == b'\x01':
if items == b'\x01':
logging.warning('DHCP Discover')
msg_type = 'DSCV'
is_valid = True
if items == b'\x03':
logging.warning('DHCP Request')
msg_type = 'RQST' # Assure DHCP server selected
if func_code == b'\x36' and msg_type == 'RQST':
logging.warning('DHCP Server Identifier check')
select_ip = socket.inet_ntoa(items) # Double check DHCP offer ip
if func_code == b'\x32' and select_ip == self._server_ip:
offer_ip = socket.inet_ntoa(items)
if offer_ip == self._offer_ip:
is_valid = True
else:
logging.warning('Offer ip double check failed') return is_valid, msg_type def serve_forever(self): self.recv_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.recv_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
self.recv_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.recv_socket.bind(('', self._port)) if self._boot_file:
if self._file_index >= len(self._boot_file):
self._file_index = 0 def handle_msg(msg, addr):
send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
send_socket.bind((self._server_ip, self._port))
m = list(struct.unpack('c'*len(msg), msg))
is_valid, msg_type = self.check_msg(m)
if is_valid:
logging.warning('Valid %s message, try to response' % msg_type)
pass_sec = ord(m[8]) * 256 + ord(m[9])
transaction_id = ''.join(['%02x' % ord(x) for x in m[4:8]])
client_mac_id = ':'.join(['%02x' % ord(x) for x in m[28:34]])
if msg_type:
offer = Offer(transaction_id=transaction_id,
client_ip_offer=self._offer_ip,
server_ip=self._server_ip,
client_mac_id=client_mac_id,
file_path=self._boot_file,
pass_sec=pass_sec,
msg_type=msg_type,
tftp_ip = self._tftp_ip)
send_socket.sendto(offer.packet, ('<broadcast>', 68))
self._file_index += 1
logging.warning('Respone done')
else:
logging.warning('Invalid message, discard...')
send_socket.close() while True:
logging.warning('Waiting discovery...')
msg, addr = self.recv_socket.recvfrom(8192)
logging.warning('Receive message from %s, port %s' % addr)
handler = Thread(target=handle_msg, args=(msg, addr))
handler.start() if __name__ == '__main__':
dhcp = DHCPServer(server_ip='127.0.0.1', offer_ip='127.0.0.10')
dhcp.serve_forever()

2 DHCP 报文加码实现过程

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

 import binascii
import struct
import socket class Offer():
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):
SERVER_NAME = ''
self._server_ip = server_ip
self._offer_ip = client_ip_offer
self._lease_time = lease_time
if not tftp_ip:
tftp_ip = server_ip
self._tftp_ip = tftp_ip
if not file_path:
file_path = ''
pass_sec = struct.pack('!H', pass_sec)
client_mac_id = binascii.unhexlify(client_mac_id.replace(':', ''))
transaction_id = binascii.unhexlify(transaction_id) self.packet = b''
self.packet += b'\x02' # op
self.packet += b'\x01' # htype
self.packet += b'\x06' # hlen
self.packet += b'\x00' # hops
self.packet += transaction_id
self.packet += pass_sec # secs
self.packet += b'\x00\x00' # flags
self.packet += b'\x00\x00\x00\x00' # current client ip
self.packet += socket.inet_aton(client_ip_offer) # offer ip
self.packet += socket.inet_aton(server_ip) # server ip
self.packet += b'\x00\x00\x00\x00' # gateway ip
self.packet += client_mac_id # client mac id
self.packet += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # client mac id padding
self.packet += SERVER_NAME.encode('utf-8')
self.packet += b'\x00'*(64-len(SERVER_NAME))
self.packet += file_path.encode('utf-8')
self.packet += b'\x00'*(128-len(file_path))
self.packet += self.optional(msg_type) def optional(self, msg_type):
magic = b'\x63\x82\x53\x63'
opt = b''
# Message type
if msg_type == 'DSCV':
opt += self.encode_int(53, 1, 2)
if msg_type == 'RQST':
opt += self.encode_int(53, 1, 5)
# Server identifier
opt += self.encode_ip(54, 4, self._server_ip)
# Subnet mask
opt += self.encode_ip(1, 4, '255.255.255.0')
# Router
opt += self.encode_ip(3, 4, '127.0.0.1')
# DNS server
opt += self.encode_ip(6, 4, '127.0.0.1')
# NetBios server
opt += self.encode_ip(44, 4, '127.0.0.1')
# IP address lease time
opt += self.encode_int(51, 4, self._lease_time)
# Extend lease time T1
opt += self.encode_int(58, 4, int(self._lease_time*0.5))
# Extend lease time T2
opt += self.encode_int(59, 4, int(self._lease_time*0.8))
# Log server
opt += self.encode_ip(7, 4, '127.0.0.1')
# TFTP server name
opt += bytes([66, 11]) + self._tftp_ip.encode()
# Tail
# TODO: find out why a b'\xff' for end
opt += b'\xff'
return magic+opt def encode_int(self, func, length, item):
m = {1: '!B', 2: '!H', 4: '!I'}
s = b''
s += (bytes([func, length]) + struct.pack(m[length], item))
return s def encode_ip(self, func, length, item):
s = b''
s += bytes([func, length])
s += socket.inet_aton(item)
return s

相关阅读


1. DHCP 理论

2. 生成器

Python的网络编程[4] -> DHCP 协议[1] -> DHCP 的 Python 实现的更多相关文章

  1. Python的网络编程[3] -> BOOTP 协议[1] -> BOOTP 的 Python 实现

    BOOTP实现 / BOOTP Implement 目录 BOOTP 的服务器建立过程 BOOTP 的客户端建立过程 Note: 理论部分请参考文末相关阅读链接 1 BOOTP 的服务器建立过程 服务 ...

  2. Python的网络编程[2] -> TFTP 协议[1] -> TFTP 的 Python 实现

    TFTP实现 / TFTP Implement 目录 TFTP 的服务器建立过程 TFTP 的客户端建立过程 1 TFTP 的服务器建立过程 服务器建立步骤主要有: (1)      设定服务器IP和 ...

  3. Python的网络编程[3] -> BOOTP 协议[0] -> BOOTP 的基本理论

    BOOTP协议 / BOOTP Protocol 目录 基本理论 BOOTP 与 DHCP 通信流程 数据报文格式 报文加解码实现 1. 基本理论 / Basic Theory BOOTP(Boots ...

  4. Python的网络编程[6] -> Modbus 协议 -> Modbus 的基本理论与 Python 实现

    Modbus协议 / Modbus Protocol 目录 Modbus 协议简介 Modbus RTU协议 Modbus TCP协议与 Python 实现 Modbus 功能码 Modbus TCP ...

  5. Python的网络编程[1] -> FTP 协议[0] -> FTP 的基本理论

    FTP协议 / FTP Protocol FTP全称为File Transfer Protocol(文件传输协议),常用于Internet上控制文件的双向传输,常用的操作有上传和下载.基于TCP/IP ...

  6. Python的网络编程[2] -> TFTP 协议[0] -> TFTP 的基本理论

    TFTP 的基本理论 目录 通信流程 数据报文格式 传输终结 异常处理 数据丢失和超时 TFTP(Trivial File Transfer Protocol,简单文件传输协议)是UDP协议族中的一个 ...

  7. Python的网络编程[1] -> FTP 协议[2] -> 使用 ftplib 建立 FTP 客户端

    使用 ftplib 建立 FTP 客户端 用于建立FTP Client,与 pyftplib 建立的 Server 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 客户端 1. 模块信息 1 ...

  8. Python的网络编程[1] -> FTP 协议[1] -> 使用 pyftplib 建立 FTP 服务器

    使用 pyftplib 建立 FTP 服务器 pyftplib 主要用于建立 FTP Server,与 ftplib 建立的 Client 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 服 ...

  9. python之网络编程

    本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...

  10. Python高级网络编程系列之第一篇

    在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...

随机推荐

  1. shell脚本从文件夹中递归提取文件

    需求 前两天碰到需要在十层左右的文件夹中提取文件的需求,于是写了此脚本. 如下面这样的文件结构: dir1 ├── a │ ├── b │ │ └── file1 │ └── file2 ├── c ...

  2. 【Linear Regression】林轩田机器学习基石

    这一节开始讲基础的Linear Regression算法. (1)Linear Regression的假设空间变成了实数域 (2)Linear Regression的目标是找到使得残差更小的分割线(超 ...

  3. 孤荷凌寒自学python第五十九天尝试使用python来读访问远端MongoDb数据服务

    孤荷凌寒自学python第五十九天尝试使用python来读访问远端MongoDb数据服务 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第五天.今天的感觉是,mongoDB数 ...

  4. DevExpress的GridControl控件设置自定义显示方法

    比如要显示性别为字符串,数据库中保存为数值(1:男,2:女,3:未知). 方法一: 点击控件上的"Run Designer"按钮,进入设计界面. 选择“Columns", ...

  5. POJ A-Wireless Network

    http://poj.org/problem?id=2236 An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated ...

  6. PHP文件操作函数及文件指针理解

    知识点: 一.fopen(),文件打开函数,读写参数有: 1.R  : 只读,指针在文件开头 2.r+:读写,指针同上 3.W :只写,写入前会删除文件内容,然后指针回到文件开头,文件不存在则创建 4 ...

  7. 开头什么的肯定要自我介绍然后把它扔到置顶咯>_<~

    大家嚎,我是NanoApe~ 现在高一,是个OIer.音游狗和一个爱着二次元的萌汉子妹子,欢迎前来勾搭>_<~ 最近就是要冲省队啦~~~~加油! 扣扣号:879006461 Weibo:伪 ...

  8. [codeforces] 585D Lizard Era: Beginning || 双向dfs

    原题 有n(n<=2)个任务和三个人,每次任务给出每个人能得到的值,每次任务选两个人,使n个任务结束后三个人得到的值是一样的.输出每次要派哪两个人,如果不行输出Impossible. n< ...

  9. POJ 2074 | 线段相交

    #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #defi ...

  10. UVALive4374 Drive through MegaCity

    题目戳这里. 首先我们对坐标进行离散化,有用的点就变成了\(O(N)\)个.我们假设\(A\)点\(B\)的右边(从\(A\)往\(B\)跑和从\(B\)往\(A\)跑等价),然后我们很容易发现不会往 ...