DHCP+Mysql server on Python

DHCP+Mysql server on Python

The goal of this project was:

  • Studying the DHCP protocol while working in an IPv4 network
  • Learning Python (slightly more than from scratch 😉)
  • server replacement DB2DHCP (my fork), original here, which is becoming increasingly difficult to build for the new OS. Plus, I don't like that there's no way to "change it right now" for the binary.
  • obtaining a functioning DHCP server with the ability to select the subscriber's IP address based on the subscriber's MAC address or the combination of switch MAC + port (Option 82)
  • writing another bike (Oh! This is my favorite pastime)
  • getting flak for my clumsiness on Habrhabr (or better yet, an invite) 😉

Result: works 😉 Tested on FreeBSD and Ubuntu OS. Theoretically, the code can be requested to run on any OS, as there seem to be no specific ties in the code.
Caution! There’s a lot more ahead.

Link to the repository for enthusiasts "to touch it live".

The process of installation, setup, and use of the result of "studying the material" is much further below, and then a bit of theory on the DHCP protocol. For myself. And for history 😉

A bit of theory

What is DHCP

It is a network protocol that allows a device to learn its IP address (and other parameters like gateway, DNS, etc.) from the DHCP server. The packet exchange occurs over the UDP protocol. The general principle of the device's operation when requesting network parameters is as follows:

  1. The device (client) broadcasts a UDP request (DHCPDISCOVER) across the entire network asking, "Well, can someone give me an IP address?" The request usually (but not always) comes from port 68 (source), while the destination is port 67 (destination). Some devices send packets from port 67 as well. The DHCPDISCOVER packet includes the MAC address of the client device.
  2. All DHCP servers present in the network (and there can be several) create an offer DHCPOFFER with network settings for the device that sent DHCPDISCOVER and also broadcast it across the network. Identification of whom this packet is intended for is done by the MAC address of the client provided earlier in the DHCPDISCOVER request.
  3. The client receives packets with network configuration offers, selects the most appealing one (criteria may vary, including delivery time of the packet and the number of intermediary routes), and makes an 'official request' DHCPREQUEST with network settings to the preferred DHCP server. In this case, the packet is sent to a specific DHCP server.
  4. The server that receives the DHCPREQUEST sends a DHCPACK packet, which again lists the network settings intended for this client.

DHCP+Mysql server on Python

Additionally, there are DHCPINFORM packets that come from the client, aimed at informing the DHCP server that the 'client is alive' and is using the issued network settings. In this server implementation, these packets are ignored.

Packet Format

In general, the Ethernet frame packet looks like this:

DHCP+Mysql server on Python

In our case, we will only consider the data directly contained in the UDP packet, without the OSI layer protocol headers, namely the structure of DHCP:

DHCPDISCOVER

So, the process of obtaining an IP address for a device begins with the DHCP client sending a broadcast request from port 68 to 255.255.255.255:67. In this packet, the client includes its MAC address as well as what it wants to receive from the DHCP server. The packet structure is described in the table below.

Table of DHCPDISCOVER Packet Structure

Position in Packet
Value Name
Example
Representation
Byte
Explanation

1
Boot Request
1
Hex
1
Message type. 1 — request from client to server, 2 — response from server to client.

2
Hardware type
1
Hex
1
Type of hardware address, in this protocol, 1 — MAC.

3
Hardware address length
6
Hex
1
Length of the device's MAC address.

4
Hops
1
Hex
1
Number of intermediary routes.

5
Transaction ID
23:cf:de:1d
Hex
4
Unique transaction identifier. Generated by the client at the start of the request operation.

7
Second elapsed
0
Hex
4
Time in seconds since the start of the address acquisition process.

9
Bootp flags
0
Hex
2
Some flags that can be set as indications of protocol parameters.

11
Client IP address
0.0.0.0
Line
4
Client IP address (if any).

15
Your client IP address
0.0.0.0
Line
4
IP address proposed by the server (if any).

19
Next server IP address
0.0.0.0
Line
4
Server IP address (if known).

23
Relay agent IP address
172.16.114.41
Line
4
IP address of the relay agent (e.g., switch).

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC address of the packet sender (client).

31
Client hardware address padding
 
Hex
10
Reserved space. Typically filled with zeros.

41
Server host name
 
Line
64
DHCP server name. Usually not transmitted.

105
Boot file name
 
Line
128
File name on the server used by diskless stations during boot

235
Magic cookie
63:82:53:63
Hex
4
The 'magic' number used to indicate that this packet belongs to the DHCP protocol

DHCP options. Can appear in any order

236
Option number
53
Dec
1
Option 53 that specifies the type of DHCP packet

1 — DHCPDISCOVER
3 — DHCPREQUEST
2 — DHCPOFFER
5 — DHCPACK
8 — DHCPINFORM

 
Length of the option
1
Dec
1

 
Value of the option
1
Dec
1

 
Option number
50
Dec
1
Which IP address the client wants to receive

 
Length of the option
4
Dec
1

 
Value of the option
172.16.134.61
Line
4

 
Option number
55
 
1
Requested network parameters by the client. The composition can vary

01 — Subnet mask
03 — Gateway
06 — DNS
0c — Hostname
0f — Network domain name
1c — Broadcast address
42 — TFTP server name
79 — Classless Static Route

 
Length of the option
8
 
1

 
Value of the option
01:03:06:0c:0f:1c:42:79
 
8

 
Option number
82
Dec
 
Option 82, which transmits the MAC address of the relay device and some additional values.

Most often — the switch port on which the DHCP client operates. This option 'contains' additional parameters. The first byte is the suboption number, the second is its length, followed by its value.

In this case, option 82 includes suboptions:
Agent Circuit ID = 00:04:00:01:00:04, where the last two bytes represent the port of the DHCP client from which the request came

Agent Remote ID = 00:06:c8:be:19:93:11:48 — MAC address of the DHCP relay device

 
Length of the option
18
Dec
 

 
Value of the option
01:06
00:04:00:01:00:04
02:08
00:06:c8:be:19:93:11:48
Hex
 

 
End of the packet
255
Dec
1
255 signifies the end of the packet

DHCPOFFER

As soon as the server receives a DHCPDISCOVER packet and it sees it can offer something to the client from the request, it creates a response — DHCPOFFER. This response is sent to the port 'from where it came,' as a broadcast, since at that moment, the client still does not have an IP address, and thus can only accept the packet if it is sent as a broadcast. The client recognizes that this packet is for it by its MAC address inside the packet, as well as the transaction number, which it generates at the moment of creating the first packet.

Structure table of the DHCPOFFER packet

Position in Packet
Common name of the value
Example
Representation
Byte
Explanation

1
Boot Request
1
Hex
1
Message type. 1 — request from client to server, 2 — response from server to client.

2
Hardware type
1
Hex
1
Type of hardware address, in this protocol, 1 — MAC.

3
Hardware address length
6
Hex
1
Length of the device's MAC address.

4
Hops
1
Hex
1
Number of intermediary routes.

5
Transaction ID
23:cf:de:1d
Hex
4
Unique transaction identifier. Generated by the client at the start of the request operation.

7
Second elapsed
0
Hex
4
Time in seconds since the start of the address acquisition process.

9
Bootp flags
0
Hex
2
Some flags that can be set as an indication of protocol parameters. In this case, 0 — indicates a Unicast request type

11
Client IP address
0.0.0.0
Line
4
Client IP address (if any).

15
Your client IP address
172.16.134.61
Line
4
IP address proposed by the server (if any).

19
Next server IP address
0.0.0.0
Line
4
Server IP address (if known).

23
Relay agent IP address
172.16.114.41
Line
4
IP address of the relay agent (e.g., switch).

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC address of the packet sender (client).

31
Client hardware address padding
 
Hex
10
Reserved space. Typically filled with zeros.

41
Server host name
 
Line
64
DHCP server name. Usually not transmitted.

105
Boot file name
 
Line
128
File name on the server used by diskless stations during boot

235
Magic cookie
63:82:53:63
Hex
4
The 'magic' number used to indicate that this packet belongs to the DHCP protocol

DHCP options. Can appear in any order

236
Option number
53
Dec
1
Option 53, specifying DHCP packet type 2 — DHCPOFFER

 
Length of the option
1
Dec
1

 
Value of the option
2
Dec
1

 
Option number
1
Dec
1
Option offering the DHCP client a subnet mask

 
Length of the option
4
Dec
1

 
Value of the option
255.255.224.0
Line
4

 
Option number
3
Dec
1
Option offering the DHCP client a default gateway

 
Length of the option
4
Dec
1

 
Value of the option
172.16.12.1
Line
4

 
Option number
6
Dec
1
Option offering the DHCP client DNS

 
Length of the option
4
Dec
1

 
Value of the option
8.8.8.8
Line
4

 
Option number
51
Dec
1
Lifetime of the issued network parameters in seconds, after which the DHCP client should request them again

 
Length of the option
4
Dec
1

 
Value of the option
86400
Dec
4

 
Option number
82
Dec
1
Option 82 repeats what was received in DHCPDISCOVER

 
Length of the option
18
Dec
1

 
Value of the option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
End of the packet
255
Dec
1
255 signifies the end of the packet

DHCPREQUEST

After the client receives the DHCPOFFER, it constructs a packet requesting network parameters not from all DHCP servers in the network, but only from one specific server whose DHCPOFFER it liked the most. The criteria for 'liking' can vary and depend on the implementation of the DHCP client. The recipient of the request is specified using the MAC address of the DHCP server. The DHCPREQUEST packet can also be sent by the client without a prior DHCPDISCOVER if an IP address from the server was previously obtained.

Structure Table of the DHCPREQUEST Packet

Position in Packet
Common name of the value
Example
Representation
Byte
Explanation

1
Boot Request
1
Hex
1
Message type. 1 — request from client to server, 2 — response from server to client.

2
Hardware type
1
Hex
1
Type of hardware address, in this protocol, 1 — MAC.

3
Hardware address length
6
Hex
1
Length of the device's MAC address.

4
Hops
1
Hex
1
Number of intermediary routes.

5
Transaction ID
23:cf:de:1d
Hex
4
Unique transaction identifier. Generated by the client at the start of the request operation.

7
Second elapsed
0
Hex
4
Time in seconds since the start of the address acquisition process.

9
Bootp flags
8000
Hex
2
Some flags may be set as an indication of protocol parameters. In this case, 'broadcast' is set.

11
Client IP address
0.0.0.0
Line
4
Client IP address (if any).

15
Your client IP address
172.16.134.61
Line
4
IP address proposed by the server (if any).

19
Next server IP address
0.0.0.0
Line
4
Server IP address (if known).

23
Relay agent IP address
172.16.114.41
Line
4
IP address of the relay agent (e.g., switch).

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC address of the packet sender (client).

31
Client hardware address padding
 
Hex
10
Reserved space. Typically filled with zeros.

41
Server host name
 
Line
64
DHCP server name. Usually not transmitted.

105
Boot file name
 
Line
128
File name on the server used by diskless stations during boot

235
Magic cookie
63:82:53:63
Hex
4
The 'magic' number used to indicate that this packet belongs to the DHCP protocol

DHCP options. Can appear in any order

236
Option number
53
Dec
3
Option 53, indicating the packet type DHCP 3 — DHCPREQUEST

 
Length of the option
1
Dec
1

 
Value of the option
3
Dec
1

 
Option number
61
Dec
1
Client Identifier: 01 (for Ethernet) + Client MAC Address

 
Length of the option
7
Dec
1

 
Value of the option
01:2c:ab:25:ff:72:a6
Hex
7

 
Option number
60
Dec
 
'Vendor class identifier'. In my case, it reports the version of the DHCP client. Other devices may return different information. Windows, for example, reports MSFT 5.0.

 
Length of the option
11
Dec
 

 
Value of the option
udhcp 0.9.8
Line
 

 
Option number
55
 
1
Requested network parameters by the client. The composition can vary

01 — Subnet mask
03 — Gateway
06 — DNS
0c — Hostname
0f — Network domain name
1c — Broadcast address
42 — TFTP server name
79 — Classless Static Route

 
Length of the option
8
 
1

 
Value of the option
01:03:06:0c:0f:1c:42:79
 
8

 
Option number
82
Dec
1
Option 82 repeats what was received in DHCPDISCOVER

 
Length of the option
18
Dec
1

 
Value of the option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
End of the packet
255
Dec
1
255 signifies the end of the packet

DHCPACK

As a confirmation that 'yes, this is your IP address, and I won't assign it to anyone else', the DHCP server sends a packet in the DHCPACK format to the client. This packet, like the others, is also sent as a broadcast. However, in the code below for the DHCP server implemented in Python, I also duplicate any broadcast request by sending a packet to a specific IP client if it is already known. Moreover, the DHCP server does not concern itself whether the DHCPACK packet reached the client. If the client does not receive the DHCPACK, it simply repeats the DHCPREQUEST after some time.

Structure Table of the DHCPACK Packet

Position in Packet
Common name of the value
Example
Representation
Byte
Explanation

1
Boot Request
2
Hex
1
Message type. 1 — request from client to server, 2 — response from server to client.

2
Hardware type
1
Hex
1
Type of hardware address, in this protocol, 1 — MAC.

3
Hardware address length
6
Hex
1
Length of the device's MAC address.

4
Hops
1
Hex
1
Number of intermediary routes.

5
Transaction ID
23:cf:de:1d
Hex
4
Unique transaction identifier. Generated by the client at the start of the request operation.

7
Second elapsed
0
Hex
4
Time in seconds since the start of the address acquisition process.

9
Bootp flags
8000
Hex
2
Some flags may be set as an indication of protocol parameters. In this case, 'broadcast' is set.

11
Client IP address
0.0.0.0
Line
4
Client IP address (if any).

15
Your client IP address
172.16.134.61
Line
4
IP address proposed by the server (if any).

19
Next server IP address
0.0.0.0
Line
4
Server IP address (if known).

23
Relay agent IP address
172.16.114.41
Line
4
IP address of the relay agent (e.g., switch).

27
Client MAC address
14:d6:4d:a7:c9:55
Hex
6
MAC address of the packet sender (client).

31
Client hardware address padding
 
Hex
10
Reserved space. Typically filled with zeros.

41
Server host name
 
Line
64
DHCP server name. Usually not transmitted.

105
Boot file name
 
Line
128
File name on the server used by diskless stations during boot

235
Magic cookie
63:82:53:63
Hex
4
The 'magic' number used to indicate that this packet belongs to the DHCP protocol

DHCP options. Can appear in any order

236
Option number
53
Dec
3
Option 53, indicating the packet type DHCP 5 — DHCPACK

 
Length of the option
1
Dec
1

 
Value of the option
5
Dec
1

 
Option number
1
Dec
1
Option offering the DHCP client a subnet mask

 
Length of the option
4
Dec
1

 
Value of the option
255.255.224.0
Line
4

 
Option number
3
Dec
1
Option offering the DHCP client a default gateway

 
Length of the option
4
Dec
1

 
Value of the option
172.16.12.1
Line
4

 
Option number
6
Dec
1
Option offering the DHCP client DNS

 
Length of the option
4
Dec
1

 
Value of the option
8.8.8.8
Line
4

 
Option number
51
Dec
1
Lifetime of the issued network parameters in seconds, after which the DHCP client should request them again

 
Length of the option
4
Dec
1

 
Value of the option
86400
Dec
4

 
Option number
82
Dec
1
Option 82 repeats what was received in DHCPDISCOVER

 
Length of the option
18
Dec
1

 
Value of the option
01:08:00:06:00
01:01:00:00:01
02:06:00:03:0f
26:4d:ec
Dec
18

 
End of the packet
255
Dec
1
255 signifies the end of the packet

Installation

The installation essentially involves setting up the necessary Python modules for operation. It is assumed that MySQL is already installed and configured.

FreeBSD

pkg install python3
python3 -m ensurepip
pip3 install mysql-connector

Ubuntu

sudo apt-get install python3
sudo apt-get install pip3
sudo pip3 install mysql-connector

Create a MySQL database, import the pydhcp.sql dump into it, and configure the configuration file.

Configuration

All server settings are contained within an XML format file. Reference file:

0.0.0.0
        255.255.255.255
        192.168.0.71
	8600
	1
        255.255.255.0
        192.168.0.1
        8.8.8.8
    
    
        localhost
	test
	test
	pydhcp
    
    
       option_82_hex:sw_port1:20:22       
       option_82_hex:sw_port2:16:18       
       option_82_hex:sw_mac:26:40
    
    
        3
	select ip,mask,router,dns from users where upper(mac)=upper('{option_82_AgentRemoteId_hex}') and upper(port)=upper('{option_82_AgentCircuitId_port_hex}')
        select ip,mask,router,dns from users where upper(mac)=upper('{sw_mac}') and upper(port)=upper('{sw_port2}')
        select ip,mask,router,dns from users where upper(mac)=upper('{ClientMacAddress}')
	insert into history (id,dt,mac,ip,comment) values (null,now(),'{ClientMacAddress}','{RequestedIpAddress}','DHCPACK/INFORM')

Now, let's take a closer look at the tags:

The dhcpserver section describes the main settings for starting the server, specifically:

  • host — the IP address the server listens on port 67
  • broadcast — the IP that is the broadcast for DHCPOFFER and DHCPACK
  • DHCPServer — the IP of the DHCP server
  • LeaseTime — the lease time of the assigned IP address
  • ThreadLimit — how many threads are simultaneously processing incoming UDP packets on port 67. It is presumed to help in high-load projects 😉
  • defaultMask, defaultRouter, defaultDNS — what is offered to the subscriber by default if the IP is found in the database, but no additional parameters are specified

The mysql section:

host, username, password, basename — all self-explanatory. An approximate structure of the database is outlined at GitHub

The query section: this describes the requests for obtaining OFFER/ACK:

  • offer_count — the number of rows with requests that return results in the form of ip, mask, router, dns
  • offer_n — query string. If the return is empty, it executes the next offer query
  • history_sql — a query that writes, for example, to the 'authorization history' for the subscriber

Requests can involve any variables from the options section or options from the DHCP protocol.

The options section. Now it gets more interesting. Here we can create variables that we can use later in the query section.

For example:

option_82_hex:sw_port1:20:22

, this command line takes the entire string received in the DHCP request option 82, in hex format, from byte 20 to byte 22 inclusive, and places it into a new variable sw_port1 (the switch port from which the request came)

option_82_hex:sw_mac:26:40

, we define the variable sw_mac, extracting hex from range 26:40

You can see all possible options that can be used in requests by running the server with the -d key. We will see a log like this:

--a DHCPINFORM packet arrived on port 67, from 0025224ad764, b'x91xa5xe0xa3xa5xa9-x8fx8a', ('172.30.114.25', 68)
{'ClientMacAddress': '0025224ad764',
 'ClientMacAddressByte': b'x00%"Jxd7d',
 'HType': 'Ethernet',
 'HostName': b'x91xa5xe0xa3xa5xa9-x8fx8a',
 'ReqListDNS': True,
 'ReqListDomainName': True,
 'ReqListPerfowmRouterDiscover': True,
 'ReqListRouter': True,
 'ReqListStaticRoute': True,
 'ReqListSubnetMask': True,
 'ReqListVendorSpecInfo': 43,
 'RequestedIpAddress': '0.0.0.0',
 'Vendor': b'MSFT 5.0',
 'chaddr': '0025224ad764',
 'ciaddr': '172.30.128.13',
 'flags': b'x00x00',
 'giaddr': '172.30.114.25',
 'gpoz': 308,
 'hlen': 6,
 'hops': 1,
 'htype': 'MAC',
 'magic_cookie': b'cx82Sc',
 'op': 'DHCPINFORM',
 'option12': 12,
 'option53': 53,
 'option55': 55,
 'option60': 60,
 'option61': 61,
 'option82': 82,
 'option_82_byte': b'x12x01x06x00x04x00x01x00x06x02x08x00'
                   b'x06x00x1eXx9exb2xad',
 'option_82_hex': '12010600040001000602080006001e589eb2ad',
 'option_82_len': 18,
 'option_82_str': "b'x12x01x06x00x04x00x01x00x06x02x08x00x06x00x1eXx9exb2xad'",
 'result': False,
 'secs': 768,
 'siaddr': '0.0.0.0',
 'sw_mac': '001e589eb2ad',
 'sw_port1': '06',
 'xidbyte': b'<x89}x8c',
 'xidhex': '3c897d8c',
 'yiaddr': '0.0.0.0'}

Accordingly, we can wrap any variable in {} and it will be used in the SQL query.

Let's note for the record, the IP address the client received:

DHCP+Mysql server on Python

DHCP+Mysql server on Python

Server Launch

.\/pydhcpdb.py -d -c config.xml

— d for DEBUG console output mode
— c <file_name> configuration file

Flight analysis

Now, let's go into detail about the server implementation in Python. This is painful. Python was studied "on the fly". Many moments were done in the style of: "wow, somehow I did it, and it works." It is not optimized at all and left in this state mainly due to the low development experience in Python. I will focus on the most interesting aspects of the server implementation in "code."

XML Configuration File Parser

The standard Python module xml.dom is used. It seems simple, but during implementation, there was a noticeable lack of decent documentation and examples online using this module.

    tree = minidom.parse(gconfig["config_file"])
    mconfig=tree.getElementsByTagName("mysql")
    for elem in mconfig:        
        gconfig["mysql_host"]=elem.getElementsByTagName("host")[0].firstChild.data      
        gconfig["mysql_username"]=elem.getElementsByTagName("username")[0].firstChild.data      
        gconfig["mysql_password"]=elem.getElementsByTagName("password")[0].firstChild.data      
        gconfig["mysql_basename"]=elem.getElementsByTagName("basename")[0].firstChild.data      
    dconfig=tree.getElementsByTagName("dhcpserver")
    for elem in dconfig:        
        gconfig["broadcast"]=elem.getElementsByTagName("broadcast")[0].firstChild.data      
        gconfig["dhcp_host"]=elem.getElementsByTagName("host")[0].firstChild.data      
        gconfig["dhcp_LeaseTime"]=elem.getElementsByTagName("LeaseTime")[0].firstChild.data      
        gconfig["dhcp_ThreadLimit"]=int(elem.getElementsByTagName("ThreadLimit")[0].firstChild.data)              
        gconfig["dhcp_Server"]=elem.getElementsByTagName("DHCPServer")[0].firstChild.data              
        gconfig["dhcp_defaultMask"]=elem.getElementsByTagName("defaultMask")[0].firstChild.data              
        gconfig["dhcp_defaultRouter"]=elem.getElementsByTagName("defaultRouter")[0].firstChild.data              
        gconfig["dhcp_defaultDNS"]=elem.getElementsByTagName("defaultDNS")[0].firstChild.data              
    qconfig=tree.getElementsByTagName("query")
    for elem in qconfig:  
        gconfig["offer_count"]=elem.getElementsByTagName("offer_count")[0].firstChild.data                          
        for num in range(int(gconfig["offer_count"])):
            gconfig["offer_"+str(num+1)]=elem.getElementsByTagName("offer_"+str(num+1))[0].firstChild.data      
        gconfig["history_sql"]=elem.getElementsByTagName("history_sql")[0].firstChild.data                          
    options=tree.getElementsByTagName("options")       
    for elem in options:          
        node=elem.getElementsByTagName("option")
        for options in node:
            optionsMod.append(options.firstChild.data)

Multithreading

Strangely enough, multithreading in Python is implemented in a very straightforward and simple manner.

def PacketWork(data, addr): 
...
# implementation for parsing the incoming packet and responding to it
...


while True:
    data, addr = udp_socket.recvfrom(1024) # waiting for a UDP packet
    thread = threading.Thread(target=PacketWork, args=(data, addr,)).start()  # as soon as it arrives, start the previously defined PacketWork function in the background with parameters
    while threading.active_count() > gconfig["dhcp_ThreadLimit"]:
       time.sleep(1) # if the number of already launched threads exceeds the set limit, wait until it decreases

Receiving/Sending DHCP Packet

To capture UDP packets coming through the network interface, you need to "bring up" the socket:

udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
udp_socket.bind((gconfig["dhcp_host"], 67))

, where the flags:

  • AF_INET — indicates that the address format will be IP: port. It can also be AF_UNIX — where the address is specified by a file name.
  • SOCK_DGRAM means that we receive not a 'raw packet', but one that has already passed through the firewall, and with a partially truncated packet. That is, we only receive the UDP packet without the 'physical' wrapper of the UDP packet. If the SOCK_RAW flag is used, it will be necessary to also parse this 'wrapper'.

The packet can be sent either as a broadcast:

                    udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) #switching the socket to broadcast mode
                    rz=udp_socket.sendto(packetack, (gconfig["broadcast"],68))

, or to the address 'from which the packet came':

                        udp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # switching the socket to 'many listeners' mode
                        rz=udp_socket.sendto(packetack, addr)

, where SOL_SOCKET means 'protocol level' for setting options,

, SO_BROADCAST is the option that sends the packet 'as a broadcast'

  , SO_REUSEADDR is an option that switches the socket to 'many listeners' mode. In theory, it is unnecessary in this case, but on one of the FreeBSD servers I was testing, the code did not work without this option.

Parsing the DHCP packet

Here I really liked Python. It turns out that right out of the 'box' it allows a fairly free manipulation of byte code. It makes it very easy to convert to decimal values, strings, and hex — that is, what we essentially need to understand the packet structure. For example, you can get a range of bytes in HEX and as plain bytes:

    res["xidhex"]=data[4:8].hex()
    res["xidbyte"]=data[4:8]

, pack the bytes into a structure:

res["flags"]=pack('BB',data[10],data[11])

Get the IP from the structure:

res["ciaddr"]=socket.inet_ntoa(pack('BBBB',data[12],data[13],data[14],data[15]));

And vice versa:

res=res+socket.inet_pton(socket.AF_INET, gconfig["dhcp_Server"])

That's all 😉

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster