Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | \ ========== Copyright Header Begin ========================================== |
2 | \ | |
3 | \ Hypervisor Software File: dhcp.fth | |
4 | \ | |
5 | \ Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | \ | |
7 | \ - Do no alter or remove copyright notices | |
8 | \ | |
9 | \ - Redistribution and use of this software in source and binary forms, with | |
10 | \ or without modification, are permitted provided that the following | |
11 | \ conditions are met: | |
12 | \ | |
13 | \ - Redistribution of source code must retain the above copyright notice, | |
14 | \ this list of conditions and the following disclaimer. | |
15 | \ | |
16 | \ - Redistribution in binary form must reproduce the above copyright notice, | |
17 | \ this list of conditions and the following disclaimer in the | |
18 | \ documentation and/or other materials provided with the distribution. | |
19 | \ | |
20 | \ Neither the name of Sun Microsystems, Inc. or the names of contributors | |
21 | \ may be used to endorse or promote products derived from this software | |
22 | \ without specific prior written permission. | |
23 | \ | |
24 | \ This software is provided "AS IS," without a warranty of any kind. | |
25 | \ ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
26 | \ INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
27 | \ PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
28 | \ MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
29 | \ ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
30 | \ DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
31 | \ OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
32 | \ FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
33 | \ DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
34 | \ ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
35 | \ SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
36 | \ | |
37 | \ You acknowledge that this software is not designed, licensed or | |
38 | \ intended for use in the design, construction, operation or maintenance of | |
39 | \ any nuclear facility. | |
40 | \ | |
41 | \ ========== Copyright Header End ============================================ | |
42 | id: @(#)dhcp.fth 1.1 04/09/07 | |
43 | purpose: DHCP support | |
44 | copyright: Copyright 2004 Sun Microsystems, Inc. All Rights Reserved | |
45 | copyright: Use is subject to license terms. | |
46 | ||
47 | \ RFC 2131: Dynamic Host Configuration Protocol | |
48 | \ RFC 2132: DHCP Options and BOOTP Vendor Extensions | |
49 | \ RFC 1534: Interoperation Between DHCP and BOOTP | |
50 | ||
51 | fload ${BP}/pkg/netinet/dhcp-h.fth | |
52 | ||
53 | headerless | |
54 | ||
55 | 0 instance value dhcp-sockid | |
56 | /insock instance buffer: dhcp-srv-addr | |
57 | /insock instance buffer: dhcp-cli-addr | |
58 | ||
59 | d# 32 instance buffer: dhcp-classid | |
60 | ||
61 | /ip-addr instance buffer: dhcp-offered-ip | |
62 | /ip-addr instance buffer: dhcp-server-id | |
63 | ||
64 | 0 instance value dhcp-sndbuf | |
65 | instance variable dhcp-sndbuflen | |
66 | 0 instance value dhcp-rcvbuf | |
67 | ||
68 | 0 instance value chosen-bootreply \ Best offer | |
69 | 0 instance value /chosen-bootreply | |
70 | 0 instance value best-offer-points | |
71 | false instance value bootp-config? \ Is best offer a BOOTREPLY? | |
72 | ||
73 | /timer instance buffer: dhcp-timer | |
74 | ||
75 | instance variable dhcp-state \ DHCP FSM state | |
76 | instance variable dhcp-xid \ Transaction identifier | |
77 | ||
78 | \ Read a byte and advance the pointer. | |
79 | : c@++ ( adr -- adr+1 c ) dup ca1+ swap c@ ; | |
80 | ||
81 | \ Find an option in the specified area of a packet. | |
82 | : (find-dhcp-option) ( adr len option# -- optadr optlen true | false ) | |
83 | >r over ca+ swap ( end start ) ( r: option# ) | |
84 | begin 2dup > while | |
85 | c@++ case | |
86 | CD_PAD of endof | |
87 | CD_END of r> 3drop false exit endof | |
88 | r@ of r> drop nip c@++ true exit endof | |
89 | ( default ) | |
90 | drop c@++ ca+ 0 | |
91 | endcase | |
92 | repeat | |
93 | r> 3drop false | |
94 | ; | |
95 | ||
96 | \ Find the specified DHCP option. Options in a DHCP message may extend | |
97 | \ to the 'sname' and 'file' fields if an 'option overload' option is | |
98 | \ present in the variable length options field. | |
99 | : find-dhcp-option ( pkt len option# -- optadr optlen true | false ) | |
100 | 2 pick >dhcp-cookie ntohl@ BOOTMAGIC <> if | |
101 | 3drop false exit | |
102 | then | |
103 | >r ( pkt,len ) ( r: opt# ) | |
104 | over >dhcp-options over /dhcp-header - ( pkt,len opts,len ) | |
105 | 2dup r@ (find-dhcp-option) if ( pkt,len opts,len optadr,len ) | |
106 | 2swap 2drop 2swap 2drop true ( optadr,len true ) | |
107 | else ( pkt,len opts,len ) | |
108 | CD_OPTION_OVERLOAD (find-dhcp-option) if ( pkt,len ovloption,len ) | |
109 | drop nip c@ dup 1 3 between if ( pkt ovlopt-value ) | |
110 | case | |
111 | 1 of >dhcp-file d# 128 endof | |
112 | 2 of >dhcp-sname d# 64 endof | |
113 | 3 of >dhcp-sname d# 192 endof | |
114 | endcase ( adr,len ) | |
115 | r@ (find-dhcp-option) ( optadr,len true | false ) | |
116 | else ( pkt n ) | |
117 | 2drop false ( false ) | |
118 | then ( optadr,len true | false ) | |
119 | else ( pkt,len ) | |
120 | 2drop false ( false ) | |
121 | then ( optadr,len true | false ) | |
122 | then ( optadr,len true | false ) | |
123 | r> drop ( optadr,len true | false ) | |
124 | ; | |
125 | ||
126 | \ Determine if the packet contains a specific option. | |
127 | : dhcp-option-found? ( pkt len option# -- found? ) | |
128 | find-dhcp-option if 2drop true else false then | |
129 | ; | |
130 | ||
131 | \ Find a vendor specific option. | |
132 | : find-vendor-option ( pkt len vendor-option# -- optadr optlen true | false ) | |
133 | >r ( pkt len ) ( r: opt# ) | |
134 | CD_VENDOR_SPEC find-dhcp-option if ( encap-options,len ) | |
135 | r@ (find-dhcp-option) ( optadr opten true | false ) | |
136 | else ( ) | |
137 | false ( false ) | |
138 | then ( optadr optlen true | false ) | |
139 | r> drop ( optadr optlen true | false ) | |
140 | ; | |
141 | ||
142 | \ Add an option with a 8-bit value to the packet | |
143 | : add-dhcpopt-byte ( adr code byte -- adr' ) | |
144 | rot >r ( code byte ) ( r: adr ) | |
145 | swap r@ >dhcpopt-code c! ( byte ) | |
146 | /c r@ >dhcpopt-len c! ( byte ) | |
147 | r@ >dhcpopt-data c! ( ) | |
148 | r> 3 ca+ ( adr' ) ( r: ) | |
149 | ; | |
150 | ||
151 | \ Add an option with a 16-bit value to the packet | |
152 | : add-dhcpopt-word ( adr code value -- adr' ) | |
153 | rot >r ( code value ) ( r: adr ) | |
154 | swap r@ >dhcpopt-code c! ( value ) | |
155 | /w r@ >dhcpopt-len c! ( value ) | |
156 | r@ >dhcpopt-data htonw! ( ) | |
157 | r> 4 ca+ ( adr' ) ( r: ) | |
158 | ; | |
159 | ||
160 | \ Add an option encoding a stream of bytes to the packet. | |
161 | : add-dhcpopt-bytes ( adr code data len -- adr' ) | |
162 | ?dup if ( adr code data len ) | |
163 | 3 roll >r ( code data len ) ( r: adr ) | |
164 | rot r@ >dhcpopt-code c! ( data len ) | |
165 | dup r@ >dhcpopt-len c! ( data len ) | |
166 | tuck r@ >dhcpopt-data swap move ( len ) | |
167 | r> swap 2+ ca+ ( adr' ) ( r: ) | |
168 | else ( adr code data ) | |
169 | 2drop ( adr' ) | |
170 | then ( adr' ) | |
171 | ; | |
172 | ||
173 | \ Maximum length of the DHCP message we are willing to accept. | |
174 | : /dhcp-maxmsg ( -- n ) if-mtu@ /udpip-header - ; | |
175 | ||
176 | \ The vendor class identifier is constructed from the root node's | |
177 | \ "name" property, with commas (,) replaced with periods (.). | |
178 | : init-dhcp-vendor-classid ( -- ) | |
179 | " /" " name" get-property decode-string 2swap 2drop ( $ ) | |
180 | dhcp-classid pack count bounds ?do ( ) | |
181 | i c@ ascii , = if ascii . i c! then ( ) | |
182 | loop ( ) | |
183 | ; | |
184 | ||
185 | \ DHCP initialization. Create a socket, initialize the client and | |
186 | \ server socket address structures, and bind local address to the | |
187 | \ socket. Allocate buffers to send/receive/store DHCP packets and | |
188 | \ determine the vendor class identifier to use. | |
189 | ||
190 | : dhcp-init ( -- ) | |
191 | AF_INET SOCK_DGRAM IPPROTO_UDP socreate to dhcp-sockid ( ) | |
192 | ||
193 | dhcp-cli-addr my-ip-addr IPPORT_BOOTPC insock-init ( ) | |
194 | dhcp-srv-addr inaddr-broadcast IPPORT_BOOTPS insock-init ( ) | |
195 | ||
196 | /dhcp-maxmsg dup alloc-mem to dhcp-sndbuf ( n ) | |
197 | dup alloc-mem to dhcp-rcvbuf ( n ) | |
198 | alloc-mem to chosen-bootreply ( ) | |
199 | ||
200 | dhcp-sockid dhcp-cli-addr /insock sobind ( ) | |
201 | ||
202 | init-dhcp-vendor-classid ( ) | |
203 | ; | |
204 | ||
205 | \ DHCP state cleanup. Free the DHCP packet buffers and close the | |
206 | \ connection. | |
207 | ||
208 | : dhcp-close ( -- ) | |
209 | dhcp-sndbuf /dhcp-maxmsg tuck free-mem ( n ) | |
210 | dhcp-rcvbuf over free-mem ( n ) | |
211 | chosen-bootreply swap free-mem ( ) | |
212 | dhcp-sockid soclose ( ) | |
213 | ; | |
214 | ||
215 | \ Filling in DHCP message options. All DHCP messages include the DHCP | |
216 | \ message type and the client identifier (if one is in use). DHCP_REQUEST | |
217 | \ and DHCP_DECLINE messages must fill the DHCP server identifier and the | |
218 | \ offered IP address. Messages other than DHCP_DECLINE must fill in the | |
219 | \ DHCP vendor class identifier, the maximum DHCP message size we are | |
220 | \ willing to accept, and the list of requested parameters. We explicitly | |
221 | \ request values for subnet mask (Option 1), Router (Option 3), Hostname | |
222 | \ (Option 12), and Vendor specific information (Option 43). | |
223 | ||
224 | : add-dhcp-options ( pkt type -- pktlen ) | |
225 | >r dup >dhcp-options ( pkt adr ) ( r: type ) | |
226 | ||
227 | CD_DHCP_TYPE r@ add-dhcpopt-byte | |
228 | CD_CLIENTID client-id count add-dhcpopt-bytes | |
229 | ||
230 | r@ DHCP_DECLINE <> if | |
231 | CD_CLASSID dhcp-classid count add-dhcpopt-bytes | |
232 | CD_REQUEST_LIST " "(01 03 0c 2b)" add-dhcpopt-bytes | |
233 | CD_MAXMSG_SIZE /dhcp-maxmsg add-dhcpopt-word | |
234 | CD_HOSTNAME hostname count add-dhcpopt-bytes | |
235 | then | |
236 | ||
237 | r> dup DHCP_REQUEST = swap DHCP_DECLINE = or if | |
238 | CD_SERVER_ID dhcp-server-id /ip-addr add-dhcpopt-bytes | |
239 | CD_REQ_IPADDR dhcp-offered-ip /ip-addr add-dhcpopt-bytes | |
240 | then | |
241 | ||
242 | CD_END over c! ca1+ swap - ( pktlen ) | |
243 | ; | |
244 | ||
245 | \ Common code to construct DHCP message to be sent. | |
246 | : init-dhcp-packet ( type -- ) | |
247 | dhcp-sndbuf dup >r /dhcp-maxmsg erase ( type ) ( r: pkt ) | |
248 | ||
249 | BOOTREQUEST r@ >dhcp-op c! | |
250 | if-htype@ r@ >dhcp-htype c! | |
251 | if-addrlen@ r@ >dhcp-hlen c! | |
252 | if-hwaddr r@ >dhcp-chaddr copy-hw-addr | |
253 | dhcp-xid @ r@ >dhcp-xid htonl! | |
254 | my-ip-addr r@ >dhcp-ciaddr copy-ip-addr | |
255 | BOOTMAGIC r@ >dhcp-cookie htonl! | |
256 | ||
257 | r> swap add-dhcp-options dhcp-sndbuflen ! ( ) ( r: ) | |
258 | ; | |
259 | ||
260 | \ Transmit formatted DHCP message. | |
261 | : send-dhcp-packet ( -- ) | |
262 | dhcp-sockid dhcp-sndbuf dhcp-sndbuflen @ ( sockid pkt len ) | |
263 | DHCP_MIN_PKTLEN max ( sockid pkt len' ) | |
264 | 0 dhcp-srv-addr /insock sosendto drop ( ) | |
265 | ; | |
266 | ||
267 | \ Managing DHCP retransmissions. Use a randomized exponential backoff | |
268 | \ to determine delay between retransmissions. On retries, the delay is | |
269 | \ doubled (for a maximum of 64 seconds), and randomized by a random | |
270 | \ number in the range +/-1.023 seconds. | |
271 | ||
272 | : dhcp-backoff ( -- ) | |
273 | dhcp-timer clear-timer 2* d# 64000 min ( timeout ) | |
274 | random dup d# 22 rshift swap 0< if negate then + ( timeout' ) | |
275 | dhcp-timer swap set-timer ( ) | |
276 | ; | |
277 | ||
278 | : retransmit-dhcp-packet ( -- ) | |
279 | send-dhcp-packet dhcp-backoff | |
280 | ; | |
281 | ||
282 | \ Determine DHCP packet type. BOOTP packets are tagged as type 0. | |
283 | : dhcp-packet-type ( pkt len -- type ) | |
284 | over >dhcp-cookie ntohl@ BOOTMAGIC = if ( pkt,len ) | |
285 | CD_DHCP_TYPE find-dhcp-option if drop c@ else 0 then ( type ) | |
286 | else ( pkt,len ) | |
287 | 2drop 0 ( 0 ) | |
288 | then ( type ) | |
289 | ; | |
290 | ||
291 | \ Receive data arriving on the socket. | |
292 | : (receive-dhcp-packet) ( -- pkt len ) | |
293 | dhcp-sockid dhcp-rcvbuf tuck /dhcp-maxmsg 0 0 0 sorecvfrom | |
294 | ; | |
295 | ||
296 | \ Incoming responses must be BOOTREPLY packets, and the xid should match. | |
297 | : receive-dhcp-packet ( -- pkt len true | false ) | |
298 | (receive-dhcp-packet) dup 0= if 2drop false exit then | |
299 | over >dhcp-op c@ BOOTREPLY <> if 2drop false exit then | |
300 | over >dhcp-xid ntohl@ dhcp-xid @ <> if 2drop false exit then | |
301 | true | |
302 | ; | |
303 | ||
304 | \ Receive OFFER messages from the server. BOOTP responses must be | |
305 | \ accepted as well. | |
306 | : receive-dhcp-offer ( -- pkt len true | false ) | |
307 | receive-dhcp-packet 0= if false exit then ( pkt len ) | |
308 | 2dup dhcp-packet-type ( pkt len type ) | |
309 | dup 0= if drop true exit then ( pkt len type ) | |
310 | DHCP_OFFER <> if 2drop false exit then ( pkt len ) | |
311 | 2dup CD_SERVER_ID find-dhcp-option if ( pkt len optadr,len ) | |
312 | 2drop true ( pkt len true ) | |
313 | else ( pkt len ) | |
314 | ." Ignoring OFFER with missing DHCP server identifier" cr | |
315 | 2drop false ( false ) | |
316 | then ( pkt len true | false ) | |
317 | ; | |
318 | ||
319 | \ Receive an ACK/NAK response from the server. | |
320 | : receive-dhcp-ack/nak ( -- pkt len true | false ) | |
321 | receive-dhcp-packet 0= if false exit then ( pkt len ) | |
322 | 2dup dhcp-packet-type ( pkt len type ) | |
323 | dup DHCP_ACK <> swap DHCP_NAK <> and if ( pkt len ) | |
324 | 2drop false ( false ) | |
325 | else ( pkt len ) | |
326 | true ( pkt len true ) | |
327 | then ( pkt len true | false ) | |
328 | ; | |
329 | ||
330 | \ Receive an ACK in response to the INFORM message we sent. Responses | |
331 | \ from a BOOTP server must be accepted as well. | |
332 | : receive-dhcpinform-response ( -- pkt len true | false ) | |
333 | receive-dhcp-packet 0= if false exit then ( pkt len ) | |
334 | 2dup dhcp-packet-type ( pkt len type ) | |
335 | dup DHCP_ACK <> swap 0<> and if ( pkt len ) | |
336 | 2drop false ( false ) | |
337 | else ( pkt len ) | |
338 | true ( pkt len true ) | |
339 | then ( pkt len true | false ) | |
340 | ; | |
341 | ||
342 | \ Stash away a response received from the server. | |
343 | : store-dhcp-response ( adr len -- ) | |
344 | dup >r chosen-bootreply swap move r> to /chosen-bootreply | |
345 | ; | |
346 | ||
347 | \ Wait for the expected response. | |
348 | : (dhcp-response-wait) ( xt -- pkt len true | false ) | |
349 | begin ( xt ) | |
350 | dhcp-timer timer-expired? ( xt timed-out? ) | |
351 | 0= while ( xt ) | |
352 | dup execute if ( xt pkt len ) | |
353 | rot drop true exit ( pkt len true ) | |
354 | then ( xt ) | |
355 | repeat ( xt ) | |
356 | drop false ( false ) | |
357 | ; | |
358 | ||
359 | \ Wait for expected response, retransmitting the sent packet if necessary. | |
360 | : dhcp-response-wait ( xt ntries -- pkt len true | false ) | |
361 | begin ( xt ntries ) | |
362 | over (dhcp-response-wait) if ( xt ntries pkt len ) | |
363 | 2swap 2drop true exit ( pkt len true ) | |
364 | then ( xt ntries ) | |
365 | ." Timed out waiting for BOOTP/DHCP reply" cr ( xt ntries ) | |
366 | 1- ( xt ntries' ) | |
367 | dup 0 u> while ( xt ntries' ) | |
368 | retransmit-dhcp-packet ( xt ntries' ) | |
369 | repeat ( xt ntries' ) | |
370 | 2drop false ( false ) | |
371 | ; | |
372 | ||
373 | \ Wait for a minimum of 10 seconds if restarting the configuration | |
374 | \ process after a failure. | |
375 | : dhcp-restart ( -- ) | |
376 | d# 10.000 ms DHCPS_INIT dhcp-state ! | |
377 | ; | |
378 | ||
379 | \ INIT state processing. Select a random transaction identifier to use | |
380 | \ in DHCP packets. Move to INFORM state if using an externally configured | |
381 | \ IP address; else move to SELECTING state. | |
382 | ||
383 | : dhcps-init ( -- ) | |
384 | random dhcp-xid l! | |
385 | my-ip-addr inaddr-any? if DHCPS_SELECTING else DHCPS_INFORM then | |
386 | dhcp-state ! | |
387 | ; | |
388 | ||
389 | \ INFORM state processing. Send a DHCPINFORM and wait for a DHCPACK. | |
390 | \ If a DHCPACK is not received even after 4 retries, enter CONFIGURED | |
391 | \ state and hope for the best. | |
392 | ||
393 | : dhcps-inform ( -- ) | |
394 | DHCP_INFORM init-dhcp-packet send-dhcp-packet ( ) | |
395 | dhcp-timer d# 4000 set-timer ( ) | |
396 | ['] receive-dhcpinform-response 4 dhcp-response-wait if ( pkt len ) | |
397 | store-dhcp-response ( ) | |
398 | DHCPS_BOUND dhcp-state ! ( ) | |
399 | else | |
400 | DHCPS_CONFIGURED dhcp-state ! ( ) | |
401 | then ( ) | |
402 | ; | |
403 | ||
404 | \ Selecting the best DHCPOFFER. We select the best OFFER from the possibly | |
405 | \ many incoming OFFER messages. OFFERs are evaluated using a points-based | |
406 | \ system we share with inetboot/wanboot. We prefer DHCP configurations | |
407 | \ which provide the most configuration information. | |
408 | ||
409 | : compute-offer-points ( pkt len -- #points ) | |
410 | over >dhcp-cookie ntohl@ BOOTMAGIC <> if | |
411 | 2drop 0 exit | |
412 | then | |
413 | ||
414 | d# 5 | |
415 | CD_DHCP_TYPE 2over rot dhcp-option-found? if d# 30 + then | |
416 | CD_VENDOR_SPEC 2over rot dhcp-option-found? if d# 80 + then | |
417 | CD_SUBNETMASK 2over rot dhcp-option-found? if 1+ then | |
418 | CD_ROUTER 2over rot dhcp-option-found? if 1+ then | |
419 | CD_HOSTNAME 2over rot dhcp-option-found? if d# 5 + then | |
420 | ||
421 | CD_OPTION_OVERLOAD 2over rot dhcp-option-found? 0= if | |
422 | 2 pick >dhcp-sname c@ 0<> if d# 10 + then | |
423 | 2 pick >dhcp-file c@ 0<> if d# 5 + then | |
424 | then | |
425 | ||
426 | 2 pick >dhcp-siaddr inaddr-any? 0= if d# 10 + then | |
427 | nip nip | |
428 | ; | |
429 | ||
430 | \ Process incoming offer and keep track of the best offer received. | |
431 | : process-offer ( pkt len -- ) | |
432 | 2dup compute-offer-points dup best-offer-points <= if ( pkt len pts ) | |
433 | 3drop exit ( ) | |
434 | then ( pkt len pts ) | |
435 | to best-offer-points ( pkt len ) | |
436 | store-dhcp-response ( ) | |
437 | ; | |
438 | ||
439 | \ Record information from the OFFER we selected. | |
440 | : process-best-offer ( -- ) | |
441 | chosen-bootreply /chosen-bootreply ( pkt len ) | |
442 | 2dup dhcp-packet-type 0= to bootp-config? ( pkt len ) | |
443 | over >dhcp-yiaddr dhcp-offered-ip copy-ip-addr ( pkt len ) | |
444 | CD_SERVER_ID find-dhcp-option if ( opt-adr,len ) | |
445 | drop dhcp-server-id copy-ip-addr ( ) | |
446 | then ( ) | |
447 | ; | |
448 | ||
449 | \ SELECTING state processing. Broadcast a DISCOVER and sift through | |
450 | \ the OFFERs to select the best one. We "collect" OFFERs for a period | |
451 | \ of 4 seconds after the first OFFER is received. If the best offer | |
452 | \ is a BOOTP configuration, move to the BOUND state, else move to the | |
453 | \ REQUESTING state. | |
454 | ||
455 | : dhcps-selecting ( -- ) | |
456 | DHCP_DISCOVER init-dhcp-packet send-dhcp-packet ( ) | |
457 | dhcp-timer d# 8000 set-timer ( ) | |
458 | ['] receive-dhcp-offer dhcp-max-retries ( xt ntries ) | |
459 | dhcp-response-wait 0= if ( ) | |
460 | ." No DHCP response after" dhcp-max-retries .d ." tries" cr | |
461 | -1 throw | |
462 | then ( pkt len ) | |
463 | process-offer ( ) | |
464 | dhcp-timer d# 4000 set-timer ( ) | |
465 | begin ( ) | |
466 | ['] receive-dhcp-offer (dhcp-response-wait) | |
467 | while ( pkt len ) | |
468 | process-offer ( ) | |
469 | repeat ( ) | |
470 | process-best-offer ( ) | |
471 | bootp-config? if DHCPS_BOUND else DHCPS_REQUESTING then dhcp-state ! | |
472 | ; | |
473 | ||
474 | \ REQUESTING state processing. Broadcast a DHCPREQUEST requesting offered | |
475 | \ parameters from one server (and implicitly declining OFFERS from other | |
476 | \ servers) and wait for a DHCPACK. On arrival of a DHCPACK, move to the | |
477 | \ VERIFYING state to perform a check on the offered IP address. | |
478 | \ | |
479 | \ If a DHCPNAK is received, or there is no response to the DHCPREQUEST | |
480 | \ even after 4 retries, restart the initialization process. | |
481 | ||
482 | : dhcps-requesting ( -- ) | |
483 | DHCP_REQUEST init-dhcp-packet send-dhcp-packet ( ) | |
484 | dhcp-timer d# 4000 set-timer ( ) | |
485 | ['] receive-dhcp-ack/nak 4 dhcp-response-wait if ( pkt len ) | |
486 | 2dup dhcp-packet-type DHCP_ACK = if ( pkt len ) | |
487 | store-dhcp-response ( ) | |
488 | DHCPS_VERIFYING dhcp-state ! ( ) | |
489 | else ( pkt len ) | |
490 | 2drop dhcp-restart ( ) | |
491 | then ( ) | |
492 | else ( ) | |
493 | dhcp-restart ( ) | |
494 | then ( ) | |
495 | ; | |
496 | ||
497 | \ VERIFYING state processing. Issue an ARP request for the offered | |
498 | \ IP address. If the IP address appears to be in use, send a DHCPDECLINE | |
499 | \ message to the server and restart the initialization process; else, | |
500 | \ move to the BOUND state. | |
501 | ||
502 | : dhcps-verifying ( -- ) | |
503 | dhcp-offered-ip 1 arp-check if | |
504 | ." IP Address " dhcp-offered-ip .ipaddr ." already in use" cr | |
505 | DHCP_DECLINE init-dhcp-packet send-dhcp-packet | |
506 | dhcp-restart | |
507 | else | |
508 | dhcp-offered-ip my-ip-addr copy-ip-addr | |
509 | DHCPS_BOUND dhcp-state ! | |
510 | then | |
511 | ; | |
512 | ||
513 | \ BOUND state processing. Extract n/w and boot configuration information | |
514 | \ we care about from the DHCP/BOOTP response. We dont deal with IP | |
515 | \ address lease times, pushing that responsibility to DHCP modules | |
516 | \ in the OS. | |
517 | ||
518 | : dhcp-set-bootsrv,file ( pkt len -- ) | |
519 | ||
520 | \ Check if boot server and filename are known. | |
521 | bootfile count is-uri? if 2drop exit then ( pkt len ) | |
522 | bootfile count nip if ( pkt len ) | |
523 | tftp-server-ip inaddr-any? 0= if 2drop exit then | |
524 | then ( pkt len ) | |
525 | ||
526 | \ If the package arguments did not specify use of TFTP, and | |
527 | \ the DHCP response provides a (TFTP or HTTP) URI, set the | |
528 | \ bootfile and exit. | |
529 | \ | |
530 | \ If the package arguments specifies use of TFTP, and the | |
531 | \ DHCP response provides a TFTP URI, then either the TFTP | |
532 | \ server or the filename is being overridden. Decode the | |
533 | \ URI and set those fields appropriately. | |
534 | \ | |
535 | \ Else, if TFTP must be used, but a HTTP URL was provided | |
536 | \ in the DHCP response, ignore the URL specification. | |
537 | ||
538 | 2dup VS_BOOT_URI find-vendor-option if ( pkt len $ ) | |
539 | 2dup check-uri$-form ( pkt len $ ) | |
540 | tftp-server-ip inaddr-any? bootfile count nip 0= and if ( pkt len $ ) | |
541 | bootfile pack drop 2drop exit ( ) | |
542 | then ( pkt len $ ) | |
543 | 2dup is-tftp-uri? if ( pkt len $ ) | |
544 | tftp-server-ip inaddr-any? if ( pkt len $ ) | |
545 | 2dup tftpuri>srv tftp-server-ip inet-aton drop ( pkt len $ ) | |
546 | then ( pkt len $ ) | |
547 | bootfile count nip 0= if ( pkt len $ ) | |
548 | 2dup tftpuri>file bootfile pack drop ( pkt len $ ) | |
549 | then ( pkt len $ ) | |
550 | 2drop 2drop exit ( ) | |
551 | then 2drop ( pkt len ) | |
552 | then ( pkt len ) | |
553 | ||
554 | \ Extract TFTP boot information from the standard bootfile | |
555 | \ and TFTP server fields. | |
556 | ||
557 | bootfile count nip 0= if ( pkt len ) | |
558 | 2dup CD_BOOTFILE_NAME find-dhcp-option 0= if ( pkt len ) | |
559 | over >dhcp-file dup cstrlen ( pkt len $ ) | |
560 | then bootfile pack drop ( pkt len ) | |
561 | then ( pkt len ) | |
562 | tftp-server-ip inaddr-any? if ( pkt len ) | |
563 | over >dhcp-siaddr tftp-server-ip copy-ip-addr ( pkt len ) | |
564 | then ( pkt len ) | |
565 | 2drop ( ) | |
566 | ; | |
567 | ||
568 | : dhcps-bound ( -- ) | |
569 | chosen-bootreply /chosen-bootreply ( pkt,len ) | |
570 | my-netmask inaddr-any? if ( pkt,len ) | |
571 | 2dup CD_SUBNETMASK find-dhcp-option if ( pkt,len adr,len ) | |
572 | drop my-netmask copy-ip-addr ( pkt,len ) | |
573 | then ( pkt,len ) | |
574 | then ( pkt,len ) | |
575 | router-ip inaddr-any? if ( pkt,len ) | |
576 | 2dup CD_ROUTER find-dhcp-option if ( pkt,len adr,len ) | |
577 | drop router-ip copy-ip-addr ( pkt,len ) | |
578 | then ( pkt,len ) | |
579 | then ( pkt,len ) | |
580 | 2dup dhcp-set-bootsrv,file ( pkt,len ) | |
581 | http-proxy count nip 0= if ( pkt,len ) | |
582 | 2dup VS_HTTP_PROXY find-vendor-option if ( pkt,len proxy$ ) | |
583 | 2dup check-htproxy$-form http-proxy pack drop ( pkt,len ) | |
584 | then ( pkt,len ) | |
585 | then ( pkt,len ) | |
586 | 2drop ( ) | |
587 | DHCPS_CONFIGURED dhcp-state ! ( ) | |
588 | ; | |
589 | ||
590 | : "bootp-response" ( -- $ ) " bootp-response" ; \ Space savings | |
591 | ||
592 | \ CONFIGURED state processing. Publish contents of DHCPACK, if one was | |
593 | \ received, in /chosen:bootp-response. | |
594 | : dhcps-configured ( -- ) | |
595 | chosen-bootreply /chosen-bootreply dup if | |
596 | encode-bytes "bootp-response" set-chosen-property | |
597 | else | |
598 | 2drop | |
599 | then | |
600 | ; | |
601 | ||
602 | : (do-dhcp) ( -- ) | |
603 | DHCPS_INIT dhcp-state ! | |
604 | begin | |
605 | dhcp-state @ case | |
606 | DHCPS_INIT of dhcps-init endof | |
607 | DHCPS_INFORM of dhcps-inform endof | |
608 | DHCPS_SELECTING of dhcps-selecting endof | |
609 | DHCPS_REQUESTING of dhcps-requesting endof | |
610 | DHCPS_VERIFYING of dhcps-verifying endof | |
611 | DHCPS_BOUND of dhcps-bound endof | |
612 | DHCPS_CONFIGURED of dhcps-configured exit endof | |
613 | endcase | |
614 | again | |
615 | ; | |
616 | ||
617 | : do-dhcp ( -- ) | |
618 | dhcp-init ( ) | |
619 | ['] (do-dhcp) catch ( throw? ) | |
620 | dhcp-close ( throw? ) | |
621 | throw ( ) | |
622 | ; | |
623 | ||
624 | headers |