• python socket编程4 - socket模拟dhcp服务器分析dhcp头部结构和option选项


    首先,使用 socket 创建一个IP v4 的 UDP 协议绑定在 67端口,实现最简单的 DHCP 服务端 。

    DHCP服务端监听DHCP客户端的广播请求。

    一、DHCP服务端部分代码和注释

    import logging
    import socket
    
    
    class SimplestDHCPServer:
    
        def __init__(self):
            self.server_socket = None    # DHCP 服务端 socket ,负责接受 DHCP 客户端的请求。 
            self._server_port = 67           # DHCP 服务端口为 67
    
        def start(self):
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            # socket.AF_INET  --> IPv4 Internet protocols
            # socket.SOCK_DGRAM  --> 数据报(无连接、不可靠、最大长度固定的消息)
            # 参见 《python socket编程1 - socket创建参数说明及参考规范》
            
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
            # socket.SOL_SOCKET --> 将参数作用在 socket 级别上。参见 《python socket编程3 - socket的选项》 level的取值范围
            # socket.SO_BROADCAST --> # 设置或获取广播标识。当选择此选项时, 数据报套接字接收向广播地址发送的数据包, 并且可以向广播地址发送数据包。参见 《python socket编程3 - socket的选项》 optName 设置选项的名称
            
            
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
            # socket.SOL_SOCKET --> 将参数作用在 socket 级别上。参见 《python socket编程3 - socket的选项》 level的取值范围
            # socket.SO_REUSEADDR --> 表示在一个 bind() 调用中允许重复使用本地地址。参见 《python socket编程3 - socket的选项》 optName 设置选项的名称
    
            self.server_socket.bind(('', self._server_port))
            while True:
                logging.warning('DHCP 已经启动...')
                msg, addr = self.server_socket.recvfrom(8192)
                print(msg, addr)
    
    
    if __name__ == '__main__':
        dh = SimplestDHCPServer()
        dh.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在本地主机运行代码之后,断开网络连接,然后再重新连接网络。输出:

    b’\x01\x01\x06\x00\xce\xd6>0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x84\\xf3\xea\xa4\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x03=\x07\x01\x84\\xf3\xea\xa4\x0c2\x04\xc0\xa8\x01k\x0c\rLAPTOP-X1NanoQ\x10\x00\x00\x00LAPTOP-DEVTEA<\x08MSFT 5.07\x0e\x01\x03\x06\x0f\x1f!+,./wy\xf9\xfc\xff’
    (‘0.0.0.0’, 68)

    二、DHCP头部解析

    1、DHCP的头部主体

    索引字段名称长度(字节)取值
    (对照msg中的值)
    说明
    0op1msg[0] == b’\x01’报文操作类型:
    1:请求报文,
    2:应答报文
    1htype1msg[1] == b’\x01’客户端MAC地址类型,指明网络类型:
    1:Ethernet MAC
    2hlen1msg[2] == b’\x06’客户端MAC地址长度,以太网MAC地址长度6个字节
    3hops1msg[3] == b’\x00’Client通常设备为0,在中继代理是有意义。
    4-7xid4Transaction ID,Client选择的随机数。
    8-9secs2由Client填写自Client开始地址获取或续订过程地址以来已经过去了几秒钟。
    10-11flags2msg[10:12] == [b’\x00’, b’\x00’]目前仅第一个bit使用,broadcast flag。
    取1时标识报文为广播报文。当DISCOVER的BOOTP Flag的B-bit未置位,Server应当以单播方式进行回应 OFFER 消息。
    12-15ciaddr4msg[12:16] == [b’\x00’, b’\x00’, b’\x00’, b’\x00’]Client IP address。仅当Client处于绑定、续订或重新绑定状态并且可以响应 ARP 请求时才填写.
    16-19yiaddr4msg[16:20] == [b’\x00’, b’\x00’, b’\x00’, b’\x00’]也即DHCP Server告知Client分配到的地址。
    20-23siaddr4msg[20:24] == [b’\x00’, b’\x00’, b’\x00’, b’\x00’]bootstrap引导所使用的下一个服务器的地址。在DHCPOFFER, DHCPACK阶段携带。
    24-27giaddr4Relay agent IP address, 只在中继存在的场景下使用。
    27-43chaddr16Client hardware address.
    44-107sname64Optional server host name, null terminated string.
    108-235file128Boot file name, null terminated string; “generic” name or null in DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER.
    236-options可变长msg[236:240] == [b’\x63’, b’\x82’, b’\x53’, b’\x63’]DHCP Option前4个字节为magic cookie。
    通常固定为0x63,0x82,0x53和0x63。

    2、DHCP 的 option 说明和参考引用

    编号名字长度说明
    0Pad0None
    1Subnet Mask4Subnet Mask Value
    2Time Offset4Time Offset in Seconds from UTC (note: deprecated by 100 and 101)
    3RouterNN/4 Router addresses
    4Time ServerNN/4 Timeserver addresses
    5Name ServerNN/4 IEN-116 Server addresses
    6Domain ServerNN/4 DNS Server addresses
    7Log ServerNN/4 Logging Server addresses
    8Quotes ServerNN/4 Quotes Server addresses
    9LPR ServerNN/4 Printer Server addresses
    10Impress ServerNN/4 Impress Server addresses
    11RLP ServerNN/4 RLP Server addresses
    12HostnameNHostname string
    13Boot File Size2Size of boot file in 512 byte chunks
    14Merit Dump FileNClient to dump and name the file to dump it to
    15Domain NameNThe DNS domain name of the client
    16Swap ServerNSwap Server address
    17Root PathNPath name for root disk
    18Extension FileNPath name for more BOOTP info
    19Forward On/Off1Enable/Disable IP Forwarding
    20SrcRte On/Off1Enable/Disable Source Routing
    21Policy FilterNRouting Policy Filters
    22Max DG Assembly2Max Datagram Reassembly Size
    23Default IP TTL1Default IP Time to Live
    24MTU Timeout4Path MTU Aging Timeout
    25MTU PlateauNPath MTU Plateau Table
    26MTU Interface2Interface MTU Size
    27MTU Subnet1All Subnets are Local
    28Broadcast Address4Broadcast Address
    29Mask Discovery1Perform Mask Discovery
    30Mask Supplier1Provide Mask to Others
    31Router Discovery1Perform Router Discovery
    32Router Request4Router Solicitation Address
    33Static RouteNStatic Routing Table
    34Trailers1Trailer Encapsulation
    35ARP Timeout4ARP Cache Timeout
    36Ethernet1Ethernet Encapsulation
    37Default TCP TTL1Default TCP Time to Live
    38Keepalive Time4TCP Keepalive Interval
    39Keepalive Data1TCP Keepalive Garbage
    40NIS DomainNNIS Domain Name
    41NIS ServersNNIS Server Addresses
    42NTP ServersNNTP Server Addresses
    43Vendor SpecificNVendor Specific Information
    44NETBIOS Name SrvNNETBIOS Name Servers
    45NETBIOS Dist SrvNNETBIOS Datagram Distribution
    46NETBIOS Node Type1NETBIOS Node Type
    47NETBIOS ScopeNNETBIOS Scope
    48X Window FontNX Window Font Server
    49X Window ManagerNX Window Display Manager
    50Address Request4Requested IP Address,已经得到地址后,确认地址
    51Address Time4IP Address Lease Time
    52Overload1Overload “sname” or “file”
    53DHCP Msg Type1DHCP Message Type,DHCP请求消息的类型,
    这个值对比下面的53号类型表说明,可以知道消息的具体类型
    54DHCP Server Id4DHCP Server Identification,确认接受分配的DHCP标识
    55Parameter ListNParameter Request List
    56DHCP MessageNDHCP Error Message
    57DHCP Max Msg Size2DHCP Maximum Message Size
    58Renewal Time4DHCP Renewal (T1) Time
    59Rebinding Time4DHCP Rebinding (T2) Time
    60Class IdNClass Identifier
    61Client IdNClient Identifier
    222-223Unassigned
    224-254Reserved (Private Use)
    255End0None

    更多选项见文章底部参考网址。

    3、 Option 53号 DHCP Msg Type值的含义

    ValueMessage Type
    1DHCPDISCOVER
    2DHCPOFFER
    3DHCPREQUEST
    4DHCPDECLINE
    5DHCPACK
    6DHCPNAK
    7DHCPRELEASE
    8DHCPINFORM
    9DHCPFORCERENEW
    10DHCPLEASEQUERY
    11DHCPLEASEUNASSIGNED
    12DHCPLEASEUNKNOWN
    13DHCPLEASEACTIVE
    14DHCPBULKLEASEQUERY
    15DHCPLEASEQUERYDONE
    16DHCPACTIVELEASEQUERY
    17DHCPLEASEQUERYSTATUS
    18DHCPTLS

    三、头部主体和Option部分解析的代码

    import logging
    import socket
    
    
    class SimplestDHCPServer:
    
        def __init__(self):
            self.server_socket = None
            self._server_port = 67
    
        def start(self):
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
            self.server_socket.bind(('', self._server_port))
            while True:
                logging.warning('DHCP 已经启动...')
                msg, addr = self.server_socket.recvfrom(8192)
                print(msg, addr)
                #以下为新增代码
                print(len(msg))  # 请求总长度
                print(hex(msg[0]))  # 头部主体,索引 0 -- 报文操作类型: 1
                print(hex(msg[1]))  # 头部主体,索引 1 -- 客户端MAC地址类型: 1
                print(hex(msg[2]))  # 头部主体,索引 2 -- 客户端MAC地址长度: 6 
                print(hex(msg[3]))  # 头部主体,索引 3 -- 是否启用代理, Client设备通常为:0
                print(hex(msg[10])) # 头部主体,索引 10~11  -- 是否为广播报文,取1时标识报文为广播报文.
                print(hex(msg[11]))  # 头部主体,索引 10~11  -- 是否为广播报文,取1时标识报文为广播报文.
                print(hex(msg[12]))  #头部主体,索引 12~15 --   客户端 IP 地址
                print(hex(msg[13])) #头部主体,索引 12~15 --   客户端 IP 地址
                print(hex(msg[14])) #头部主体,索引 12~15 --   客户端 IP 地址
                print(hex(msg[15])) #头部主体,索引 12~15 --   客户端 IP 地址
    
                print(hex(msg[16])) #头部主体,索引 16~19 --   客户端确认使用的 IP 地址
                print(hex(msg[17])) #头部主体,索引 16~19 --   客户端确认使用的 IP 地址
                print(hex(msg[18])) #头部主体,索引 16~19 --   客户端确认使用的 IP 地址
                print(hex(msg[19])) #头部主体,索引 16~19 --   客户端确认使用的 IP 地址
    
                print(hex(msg[20]))  #头部主体,索引 20~23 --   使用的DHCP服务器地址
                print(hex(msg[21])) #头部主体,索引 20~23 --   使用的DHCP服务器地址
                print(hex(msg[22])) #头部主体,索引 20~23 --   使用的DHCP服务器地址
                print(hex(msg[23])) #头部主体,索引 20~23 --   使用的DHCP服务器地址
    
                print(hex(msg[236]))  #头部主体,索引 236~239 --   DHCP Option的开始标识
                print(hex(msg[237]))  #头部主体,索引 236~239 --   DHCP Option的开始标识
                print(hex(msg[238]))  #头部主体,索引 236~239 --   DHCP Option的开始标识
                print(hex(msg[239])) #头部主体,索引 236~239 --   DHCP Option的开始标识
                print('以上是头部主体信息')
                print('以下是头部option信息')
                print('option code:', hex(msg[240]))
                print('length:', hex(msg[241]))
                print('value:', hex(msg[242]))
                if msg[242] == 0x3:
                    print("这是一个DHCP Request")
    
    
    if __name__ == '__main__':
        dh = SimplestDHCPServer()
        dh.start()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    322 #请求总长度
    0x1 #头部主体,索引 0 – 报文操作类型:1
    0x1 #头部主体,索引 1 – 客户端MAC地址类型:1
    0x6 #头部主体,索引 2– 客户端MAC地址长度: 6
    0x0 #头部主体,索引 3 – 是否启用代理, Client设备通常为:0
    0x0 #头部主体,索引 10~11 – 是否为广播报文,取1时标识报文为广播报文.
    0x0 #头部主体,索引 10~11 – 是否为广播报文,取1时标识报文为广播报文.
    0x0 #头部主体,索引 12~15 – 客户端 IP 地址
    0x0 #头部主体,索引 12~15 – 客户端 IP 地址
    0x0 #头部主体,索引 12~15 – 客户端 IP 地址
    0x0 #头部主体,索引 12~15 – 客户端 IP 地址
    0x0 #头部主体,索引 16~19 – 客户端确认使用的 IP 地址
    0x0 #头部主体,索引 16~19 – 客户端确认使用的 IP 地址
    0x0 #头部主体,索引 16~19 – 客户端确认使用的 IP 地址
    0x0 #头部主体,索引 16~19 – 客户端确认使用的 IP 地址
    0x0 #头部主体,索引 20~23 – 使用的DHCP服务器地址
    0x0 #头部主体,索引 20~23 – 使用的DHCP服务器地址
    0x0 #头部主体,索引 20~23 – 使用的DHCP服务器地址
    0x0 #头部主体,索引 20~23 – 使用的DHCP服务器地址
    0x63 #头部主体,索引 236~239 – DHCP Option的开始标识
    0x82 #头部主体,索引 236~239 – DHCP Option的开始标识
    0x53 #头部主体,索引 236~239 – DHCP Option的开始标识
    0x63 #头部主体,索引 236~239 – DHCP Option的开始标识
    以上是头部主体信息
    以下是头部option信息
    option code: 0x35 #option code,索引 240 – 1个字节, 0x35 ( 53) 标识 DHCP消息类型
    length: 0x1 #长度,索引 241 – 1 个字节,0x1
    value: 0x3 #取值 ,索引 242 – 1个字节 , 0x3 (3) 标识是 DHCP客户端发出的请求IP的消息。
    这是一个DHCP Request

    四、DHCP的四个阶段以及数据经过的层

    DHCP 分为四个阶段: Discovery、Offer、Request、Afirmledgement(也称为 DORA 进程)。DHCP 使用这个过程为客户端提供 IP 地址。

    • Discovery(发现)
      DHCP 客户端发送一条信息来发现网络中 DHCP 服务器。这个消息在网络层和数据链路层广播。
    • Offer(提供)
      DHCP 服务器从客户端接收信息,并为 DHCP 客户端提供 IP 地址。这个消息在数据链路层单播,但在网络层广播。
    • Request(请求)
      DHCP 客户端为提供的 IP 地址请求 DHCP 服务器。这个消息在数据链路层单播,但在网络层广播。
    • Acknowledgment(承认)
      DHCP 服务器向 DHCP 客户端发送承认信息。这个消息在数据链路层单播,但在网络层广播。它是 DHCP DORA 进程的最后一个信息。

    五、windows下不能直接发送链路层数据,需要借助第三方软件

    Npcap is the Nmap Project's packet capture (and sending) library for Microsoft Windows. 
    
    It implements the open Pcap API using a custom Windows kernel driver alongside our Windows build of the excellent libpcap library. 
    
    This allows Windows software to capture raw network traffic (including wireless networks, wired ethernet, localhost traffic, and many VPNs) using a simple, portable API. 
    
    Npcap allows for sending raw packets as well. Mac and Linux systems already include the Pcap API...
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    参考资料:

    • http://www.rfc.ac.cn/html/rfc2131.html
    • http://www.rfc.ac.cn/html/rfc1533.html
    • https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
    • https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/8/html/managing_networking_infrastructure_services/dhcp-transaction-phases_providing-dhcp-services
    • https://npcap.com/
  • 相关阅读:
    JSP基础语法和指令(二)
    TypeScript 地图标记案例
    JVM学习-字节码指令集(三)
    使用OpenGL绘制shp文件
    服务可用性设计
    python之集合的创建与使用,遍历,集合常见的操作函数,集合与列表,元组,字典的嵌套
    使用动态参数构建CUDA图
    uniapp快速入门系列(1)- 概述与基础知识
    HarmonyOS 鸿蒙DevEco:导入无法运行提示Sync failed
    【Oracle系列1】Oracle 的connect权限和create session的区别
  • 原文地址:https://blog.csdn.net/teamlet/article/details/134433042