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. Linux利用OneinStack搭建环境

    OneinStack官方网站:https://oneinstack.com 介绍 OneinStack支持以下数种环境组合: LNMP(Linux + Nginx+ MySQL+ PHP) LAMP( ...

  2. Nova 如何统计 OpenStack 资源

    1.云计算的本质在于将硬件资源软件化,以达到快速按需交付的效果,最基本的计算.存储和网络基础元素并没有因此改变.就计算而言,CPU.RAM 和 DISK等依旧是必不可少的核心资源. 从源代码和数据库相 ...

  3. Android事件分发机制详解(2)----分析ViewGruop的事件分发

    首先,我们需要 知道什么是ViewGroup,它和普通的View有什么区别? ViewGroup就是一组View的集合,它包含很多子View和ViewGroup,是Android 所有布局的父类或间接 ...

  4. poj 3436 网络流构图经典

    ACM Computer Factory Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6012   Accepted: 2 ...

  5. Linux下磁盘管理

    设置密码mkpasswdmkpasswd -s 0mkpasswd -s 0 -1 15 规定密码的长度 1. 查看磁盘或者目录的容量df 查看磁盘各分区使用情况 不加参数以k为单位 df -i in ...

  6. flex弹性布局的基本介绍

    最近开始做元素排列比较复杂的项目,同时需要各种型号手机的适配,我发现以前所掌握的盒子模型.display.position.float等已经不能满足我的需求了, 于是开始着重学习flex弹性布局并运用 ...

  7. 解决:dubbo找不到dubbo.xsd报错

    构建dubbo项目的时候会遇到: Multiple annotations found at this line: - cvc-complex-type.2.4.c: The matching wil ...

  8. 2016华中农业大学预赛 E 想法题

    Problem E: Balance Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 205  Solved: 64[Submit][Status][We ...

  9. POJ3680:Intervals(离散化+最大流最小费用)

    Intervals Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 9320   Accepted: 4014 题目链接:ht ...

  10. Windows7下的Run运行命令一览表

    按住Windows键(就是左边Ctrl和Alt之间那个印windows徽标的键,简称Win键)+R,即可弹出运行对话框,在里面输入黑体字符即可运行相应程序.相比XP这次新增了不少新东西. 添加/删除程 ...