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 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.

    Work demo



    : Script repository: firemoon777 / bash-dhcp-server

    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 has been issued.

    Solution, like, on the surface: ping until blue in the face, until the equipment responds:

    while ! ping -c1 -W1 "$DHCP" | grep -q "time="doecho"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), the 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 ):

    1. The client finds out where the DHCP server is here ( D iscover)
    2. The server responds and offers its address ( O ffer)
    3. The client requests the proposed address from a specific server ( R equest)
    4. The server agrees and issues an address ( A ck)

    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).

    A table with all the fields of a DHCP packet from Wikipedia
    FieldDescriptionLength (in bytes)
    op
    Message type For example, the values ​​can be: BOOTREQUEST (0x01, request from client to server) and BOOTREPLY (0x02, response from server to client).
    one
    htype
    Тип аппаратного адреса. Допустимые значения этого поля определены в RFC 1700 «Assigned Numbers». Например, для MAC-адреса Ethernet это поле принимает значение 0x01.
    1
    hlen
    Длина аппаратного адреса в байтах. Для MAC-адреса Ethernet —0x06.
    1
    hops
    Количество промежуточных маршрутизаторов (так называемых агентов ретрансляции DHCP), через которые прошло сообщение. Клиент устанавливает это поле в 0x00.
    1
    xid
    Уникальный идентификатор транзакции в 4 байта, генерируемый клиентом в начале процесса получения адреса.
    4
    secs
    Время в секундах с момента начала процесса получения адреса. Может не использоваться (в этом случае оно устанавливается в 0x0000).
    2
    flags
    Поле для флагов — специальных параметров протокола DHCP.
    2
    ciaddr
    IP-адрес клиента. Заполняется только в том случае, если клиент уже имеет собственный IP-адрес и способен отвечать на запросы ARP (это возможно, если клиент выполняет процедуру обновления адреса по истечении срока аренды).
    4
    yiaddr
    Новый IP-адрес клиента, предложенный сервером.
    4
    siaddr
    IP-адрес сервера. Возвращается в предложении DHCP (см. ниже).
    4
    giaddr
    IP-адрес агента ретрансляции, если таковой участвовал в процессе доставки сообщения DHCP до сервера.
    4
    chaddr
    Аппаратный адрес (обычно MAC-адрес) клиента.
    16
    sname
    Необязательное имя сервера в виде нуль-терминированной строки.
    64
    file
    Необязательное имя файла на сервере, используемое бездисковыми рабочими станциями при удалённой загрузке. Как и sname, представлено в виде нуль-терминированной строки.
    128
    options
    Поле опций DHCP. Здесь указываются различные дополнительные параметры конфигурации. В начале этого поля указываются четыре особых байта со значениями 99, 130, 83, 99 («волшебные числа»), позволяющие серверу определить наличие этого поля. Поле имеет переменную длину, однако DHCP-клиент должен быть готов принять DHCP-сообщение длиной в 576 байт (в этом сообщении поле options имеет длину 340 байт).
    переменная


    List of all DHCP options in RFC 2132

    DHCP options are encoded as follows:
    roomLengthData

    For example, parameter 3 (proposed gateway) with a value of 10.0.0.1:
    3fourten00one

    In case you need to pass several parameters, the parameter length increases.
    For example, in parameter 6 (DNS server), we will transmit two addresses (1.1.1.1 and 8.8.4.4):
    6eightoneoneoneoneeighteightfourfour

    A sign of the end of the option field is the parameter with the number 255 (0xFF) and length 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 simple 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 needed because of the -w key with a value of 0. After receiving one packet (UDP Broadcast), the 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 displaying the address, 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}; doread -r tmp
    	msg[$i]=$tmpdone

    Although all values ​​are transmitted in hexadecimal notation, 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 a brief 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 all 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.

    # Зачищаем файл
    >/tmp/dhcp.payload
    # Записываем начало фиксированной длиныfor i in${msg[*]}; doprintf"\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 here 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 should 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.

    Sources


    1. DHCP - Wikipedia
    2. DHCP and BOOTP Parameters - IANA

    Also popular now: