How To Create Your Own Bash DHCP Server
I like to automate the process and write my own bikes to study this or that material. My new goal was a DHCP server that will issue an address in small networks so that you can make an initial hardware configuration.
In this article, I will tell you a little about the DHCP protocol and some subtleties from bash.
Final result
Let’s start from the end so that it is clear what we are fighting for.
Demonstration of work:
Initial problem
The setup I need is done this way: we connect directly over the twisted pair to the equipment, issue a temporary address via DHCP, configure the already created script. And so ten to twenty times in a row.
Many well-known isc-dhcp-server copes with its duties perfectly, but, alas, it does not notify my script that the address has been issued, so you need to somehow block the execution until the address is issued.
The solution, like, on the surface: ping until blue in the face, until the equipment responds:
while ! ping -c1 -W1 "$DHCP" | grep -q "time=" do echo "Waiting for $DHCP..." done
But in this decision, definitely, there is not enough adventurism.
Theoretical part
Getting an address with one DHCP server
The DHCP protocol works over UDP on ports 67 and 68. The server always works only on 67, and the client only on 68. Since the client does not have an address (has the address 0.0.0.0), DHCP packets are sent in a broadcast manner. Those. the client always sends packets to the address 255.255.255.255:67 from the address 0.0.0.0:68, and the server sends from its address: 67 to the address 255.255.255.255:68.
The client receives the address in four packets (DORA):
- The client finds out where the DHCP server is here (Discover)
- The server responds and offers its address (Offer)
- The client requests the proposed address from a specific server (Request)
- The server agrees and gives the address (Ack)
Visually, the scheme can be represented as follows:
Getting an address with multiple DHCP servers
When a client sends Discover, all servers that can hear will send their Offer to the client. But the customer must choose someone one. Client selection is announced in the Request message by option 54 (DHCP server), which contains the IP address of the preferred DHCP server. Although the Request is sent to everyone else on the network, only the DHCP server whose IP is specified in option 54 responds.
DHCP Package Contents
A DHCP packet consists of two parts: a constant, 236 bytes in size, and a variable that carries options (DHCP Option).
DHCP options are encoded as follows:
Number | Length | Data |
For example, parameter 3 (proposed gateway) with a value of 10.0.0.1:
3 | 4 | 10 | 0 | 0 | 1 |
In case you need to pass several parameters, the parameter length increases.
For example, in parameter 6 (DNS server), we will transfer two addresses (1.1.1.1 and 8.8.4.4):
6 | 8 | 1 | 1 | 1 | 1 | 8 | 8 | 4 | 4 |
A sign of the end of the options field is the parameter with the number 255 (0xFF) and a length of 0.
Most often, the client submits parameter 55 (the list of parameters that he wants to receive in response) in DHCP Discover, however, we have the right to give him not everything he asked for.
Practical part
It was originally planned to write a server in some more suitable language for this (C), however, it would be everyday and simple. How it is to write a script that will take over the functions of the dhcp-server.
Simplify
Since the server being developed was supposed to be used in networks of two nodes connected by a patch, the following simplifications were adopted:
- it is guaranteed that one client is online;
- it is guaranteed that there are no more dhcp servers in the network
- the trigger decides which address to issue
- DHCP Release and DHCP Decline are ignored.
Listener
First, you need to learn how to take packets. This requires a certified sympathetic listener, for example, nc. But not every NC is suitable for these purposes. OpenBSD netcat 1.130 from Debian is suitable, but 1.105 with Ubuntu is gone. Run nc to listen to all UDP packets arriving on port 67.
nc -l 0.0.0.0 -up 67 -w0
OpenBSD netcat is also necessary because of the -w key with a value of 0. After receiving one packet (UDP Broadcast), traditional nc does not accept more packets, but it does not end either.
Work with raw bytes
In the command interpreter, it is very difficult to work with non-printable characters, for example, with a null character: it simply ignores it. And the DHCP packet contains many bytes 0x00 (for example, the file field). The solution to the problem comes in the form of hex-dump:
nc -l 0.0.0.0 -up 67 -w0 | stdbuf -o0 od -v -w1 -t x1 -An
One byte per line, without address output, not missing duplicate bytes. You can also spice up stdbuf -o0 so that the output is not buffered.
Receiving, storing and processing packages
From stdout, the od command is retrieved by the read command and added to the array.
msg=() for i in {0..235}; do read -r tmp msg[$i]=$tmp done
Although all values are transmitted in the hexadecimal number system, the DHCP Option number and option length are best displayed on the screen/logs in the usual decimal form. To do this, you can use the short bash entry:
$ op=AC $ echo $((16#$op)) 172
The received packet is edited according to the type of request (Discover or Request) and sent back.
Send reply
However, sending a package is not such an easy task. First, you need to convert the bytes from the dump into the raw bytes and send everything at once in one packet.
The conversion can be done using the printf utility with the help of escape sequences. And so that nothing is lost, write the bytes immediately to the file.
# Clearing the file > /tmp/dhcp.payload # Write the beginning of a fixed length for i in $ {msg [*]}; do printf "\ x $ i" >> /tmp/dhcp.payload done
OpenBSD netcat is also used for sending. However, if version 1.105 with Ubuntu is suitable as a listener, then it is not suitable for sending broadcast UDP messages: we get a protocol not available error.
cat /tmp/dhcp.payload | nc -ub 255.255.255.255 68 -s $SERVER -p 67 -w0
The -b switch allows sending broadcast messages and this is the second reason why the server needs to be started from under the superuser.
What is there with restrictions?
This DHCP server was developed with simplifications like a single client on the network. However, it will work with multiple clients. Just get the fastest address.
Conclusion
Although bash scripts can hardly be called a full-fledged programming language, nevertheless, with the proper desire, you can even solve such tasks as issuing an IP address on a network without using specially designed software. And the solution of specific tasks not only brings joy, but also new knowledge that was discovered at the moment of the solution.