DHCP and BOOTP

The code in <PNet>\DHCP.fth implements a DHCP client according to RFC2131. See also RFC2132 and RFC1542. Do not attempt to modify this code until you have absorbed these RFCs.

If you do not have a DHCP server on you network, there are many available. For Windows PCs, several users have recommended the haneWin DHCP Server from www.hanewin.net.

0 equ DHCPdebug?        \ -- x
Set this non-zero to display DHCP debug information during execution.

DHCP/BOOTP state machine

The DHCP system is controlled by a state machine, which is run whenever the timer *\fo{DHCPtimer) times out. The action performed is responsible setting the next state and updating the timer. The state machine actions are performed by DHCPidle ( -- ) which is called in the service task.

States of the DHCP state machine. See RFC2131 Figure 5 below for the transition diagram.


 --------                               -------
|        | +-------------------------->|       |<-------------------+
| INIT-  | |     +-------------------->| INIT  |                    |
| REBOOT |DHCPNAK/         +---------->|       |<---+               |
|        |Restart|         |            -------     |               |
 --------  |  DHCPNAK/     |               |                        |
    |      Discard offer   |      -/Send DHCPDISCOVER               |
-/Send DHCPREQUEST         |               |                        |
    |      |     |      DHCPACK            v        |               |
 -----------     |   (not accept.)/   -----------   |               |
|           |    |  Send DHCPDECLINE |           |                  |
| REBOOTING |    |         |         | SELECTING |<----+            |
|           |    |        /          |           |     |DHCPOFFER/  |
 -----------     |       /            -----------   |  |Collect     |
    |            |      /                  |   |       |  replies   |
DHCPACK/         |     /  +----------------+   +-------+            |
Record lease, set|    |   v   Select offer/                         |
timers T1, T2   ------------  send DHCPREQUEST      |               |
    |   +----->|            |             DHCPNAK, Lease expired/   |
    |   |      | REQUESTING |                  Halt network         |
    DHCPOFFER/ |            |                       |               |
    Discard     ------------                        |               |
    |   |        |        |                   -----------           |
    |   +--------+     DHCPACK/              |           |          |
    |              Record lease, set    -----| REBINDING |          |
    |                timers T1, T2     /     |           |          |
    |                     |        DHCPACK/   -----------           |
    |                     v     Record lease, set   ^               |
    +----------------> -------      /timers T1,T2   |               |
               +----->|       |<---+                |               |
               |      | BOUND |<---+                |               |
  DHCPOFFER, DHCPACK, |       |    |            T2 expires/   DHCPNAK/
   DHCPNAK/Discard     -------     |             Broadcast  Halt network
               |       | |         |            DHCPREQUEST         |
               +-------+ |        DHCPACK/          |               |
                    T1 expires/   Record lease, set |               |
                 Send DHCPREQUEST timers T1, T2     |               |
                 to leasing server |                |               |
                         |   ----------             |               |
                         |  |          |------------+               |
                         +->| RENEWING |                            |
                            |          |----------------------------+
                             ----------
          Figure 5: State-transition diagram for DHCP clients
[sm
  smState smDHidle      \ idle, do nothing (pseudostate)
  smState smDHinit      \ initialisation, o/p DHCPDISCOVER
  smState smDHselect    \ process DHCPOFFER(s)
  smState smDHrequest   \ process ACK, discard offers
                        \ ACK/decline -> o/p DHCPDECLINE, smDHinit
                        \ ACK/accept -> record lease, smDHbound
  smState smDHarpcheck  \ go ACK, check -> DHCPdecline/init or bound
  smState smDHbound     \ discard messages, time out ->
  smState smDHrenew     \ wait ACK, T2 expiry
  smState smDHrebind    \ wait ACK/NAK
  smState smDHrebooting \ warm restart, DHCPREQUEST, wait ACK/NAK
sm] #smDHactions        \ number of states in this machine

variable DHCPTimer      \ -- addr
Holds the TICKS time at which the next DHCP action will be performed, 0 for no action.

variable smDHcount      \ -- addr
Down counter used to provide repetitions and timeouts.

variable smDHstate      \ -- n
Holds current DHCP state.

create smDHactions      \ -- addr
Execution table containing the xts of the action words.

: setDHstate    \ xt state --
INTERPRETER word that sets the action of the given state.

: DHCPidle      \ --
Performed periodically to do DHCP actions and lease checks.

State machine utilitiles

: SetDHCPtimer  \ ms --
Set the DHCP timer to time out ms later.

: .DHstate      \ --
Debug tool to display the current state of the DHCP state machine.

: toDHCPstate   \ ms n state --
The given state will be executed after the given period in ms and n times if sensitive to the number of times. Note that this specifies the interval until the state is executed, not the repetition rate of the state.

DHCP Data definitions

DHCP control data

These variables contain the working data used to access the DHCP server.

variable BOOTP_XID      \ -- addr
Transaction ID number.

variable DHCPLease      \ -- addr
Length of DHCP lease in ms.

variable DHCPLeaseTimer \ -- addr
TICKS at which lease times out.

variable DHCP_T1        \ -- addr
Time to RENEW in ms.

variable DHCP_T2        \ -- addr
time to REBIND in ms.

variable DHCPServer     \ -- addr
DHCP server IP address.

wvariable DHCPflags     \ -- addr
Holds the DHCP flags, set to $8000 to force broadcast responses, or 0 when we can accept unicast responses.

DHCP transient data

These variables contain data generated by the DHCP transactions. It is later manipulated to set system variables and the DHCP control data after validation.

variable GivenIP        \ --
IP address given by DHCP or BOOTP.

variable GivenMask      \ --
IP mask given by DHCP or BOOTP.

variable GivenGateway   \ --
IP gateway given by DHCP or BOOTP.

variable GivenServer    \ --
IP address of DHCP server given by DHCP or BOOTP.

variable GivenLease     \ -- addr
Length of DHCP lease in seconds given by DHCP or BOOTP.

variable GivenT1        \ -- addr
Time to RENEW in ms given by DHCP or BOOTP.

variable GivenT2        \ -- addr
time to REBIND in ms given by DHCP or BOOTP.

variable GivenReply?    \ -- addr
Set non-zero when a DHCP reply has been received, but the data has not yet been been processed. During this time any following DHCP packets will be discarded.

variable GivenSNTP      \ -- addr
SNTP server returned by DHCP.

variable GivenDNS       \ -- addr
DNS server returned by DHCP.

DHCP packet layout

STRUCT /dhcp    \ -- len
BOOTP and DHCP payload structure after the IP and UDP headers.

CREATE BOOTPMAGIC       \ -- addr
Magic number used to identify BOOTP and DHCP packets.

DHCP tools

: umin          \ u1 u2 -- u1|u2
Minimum of two unsigned values.

: TxMs          \ secs -- ms
Return the number of seconds required for expiry of the T1/T2 timer.

: NoDHCPserver  \ --
Set up for no known DHCP server.

: AcceptDHCPin  \ --
Accept incoming DHCP packets by clearing GivenReply?.

: RejectDHCPin  \ --
Reject incoming DHCP packets by setting GivenReply? to an non-valid value.

: AcceptGiven   \ --
Transfer the data given by DHCP transactions to the working data.

DHCP state selection

Each state is controlled by three values defined here.

: goIdle        \ --
Go to the smDHidle state.

: goInit        \ --
Go to the smDHnit state, so starting the DHCP process.

: goSelect      \ --
Go to the smDHselect state.

: goRequest     \ --
Go to the smDHrequest state.

: goARPcheck    \ --
Go to the smDHselect state.

: goBound       \ --
Go to the smDHbound state.

: goRenew       \ --
Go to the smDHrenew state.

: goRebind      \ --
Go to the smDHrebind state.

: goRebooting   \ --
Go to the smDHreboot state.

UDP transmission

: (BOOTPSend)   { *pb ipaddr  -- }
Send BOOTP data as a UDP packet. The parameters are *pb, a pointer to a PBuff containing the UDP data, and ipaddr, the IP address to send to.

Outgoing message tools

Some data is carried as DHCP options in the DHCP packet. Options that may contain an odd number of bytes are forced to a 16 bit boundary.

: c!++          \ addr b -- addr+1
COMPILER word, saves b at addr and increments addr.

: !(n)++        \ addr x -- addr+4
COMPILER word, saves x at addr and increments addr.

: haligned      \ *opt -- *opt'
Align option - assumes that options start 16 bit aligned. If necessary a zero (pad) byte is added.

: INCLUDE_SERVERID_OPT  \ *opt -- *opt'
Add served IP address to the options list.

: INCLUDE_REQ_INF_LEASE_OPT     \ *opt -- *opt'
Add request for an ininite lease to the options list.

: INCLUDE_OFFERED_LEASE_OPT     \ *opt -- *opt'
Add the offered lease to the options list.

: INCLUDE_REQUESTADDR_OPT       \ *opt -- *opt'
Add the offered IP address to the options list.

CREATE ParamRequestList
Parameter Request List that indicates which responses we want.

: INCLUDE_PARAMREQ_OPT  \ *opt -- *opt'
Add parameter request list to the options list.

: addDHCPopts   \ msgtype *bootp --
Add the relevant options for the message type to the options section of the DHCP message.

: UseBroadcast? \ ServerAddr IPAddr -- ServerAddr
If ServerAddr and IPAddr are not the same and IPAddr is not INADDR_BROADCAST (the initialised value) then substitute INADDR_BROADCAST for ServerAddr. So if siaddr is left at 0 in the DHCP offer the Request will be broadcast.

: GetBOOTPServerAddr    \ -- ipaddr
If there is any doubt about the server IP address then broadcast the reply. At this point the routing table only contains entries for the received BOOTP reply and the broadcast address so there is no point in replying to the siaddr or the address in DHCPServerID if it differs from BOOTPServer.

: .DHCPtype     \ msgtype --
Display the message type as text.

: SendDHCP      { msgtype | *pb *bootp -- }
Send a DHCP message. The fields and options are filled in as appropriate for the message type.

: SendDiscover  \ --
Send a DHCPDISCOVER message.

: SendRequest   \ --
Send a DHCPREQUEST message.

: SendDecline   \ --
Send a DHCPDECLINE message.

: doDHidle      \ --
The idle action is just to restart the timer.

: doDHinit      \ --
Send DHCPDISCOVER periodically until we get a response

: doDHselect    \ --
Select is currently a no-op as we take the first offer we receive.

: doDHrequest   \ --
Send DHCPREQUEST periodically until we get a response

: doDHarpcheck  \ --
The DHCP specification says that clients SHOULD check that the IP address given to them is not used elsewhere. If a route is found, the process is restarted.

: doDHbound     \ --
Wait until the T1 timer expires, and go to the RENEWING state.

: doDHrenew     \ --
Send DHCPREQUEST periodically until we get a response.

: doDHrebind    \ --
Send DHCPREQUEST periodically until we get a response.

: doDHrebooting \ --
Send DHCPREQUEST periodically until we get a response.

Receive BOOTP/DHCP packet

DHCP packet reception is treated as a special case because DHCP uses two ports and must operate before the unit's IP address has been set.

: doDHCPopt     \ *option -- *option'
Process the given option if we know about it and step over it. Unknown options are ignored. Values for known options are saved in the Givenxxx variables.

: scanDHCPopts  \ *bootp len --
Given the DHCP data area, parse the DHCP options.

: RxDHCP        { *pb | *udp *bootp len -- }
Process received DHCP and BOOTP packets. When we get here we know this is from port 67 to port 68 and that the Ethernet packet was either addressed to us or was broadcast.

: DHCPpacket?   \ *udp -- flag
Given a UDP header, return non-zero if the packet is a DHCP packet. Used in the UDP layer to route DHCP packets.

State machine initialisation and startup

  ' doDHidle       smDHidle      setDHstate
  ' doDHinit       smDHinit      setDHstate
  ' doDHselect     smDHselect    setDHstate
  ' doDHrequest    smDHrequest   setDHstate
  ' doDHarpcheck   smDHarpcheck  setDHstate
  ' doDHbound      smDHbound     setDHstate
  ' doDHrenew      smDHrenew     setDHstate
  ' doDHrebind     smDHrebind    setDHstate
  ' doDHrebooting  smDHrebooting setDHstate

#68 equ DHCPport        \ -- port
DHCP standard port.

create DHCPServiceStruct        \ -- addr
Holds the UDP Service info to run the DHCP code on receipt of DHCP packets, or idle on timeout. Note that this must match the /UDPservice structure in UDP.fth.

: runDHCP       \ --
Start the DHCP process and wait until a valid IP address has been set.