In Chapter 5 we described how a diskless system, with no knowledge of its IP address, can determine its IP address using RARP when it is bootstrapped. There are two problems with RARP: (1) the only thing returned is the IP address, and (2) since RARP uses a link-layer broadcast, RARP requests are not forwarded by routers (necessitating an RARP server on every physical network). This chapter describes an alternative method for a diskless system to bootstrap itself, called the Bootstrap Protocol, or BOOTP.
BOOTP uses UDP and normally works in conjunction
with TFTP (Chapter 15). RFC 951 [Croft and Gilmore 1985] is the
official specification for BOOTP with clarifications given in
RFC 1542 [Wimer 1993].
16.2 BOOTP Packet Format
BOOTP requests and replies are encapsulated in UDP datagrams, as shown in Figure 16.1.
Figure 16.2 shows the format of the 300-byte BOOTP request and reply.
Opcode is 1 for a request and 2 for a reply. The hardware type field is 1 for a 10 Mbits/sec Ethernet, the same value that is in the field of the same name in an ARP request or reply (Figure 4.3). Similarly, the hardware address length is 6 bytes for an Ethernet.
The hop count is set to 0 by the client, but can be used by a proxy server (described in Section 16.5).
The transaction ID is a 32-bit integer set by the client and returned by the server. This lets the client match a response with a request. The client should set this to a random number for each request.
Number of seconds can be set by the client to the time since it started trying to bootstrap. The servers can look at this value, and perhaps a secondary server for a client won't respond until the number of seconds has exceeded some value, implying that the client's primary server is down.
If the client already knows its IP address, it fills in the client IP address. Otherwise, the client sets this to 0. In the latter case the server fills in your IP address with the client's IP address. The server IP address is filled in by the server. If a proxy server is used (Section 16.5), that proxy server fills in its gateway IP address.
The client must set its client hardware address. Although this is the same value as in the Ethernet header, by placing the field in the UDP datagram also, it is easily available to any user process (e.g., a BOOTP server) that receives the datagram. It is normally much harder (or impossible) for a process reading UDP datagrams to determine the fields in the Ethernet header that carried the UDP datagram.
The server hostname is a null terminated string that is optionally filled in by the server. The server can also fill in the boot filename with the fully qualified, null terminated pathname of a file to bootstrap from.
The vendor-specific area is used for various extensions to BOOTP. Section 16.6 describes some of these extensions.
When a client is bootstrapping using BOOTP (an opcode of 1) the request is normally a link-layer broadcast and the destination IP address in the IP header is normally 255.255.255.255 (the limited broadcast. Section 12.2). The source IP address is often 0.0.0.0 since the client does not know its own IP address yet. Recall from Figure 3.9 that 0.0.0.0 is a valid source IP address when a system is bootstrapping itself.
There are two well-known ports for BOOTP: 67 for the server and 68 for the client. This means the client does not choose an unused ephemeral port, but uses 68 instead. The reason two port numbers were chosen, instead of just one for the server, is that a server's reply can be (but normally isn't) broadcast.
If the server's reply were broadcast, and if the client were to choose an ephemeral port number, these broadcasts would also be received by other applications on other hosts that happen to be using the same ephemeral port number. Hence, it is considered bad form to broadcast to a random (i.e., ephemeral) port number.
If the client also used the server's well-known port (67) as its port, then all servers on the network are awakened to look at each broadcast reply. (If all the servers were awakened, they would examine the opcode, see that it's a reply and not a request, and go back to sleep.) Therefore the choice was made to have all clients use a single well-known port that differs from the server's well-known port.
If multiple clients are bootstrapping at the same
time, and if the server broadcasts the replies, each client sees
the replies intended for the other clients. The clients can use
the transaction ID field in the BOOTP header to match replies
with requests, or the client can examine the returned client hardware
16.3 An Example
Let's look at an example of BOOTP when an X terminal is bootstrapped. Figure 16.3 shows the tcpdump output. (The client's name is proteus and the server's name is mercury. This tcpdump output was obtained on a different network from the one we've been using for all the other examples in the text.)
|1||0.0||0.0.0.0.68 > 255.255.255.255.bootp:
secs: 100 ether 0:0:a7:0:62:7c
|2||0.355446 (0.3554)||mercury.bootp > proteus.68: secs:100 Y:proteus
S: mercury G: mercury ether 0:0:a7:0:62:7c
|3||0.355447 (0.0000)||arp who-has proteus tell 0.0.0.0|
|4||0.851508 (0.4961)||arp who-has proteus tell 0.0.0.0|
|5||1.371070 (0..5196)||arp who-has proteus tell proteus|
|6||1.863226 (0..4922)||proteus.68 > 255.255.255.255.bootp:
secs: 100 ether 0:0:a7:0:62:7c
|7||1.71038 (0..0078)||mercury.bootp > proteus.68: secsilOO Y:proteus
S: mercury G: mercury ether 0:0:a7:0;62:7c
|8||3.871038 (2.0000)||proteus.68 > 255.255.255.255.bootp:|
secs: 1OO ether 0:0:a7:0:62:7c
|9||3.878850 (0.0078)||mercury.bootp > proteus. 68: secs: 100 Y:proteus
S: mercury G: mercury ether 0:0:a7:0:62:7c
|10||5.925786 (2.0469)||arp who-has mercury tell proteus|
|11||5.929692 (0.0039)||arp reply mercury is-at 8:0:2b:28:eb:1d|
|12||5.929694 (0.0000)||proteus. tftp > mercury, tftp: 37 RRQ
|13||5.996094 (0.0664)||mercury.2352 > proteus.tftp: 516 DATA block 1|
|14||6.000000 (0.0039)||proteus. tftp > mercury. 2352: 4 ACK|
|many lines deleted here|
|15||14.980472 (8.9805)||mercury.2352 > proteus.tftp: 516 DATA block 2510|
|16||14.984376 (0.0039)||proteus. tftp > mercury. 2352: 4 ACK|
|17||14.984377 (0.0000)||mercury. 2352 > proteus. tftp: 228 DATA block 2464|
|18||14.984378 (0.0000)||proteus.tftp > mercury.2352: 4 ACK|
In line 1 we see the client's request from 0.0.0.0.68, destined for 255.255.255.255.67. The only fields the client has filled in are the number of seconds and its Ethernet address. We'll see that this client always sets the number of seconds to 100. The hop count and transaction ID are both 0 since they are not output by tcpdump. (A transaction ID of 0 means the client ignores the field, since it would set this field to a random number if it was going to verify the returned value in the response.)
Line 2 is the reply from the server. The fields filled in by the server are the client's IP address (which tcpdump prints as the name proteus), the server's IP address (printed as the name mercury), the IP address of a gateway (printed as the name mercury), and the name of a boot file.
After receiving the BOOTP reply, the client immediately issues an ARP request to see if anyone else on the network has its IP address. The name proteus following who-has corresponds to the target IP address (Figure 4.3), and the sender's IP address is set to 0.0.0.0. It sends another identical ARP request 0.5 second later, and another one 0.5 second after that. In the third ARP request (line 5) it changes the sender's IP address to be its own IP address. This is a gratuitous ARP request (Section 4.7).
Line 6 shows that the client waits another 0.5 second and broadcasts another BOOTP request. The only difference between this request and line 1 is that now the client puts its own IP address in the IP header. It receives the same reply from the same server (line 7). The client waits another 2 seconds and broadcasts yet another BOOTP request (line 8) and receives the same reply from the same server.
The client then waits another 2 seconds and sends an ARP request for its server mercury (line 10). The ARP reply is received and the client immediately issues a TFTP read request for its boot file (line 12). What follows are 2464 TFTP data packets and acknowledgments. The amount of data transferred is 512 x 2463+224 = 1,261,280 bytes. This loads the operating system into the X terminal. We have deleted most of the TFTP lines from Figure 16.3.
One thing to notice, when comparing this TFTP exchange with Figure 15.2, is that here the client uses the TFTP well-known port (69) for the entire transfer. Since one of the two partners is using port 69, tcpdump knows that the packets are TFTP messages, so it is able to interpret each packet using the TFTP protocol. This is why Figure 16.3 indicates which packets contain data, which contain acknowledgments, and what the block number is for each packet. We didn't get this additional information in Figure 15.2 because neither end was using TFTP's well-known port for the data transfer. Normally the TFTP client cannot use TFTP's well-known port, since that port is used by the server on a multiuser system. But here the system is being bootstrapped, so a TFTP server is not provided, allowing the client to use the port for the duration of the transfer. This also implies that the TFTP server on mercury doesn't care what the client's port number is-it sends the data to the client's port, whatever that happens to be.
From Figure 16.3 we see that 1,261,280 bytes are transferred in 9 seconds. This is a rate of about 140,000 bytes per second. While this is slower than most FTP file transfers across an Ethernet, it is not that bad for a simple stop-and-wait protocol such as TFTP.
What follows as this X terminal is bootstrapped are
additional TFTP transfers of the terminal's font files, some DNS
name server queries, and then the initialization of the X protocol.
The total time in Figure 16.3 was almost 15 seconds, and another
6 seconds is taken for the remaining steps. This gives a total
of 21 seconds to bootstrap the diskless X terminal.
16.4 BOOTP Server Design
The BOOTP client is normally provided in read-only memory on the diskless system. It is interesting to see how the server is normally implemented.
First, the server reads UDP datagrams from its well-known port (67). Nothing special is required. This differs from an RARP server (Section 5.4), which we said had to read Ethernet frames with a type field of "RARP request." The BOOTP protocol also made it easy for the server to obtain the client's hardware address, by placing it into the BOOTP packet (Figure 16.2).
An interesting problem arises: how can the server send a response directly back to the client? The response is a UDP datagram, and the server knows the client's IP address (probably read from a configuration file on the server). But if the client sends a UDP datagram to that IP address (the normal way UDP output is handled), the server's host will probably issue an ARP request for that IP address. But the client can't respond to the ARP request since it doesn't know its IP address yet! (This is called the "chicken and egg" issue in RFC 951.)
There are two solutions. The first, commonly used by Unix servers, is tor the server to issue an ioctl(2) request to the kernel, to place an entry into the ARP cache for this client. (This is what the arp -s command does. Section 4.8.) The server can do this since it knows the client's hardware address and IP address. This means that when the server sends the UDP datagram (the BOOTP reply), the server's ARP module will find the client's IP address in the ARP cache.
An alternative solution is tor the server to broadcast
the BOOTP reply, instead of sending it directly to the client.
Since reducing the number of broadcasts on a network is always
desirable, this solution should be used only if the server cannot
make an entry into its ARP cache. Normally it requires superuser
permission to make an entry into the ARP cache, requiring a broadcast
reply if the server is nonprivileged.
16.5 BOOTP Through a Router
We said in Section 5.4 that one of the drawbacks of RARP is that it uses a link-layer broadcast, which is normally not forwarded by a router. This required an RARP server on each physical network. BOOTP can be used through a router, if supported by the router. (Most major router vendors do support this feature.)
This is mainly intended for diskless routers, because if a multiuser system with a disk is used as a router, it can probably run a BOOTP server itself. Alternatively, the common Unix BOOTP server (Appendix F) supports this relay mode, but again, if you can run a BOOTP server on the physical network, there's normally no need to forward the requests to yet another server on another network.
What happens is that the router (also called the
"BOOTP relay agent") listens tor BOOTP requests on the
server's well-known port (67). When a request is received, the
relay agent places its IP address into the gateway IP address
field in the BOOTP request, and sends the request to the real
BOOTP server. (The address placed by the relay agent into the
gateway field is the IP address of the interface on which the
request was received.) The relay agent also increments the hops
field by one. (This is to prevent infinite loops in case the request
is reforwarded. RFC 951 mentions that the request should probably
be thrown away if the hop count reaches 3.) Since the outgoing
request is a unicast datagram (as opposed to the original client
request that was broadcast), it can follow any route to the real
BOOTP server, passing through other routers. The real server gets
the request, forms the BOOTP reply, and sends it back to the relay
agent, not the client. The real server knows that the request
has been forwarded, since the gateway field in the request is
nonzero. The relay agent receives the reply and sends it to the
16.6 Vendor-Specific Information
In Figure 16.2 we showed a 64-byte vendor-specific area, RFC 1533 [Alexander and Droms 1993] defines the format of this area. This area contains optional information for the server to return to the client.
If information is provided, the first 4 bytes of this area are set to the IP address 18.104.22.168. This is called the magic cookie and means there is information in the area.
The rest of this area is a list of items. Each item begins with a 1-byte tag field. Two of the items consist of just the tag field: a tag of 0 is a pad byte (to force following items to preferred byte boundaries), and a tag of 255 marks the end of the items. Any bytes remaining in this area after the first end byte should be set to this value (255).
Other than these two 1-byte items, the remaining items consist of a single length byte, followed by the information. Figure 16.4 shows the format of some of the items in the vendor-specific area.
The subnet mask and time value are really fixed-length items because their values always occupy 4 bytes. The time offset is the number of seconds since midnight January 1,1900, UTC.
The gateway item is an example of a variable-length item. The length is always a multiple of 4, and the values are the 32-bit IP addresses of one or more gateways (routers) for the client to use. The first one returned must be the preferred gateway.
There are 14 other items defined in RFC 1533. Probably the most important is the IP address of a DNS name server, with a tag value of 6. Other items return the IP address of a printer server, the IP address of a time server, and so on. Refer to the RFC for all the details.
Returning to our example in Figure 16.3, we never saw an ICMP address mask request (Section 6.3) that would have been broadcast by the client to find its subnet mask. Although it wasn't output by tcpdump, we can probably assume that the client's subnet mask was returned in the vendor-specific area of the BOOTP reply.
The Host Requirements RFC recommends that a system using BOOTP obtain its subnet mask using BOOTP, not ICMP.
The size of the vendor-specific area is limited to
64 bytes. This is a constraint for some applications. A new protocol
named DHCP (Dynamic Host Configuration Protocol) is built on,
but replaces, BOOTP. DHCP extends this area to 312 bytes and is
defined in RFC 1541 [Droms 1993].
BOOTP uses UDP and is intended as an alternative to RARP for bootstrapping a diskless system to find its IP address. BOOTP can also return additional information, such as the IP address of a router, the client's subnet mask, and the IP address of a name server.
Since BOOTP is used in the bootstrap process, a diskless system needs the following protocols implemented in read-only memory: BOOTP, TFTP, UDP, IP, and a device driver for the local network.
The implementation of a BOOTP server is easier than an RARP server, since BOOTP requests and replies are in UDP datagrams, not special link-layer frames. A router can also serve as a proxy agent for a real BOOTP server, forwarding client requests to the real server on a different network.
16.1 We've said that one advantage of BOOTP over RARP is that BOOTP can work through routers, whereas RARP, which is a link-layer broadcast, cannot. Yet in Section 16.5 we had to define special ways for BOOTP to work through a router. What would happen if a capability were added to routers allowing them to forward RARP requests?
16.2 We said that a BOOTP client must use the transaction ID to match responses with requests, in case there are multiple clients bootstrapping at the same time from a server that broadcasts replies. But in Figure 16.3 the transaction ID is 0, implying that this client ignores the transaction ID. How do you think this client matches the responses with its requests?