Python的网络编程[4] -> DHCP 协议[1] -> DHCP 的 Python 实现
DHCP实现 / DHCP Implement
目录
下面介绍建立一个简单的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 实现的更多相关文章
- Python的网络编程[3] -> BOOTP 协议[1] -> BOOTP 的 Python 实现
BOOTP实现 / BOOTP Implement 目录 BOOTP 的服务器建立过程 BOOTP 的客户端建立过程 Note: 理论部分请参考文末相关阅读链接 1 BOOTP 的服务器建立过程 服务 ...
- Python的网络编程[2] -> TFTP 协议[1] -> TFTP 的 Python 实现
TFTP实现 / TFTP Implement 目录 TFTP 的服务器建立过程 TFTP 的客户端建立过程 1 TFTP 的服务器建立过程 服务器建立步骤主要有: (1) 设定服务器IP和 ...
- Python的网络编程[3] -> BOOTP 协议[0] -> BOOTP 的基本理论
BOOTP协议 / BOOTP Protocol 目录 基本理论 BOOTP 与 DHCP 通信流程 数据报文格式 报文加解码实现 1. 基本理论 / Basic Theory BOOTP(Boots ...
- Python的网络编程[6] -> Modbus 协议 -> Modbus 的基本理论与 Python 实现
Modbus协议 / Modbus Protocol 目录 Modbus 协议简介 Modbus RTU协议 Modbus TCP协议与 Python 实现 Modbus 功能码 Modbus TCP ...
- Python的网络编程[1] -> FTP 协议[0] -> FTP 的基本理论
FTP协议 / FTP Protocol FTP全称为File Transfer Protocol(文件传输协议),常用于Internet上控制文件的双向传输,常用的操作有上传和下载.基于TCP/IP ...
- Python的网络编程[2] -> TFTP 协议[0] -> TFTP 的基本理论
TFTP 的基本理论 目录 通信流程 数据报文格式 传输终结 异常处理 数据丢失和超时 TFTP(Trivial File Transfer Protocol,简单文件传输协议)是UDP协议族中的一个 ...
- Python的网络编程[1] -> FTP 协议[2] -> 使用 ftplib 建立 FTP 客户端
使用 ftplib 建立 FTP 客户端 用于建立FTP Client,与 pyftplib 建立的 Server 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 客户端 1. 模块信息 1 ...
- Python的网络编程[1] -> FTP 协议[1] -> 使用 pyftplib 建立 FTP 服务器
使用 pyftplib 建立 FTP 服务器 pyftplib 主要用于建立 FTP Server,与 ftplib 建立的 Client 进行通信. 快速导航 1. 模块信息 2. 建立 FTP 服 ...
- python之网络编程
本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 消息传递(管道.FIFO.消息队列) 同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 共享内存(匿名的和具名的) 远程过程调用 ...
- Python高级网络编程系列之第一篇
在上一篇中我们简单的说了一下Python中网络编程的基础知识(相关API就不解释了),其中还有什么细节的知识点没有进行说明,如什么是TCP/IP协议有几种状态,什么是TCP三次握手,什么是TCP四次握 ...
随机推荐
- 【Neural Network】林轩田机器学习技法
首先从单层神经网络开始介绍 最简单的单层神经网络可以看成是多个Perception的线性组合,这种简单的组合可以达到一些复杂的boundary. 比如,最简单的逻辑运算AND OR NOT都可以由多 ...
- JAVA中的类
节选自:http://www.cnblogs.com/dolphin0520/p/3811445.html 1. 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一 ...
- Python 3基础教程13-写入文件
前面介绍了函数,这篇我们就利用Python 内构函数open来写入字符串到txt文件里. 直接看demo.py 这里有一个小问题,如果我要输入时中文到txt文件会报unicode错误,暂时没法解决.
- NTP学习
NTP(The Network Time Protocol),本以为是一个非常简单的协议,但是看了百度百科和ntp.org的介绍后,我发现我错了. 这个看似简单的协议存在一个很关键也是非常重要的问题- ...
- 课时1:我和python的第一次亲密接触
目录: 一.Python3的下载与安装 二.从IDIE启动Python 三.尝试点新的东西 四.为什么会这样? 五.课时01课后习题及答案 ============================== ...
- Android之SQLite总结
SQLite 是一个轻量级的数据库,常用于各种嵌入式设备当中.android 提供了SQLiteOpenHelper的抽象类用于帮助开发数据库.在实际使用中经常定义一个类继承SQLiteOpenHel ...
- 软件工程概论课堂测试一————添加新课程(web)
设计思想 三个文件Class_add.java add.jsp addInput.jsp Class_add.java : 内封装方法:连接数据库.向数据库添加课程信息.判断非合理的输入情况.判断 ...
- mininet、floodlight在第一次SDN上机作业中出现的一些问题
mininet.floodlight在第一次SND上机作业中出现的一些问题 首先给出链接 VMware安装 mininet安装 floodlight安装及问题,各个版本Ubuntu SDN第一次上机作 ...
- Crash的游戏 [组合+递推]
题面 思路 问题转化 这个问题的核心在于,我们需要把"加入一个球.拿出一个球"这两个操作转化一下 因为显然两个操作同时进行的话,我们没有办法从单纯的组合意义去分析 我们首先把$m$ ...
- [SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]
题面 传送门 思路 $LCT$ 我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~ 那么我们尝试把$Access$操作魔改成本题中的涂色 我们令$LCT$中的每一个$splay$链代 ...