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. Scrapy使用示例

    很多网站都提供了浏览者本地的天气信息,这些信息是如何获取到的呢,方法有很多种,大多是利用某些网站提供的天气api获取的,也有利用爬虫采集的.本文就介绍如何用Scrapy来采集天气信息(从新浪天气频道采 ...

  2. python学习笔记四:lambda表达式和switch

    一.定义 lambda arg1,arg2... : returnValue 二.示例 #!/usr/bin/python def f(x,y): return x*y print f(2,3) g ...

  3. activiti并发多实例子流程任务处理

    一直在搞工作流(activiti),总结一下关于工作流(activiti)中同时并发处理多个子流程的操作方法. 先说下我要实现的业务: 1.办公室发通知(在系统申报页面上,勾选科室,被选中的科室执行第 ...

  4. winform对图片进行灰度处理

    //图片进行灰度处理 //originalImage为原图像 返回灰度图像 private Bitmap GrayImage(Bitmap originalImage) { ImageAttribut ...

  5. Python全栈工程师 (exercises)

    # 1:给定一个数,判断他是正数,负数,还是0 a = int(input("请输入一该个整数")) if a == 0: print(a, "是0") eli ...

  6. java中newInstance和new(转)

    在Java开发特别是数据库开发中,经常会用到Class.forName( )这个方法.通过查询Java Documentation我们会发现使用Class.forName( )静态方法的目的是为了动态 ...

  7. STL之priority_queue使用简介

    优先队列容器也是一种从一端入队,另一端出对的队列.不同于一般队列的是,队列中最大的元素总是位于队首位置,因此,元素的出对并非按照先进先出的要求,将最先入队的元素出对,而是将当前队列中的最大元素出对. ...

  8. 2013 ACM/ICPC Asia Regional Changsha Online – C题 Color Representation Conversion (坑爹模拟题)

    题意:给你三种颜色表示模式,RGB,HSV和HSL,实现任意模式之间两两转化. 1.最好别看题目中给的转化公式描述,我觉得叙述的一点也不清楚,看维基百科,把维基百科上的公式一句一句翻译过来就好 2.在 ...

  9. jmeter的build.xml

    <?xml version="1.0"?><!--   Licensed to the Apache Software Foundation (ASF) unde ...

  10. BZOJ3622 已经没有什么好害怕的了 【dp + 二项式反演】

    题目链接 BZOJ3622 题解 既已开题 那就已经没有什么好害怕的了 由题目中奇怪的条件我们可以特判掉\(n - k\)为奇数时答案为\(0\) 否则我们要求的就是糖果大于药片恰好有\(\frac{ ...