Can't use strncpy() to copy strings that aren't NUL-terminated to
[people/andreif/gpxe.git] / src / net / udp / dhcp.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <string.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <errno.h>
23 #include <assert.h>
24 #include <byteswap.h>
25 #include <gpxe/if_ether.h>
26 #include <gpxe/netdevice.h>
27 #include <gpxe/xfer.h>
28 #include <gpxe/open.h>
29 #include <gpxe/job.h>
30 #include <gpxe/retry.h>
31 #include <gpxe/tcpip.h>
32 #include <gpxe/ip.h>
33 #include <gpxe/uri.h>
34 #include <gpxe/dhcp.h>
35
36 /** @file
37  *
38  * Dynamic Host Configuration Protocol
39  *
40  */
41
42 /** DHCP operation types
43  *
44  * This table maps from DHCP message types (i.e. values of the @c
45  * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP
46  * packet.
47  */
48 static const uint8_t dhcp_op[] = {
49         [DHCPDISCOVER]  = BOOTP_REQUEST,
50         [DHCPOFFER]     = BOOTP_REPLY,
51         [DHCPREQUEST]   = BOOTP_REQUEST,
52         [DHCPDECLINE]   = BOOTP_REQUEST,
53         [DHCPACK]       = BOOTP_REPLY,
54         [DHCPNAK]       = BOOTP_REPLY,
55         [DHCPRELEASE]   = BOOTP_REQUEST,
56         [DHCPINFORM]    = BOOTP_REQUEST,
57 };
58
59 /** Raw option data for options common to all DHCP requests */
60 static uint8_t dhcp_request_options_data[] = {
61         DHCP_MAX_MESSAGE_SIZE, DHCP_WORD ( ETH_MAX_MTU ),
62         DHCP_VENDOR_CLASS_ID,
63         DHCP_STRING (  'E', 't', 'h', 'e', 'r', 'b', 'o', 'o', 't' ),
64         DHCP_PARAMETER_REQUEST_LIST,
65         DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, DHCP_LOG_SERVERS,
66                       DHCP_HOST_NAME, DHCP_DOMAIN_NAME, DHCP_ROOT_PATH,
67                       DHCP_VENDOR_ENCAP, DHCP_TFTP_SERVER_NAME,
68                       DHCP_BOOTFILE_NAME, DHCP_EB_ENCAP,
69                       DHCP_ISCSI_INITIATOR_IQN ),
70         DHCP_END
71 };
72
73 /**
74  * Name a DHCP packet type
75  *
76  * @v msgtype           DHCP message type
77  * @ret string          DHCP mesasge type name
78  */
79 static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) {
80         switch ( msgtype ) {
81         case 0:                 return "BOOTP"; /* Non-DHCP packet */
82         case DHCPDISCOVER:      return "DHCPDISCOVER";
83         case DHCPOFFER:         return "DHCPOFFER";
84         case DHCPREQUEST:       return "DHCPREQUEST";
85         case DHCPDECLINE:       return "DHCPDECLINE";
86         case DHCPACK:           return "DHCPACK";
87         case DHCPNAK:           return "DHCPNAK";
88         case DHCPRELEASE:       return "DHCPRELEASE";
89         case DHCPINFORM:        return "DHCPINFORM";
90         default:                return "DHCP<invalid>";
91         }
92 }
93
94 /**
95  * Calculate DHCP transaction ID for a network device
96  *
97  * @v netdev            Network device
98  * @ret xid             DHCP XID
99  *
100  * Extract the least significant bits of the hardware address for use
101  * as the transaction ID.
102  */
103 static uint32_t dhcp_xid ( struct net_device *netdev ) {
104         uint32_t xid;
105
106         memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len
107                          - sizeof ( xid ) ), sizeof ( xid ) );
108         return xid;
109 }
110
111 /** Options common to all DHCP requests */
112 struct dhcp_option_block dhcp_request_options = {
113         .data = dhcp_request_options_data,
114         .max_len = sizeof ( dhcp_request_options_data ),
115         .len = sizeof ( dhcp_request_options_data ),
116 };
117
118 /**
119  * Set option within DHCP packet
120  *
121  * @v dhcppkt           DHCP packet
122  * @v tag               DHCP option tag
123  * @v data              New value for DHCP option
124  * @v len               Length of value, in bytes
125  * @ret rc              Return status code
126  *
127  * Sets the option within the first available options block within the
128  * DHCP packet.  Option blocks are tried in the order specified by @c
129  * dhcp_option_block_fill_order.
130  *
131  * The magic options @c DHCP_EB_YIADDR and @c DHCP_EB_SIADDR are
132  * intercepted and inserted into the appropriate fixed fields within
133  * the DHCP packet.  The option @c DHCP_OPTION_OVERLOAD is silently
134  * ignored, since our DHCP packet assembly method relies on always
135  * having option overloading in use.
136  */
137 static int set_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
138                                     unsigned int tag, const void *data,
139                                     size_t len ) {
140         struct dhcphdr *dhcphdr = dhcppkt->dhcphdr;
141         struct dhcp_option_block *options = &dhcppkt->options;
142         struct dhcp_option *option = NULL;
143
144         /* Special-case the magic options */
145         switch ( tag ) {
146         case DHCP_OPTION_OVERLOAD:
147                 /* Hard-coded in packets we create; always ignore */
148                 return 0;
149         case DHCP_EB_YIADDR:
150                 memcpy ( &dhcphdr->yiaddr, data, sizeof ( dhcphdr->yiaddr ) );
151                 return 0;
152         case DHCP_EB_SIADDR:
153                 memcpy ( &dhcphdr->siaddr, data, sizeof ( dhcphdr->siaddr ) );
154                 return 0;
155         case DHCP_TFTP_SERVER_NAME:
156                 memset ( dhcphdr->sname, 0, sizeof ( dhcphdr->sname ) );
157                 if ( len > sizeof ( dhcphdr->sname ) )
158                         len = sizeof ( dhcphdr->sname );
159                 memcpy ( dhcphdr->sname, data, len );
160                 return 0;
161         case DHCP_BOOTFILE_NAME:
162                 memset ( dhcphdr->file, 0, sizeof ( dhcphdr->file ) );
163                 if ( len > sizeof ( dhcphdr->file ) )
164                         len = sizeof ( dhcphdr->file );
165                 memcpy ( dhcphdr->file, data, len );
166                 return 0;
167         default:
168                 /* Continue processing as normal */
169                 break;
170         }
171                 
172         /* Set option */
173         option = set_dhcp_option ( options, tag, data, len );
174
175         /* Update DHCP packet length */
176         dhcppkt->len = ( offsetof ( typeof ( *dhcppkt->dhcphdr ), options )
177                          + dhcppkt->options.len );
178
179         return ( option ? 0 : -ENOSPC );
180 }
181
182 /**
183  * Copy option into DHCP packet
184  *
185  * @v dhcppkt           DHCP packet
186  * @v options           DHCP option block, or NULL
187  * @v tag               DHCP option tag to search for
188  * @v new_tag           DHCP option tag to use for copied option
189  * @ret rc              Return status code
190  *
191  * Copies a single option, if present, from the DHCP options block
192  * into a DHCP packet.  The tag for the option may be changed if
193  * desired; this is required by other parts of the DHCP code.
194  *
195  * @c options may specify a single options block, or be left as NULL
196  * in order to search for the option within all registered options
197  * blocks.
198  */
199 static int copy_dhcp_packet_option ( struct dhcp_packet *dhcppkt,
200                                      struct dhcp_option_block *options,
201                                      unsigned int tag, unsigned int new_tag ) {
202         struct dhcp_option *option;
203         int rc;
204
205         option = find_dhcp_option ( options, tag );
206         if ( option ) {
207                 if ( ( rc = set_dhcp_packet_option ( dhcppkt, new_tag,
208                                                      &option->data,
209                                                      option->len ) ) != 0 )
210                         return rc;
211         }
212         return 0;
213 }
214
215 /**
216  * Copy options into DHCP packet
217  *
218  * @v dhcppkt           DHCP packet
219  * @v options           DHCP option block, or NULL
220  * @v encapsulator      Encapsulating option, or zero
221  * @ret rc              Return status code
222  * 
223  * Copies options with the specified encapsulator from DHCP options
224  * blocks into a DHCP packet.  Most options are copied verbatim.
225  * Recognised encapsulated options fields are handled as such.
226  *
227  * @c options may specify a single options block, or be left as NULL
228  * in order to copy options from all registered options blocks.
229  */
230 static int copy_dhcp_packet_encap_options ( struct dhcp_packet *dhcppkt,
231                                             struct dhcp_option_block *options,
232                                             unsigned int encapsulator ) {
233         unsigned int subtag;
234         unsigned int tag;
235         int rc;
236
237         for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) {
238                 tag = DHCP_ENCAP_OPT ( encapsulator, subtag );
239                 switch ( tag ) {
240                 case DHCP_EB_ENCAP:
241                 case DHCP_VENDOR_ENCAP:
242                         /* Process encapsulated options field */
243                         if ( ( rc = copy_dhcp_packet_encap_options ( dhcppkt,
244                                                                      options,
245                                                                      tag)) !=0)
246                                 return rc;
247                         break;
248                 default:
249                         /* Copy option to reassembled packet */
250                         if ( ( rc = copy_dhcp_packet_option ( dhcppkt, options,
251                                                               tag, tag ) ) !=0)
252                                 return rc;
253                         break;
254                 };
255         }
256
257         return 0;
258 }
259
260 /**
261  * Copy options into DHCP packet
262  *
263  * @v dhcppkt           DHCP packet
264  * @v options           DHCP option block, or NULL
265  * @ret rc              Return status code
266  * 
267  * Copies options from DHCP options blocks into a DHCP packet.  Most
268  * options are copied verbatim.  Recognised encapsulated options
269  * fields are handled as such.
270  *
271  * @c options may specify a single options block, or be left as NULL
272  * in order to copy options from all registered options blocks.
273  */
274 int copy_dhcp_packet_options ( struct dhcp_packet *dhcppkt,
275                                struct dhcp_option_block *options ) {
276         return copy_dhcp_packet_encap_options ( dhcppkt, options, 0 );
277 }
278
279 /**
280  * Create a DHCP packet
281  *
282  * @v netdev            Network device
283  * @v msgtype           DHCP message type
284  * @v data              Buffer for DHCP packet
285  * @v max_len           Size of DHCP packet buffer
286  * @v dhcppkt           DHCP packet structure to fill in
287  * @ret rc              Return status code
288  *
289  * Creates a DHCP packet in the specified buffer, and fills out a @c
290  * dhcp_packet structure that can be passed to
291  * set_dhcp_packet_option() or copy_dhcp_packet_options().
292  */
293 int create_dhcp_packet ( struct net_device *netdev, uint8_t msgtype,
294                          void *data, size_t max_len,
295                          struct dhcp_packet *dhcppkt ) {
296         struct dhcphdr *dhcphdr = data;
297         int rc;
298
299         /* Sanity check */
300         if ( max_len < sizeof ( *dhcphdr ) )
301                 return -ENOSPC;
302
303         /* Initialise DHCP packet content */
304         memset ( dhcphdr, 0, max_len );
305         dhcphdr->xid = dhcp_xid ( netdev );
306         dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE );
307         dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto );
308         dhcphdr->hlen = netdev->ll_protocol->ll_addr_len;
309         memcpy ( dhcphdr->chaddr, netdev->ll_addr, dhcphdr->hlen );
310         dhcphdr->op = dhcp_op[msgtype];
311
312         /* Initialise DHCP packet structure */
313         dhcppkt->dhcphdr = dhcphdr;
314         dhcppkt->max_len = max_len;
315         init_dhcp_options ( &dhcppkt->options, dhcphdr->options,
316                             ( max_len -
317                               offsetof ( typeof ( *dhcphdr ), options ) ) );
318         
319         /* Set DHCP_MESSAGE_TYPE option */
320         if ( ( rc = set_dhcp_packet_option ( dhcppkt, DHCP_MESSAGE_TYPE,
321                                              &msgtype,
322                                              sizeof ( msgtype ) ) ) != 0 )
323                 return rc;
324
325         return 0;
326 }
327
328 /**
329  * Calculate used length of a field containing DHCP options
330  *
331  * @v data              Field containing DHCP options
332  * @v max_len           Field length
333  * @ret len             Used length (excluding the @c DHCP_END tag)
334  */
335 static size_t dhcp_field_len ( const void *data, size_t max_len ) {
336         struct dhcp_option_block options;
337         struct dhcp_option *end;
338
339         options.data = ( ( void * ) data );
340         options.len = max_len;
341         end = find_dhcp_option ( &options, DHCP_END );
342         return ( end ? ( ( ( void * ) end ) - data ) : 0 );
343 }
344
345 /**
346  * Merge field containing DHCP options or string into DHCP options block
347  *
348  * @v options           DHCP option block
349  * @v data              Field containing DHCP options
350  * @v max_len           Field length
351  * @v tag               DHCP option tag, or 0
352  *
353  * If @c tag is non-zero, the field will be treated as a
354  * NUL-terminated string representing the value of the specified DHCP
355  * option.  If @c tag is zero, the field will be treated as a block of
356  * DHCP options, and simply appended to the existing options in the
357  * option block.
358  *
359  * The caller must ensure that there is enough space in the options
360  * block to perform the merge.
361  */
362 static void merge_dhcp_field ( struct dhcp_option_block *options,
363                                const void *data, size_t max_len,
364                                unsigned int tag ) {
365         size_t len;
366         void *dest;
367         struct dhcp_option *end;
368
369         if ( tag ) {
370                 set_dhcp_option ( options, tag, data, strlen ( data ) );
371         } else {
372                 len = dhcp_field_len ( data, max_len );
373                 dest = ( options->data + options->len - 1 );
374                 memcpy ( dest, data, len );
375                 options->len += len;
376                 end = ( dest + len );
377                 end->tag = DHCP_END;
378         }
379 }
380
381 /**
382  * Parse DHCP packet and construct DHCP options block
383  *
384  * @v dhcphdr           DHCP packet
385  * @v len               Length of DHCP packet
386  * @ret options         DHCP options block, or NULL
387  *
388  * Parses a received DHCP packet and canonicalises its contents into a
389  * single DHCP options block.  The "file" and "sname" fields are
390  * converted into the corresponding DHCP options (@c
391  * DHCP_BOOTFILE_NAME and @c DHCP_TFTP_SERVER_NAME respectively).  If
392  * these fields are used for option overloading, their options are
393  * merged in to the options block.
394  *
395  * The values of the "yiaddr" and "siaddr" fields will be stored
396  * within the options block as the magic options @c DHCP_EB_YIADDR and
397  * @c DHCP_EB_SIADDR.
398  * 
399  * Note that this call allocates new memory for the constructed DHCP
400  * options block; it is the responsibility of the caller to eventually
401  * free this memory.
402  */
403 static struct dhcp_option_block * dhcp_parse ( const struct dhcphdr *dhcphdr,
404                                                size_t len ) {
405         struct dhcp_option_block *options;
406         size_t options_len;
407         unsigned int overloading;
408
409         /* Sanity check */
410         if ( len < sizeof ( *dhcphdr ) )
411                 return NULL;
412
413         /* Calculate size of resulting concatenated option block:
414          *
415          *   The "options" field : length of the field minus the DHCP_END tag.
416          *
417          *   The "file" field : maximum length of the field minus the
418          *   NUL terminator, plus a 2-byte DHCP header or, if used for
419          *   option overloading, the length of the field minus the
420          *   DHCP_END tag.
421          *
422          *   The "sname" field : as for the "file" field.
423          *
424          *   15 bytes for an encapsulated options field to contain the
425          *   value of the "yiaddr" and "siaddr" fields
426          *
427          *   1 byte for a final terminating DHCP_END tag.
428          */
429         options_len = ( ( len - offsetof ( typeof ( *dhcphdr ), options ) ) - 1
430                         + ( sizeof ( dhcphdr->file ) + 1 )
431                         + ( sizeof ( dhcphdr->sname ) + 1 )
432                         + 15 /* yiaddr and siaddr */
433                         + 1 /* DHCP_END tag */ );
434         
435         /* Allocate empty options block of required size */
436         options = alloc_dhcp_options ( options_len );
437         if ( ! options ) {
438                 DBG ( "DHCP could not allocate %d-byte option block\n",
439                       options_len );
440                 return NULL;
441         }
442         
443         /* Merge in "options" field, if this is a DHCP packet */
444         if ( dhcphdr->magic == htonl ( DHCP_MAGIC_COOKIE ) ) {
445                 merge_dhcp_field ( options, dhcphdr->options,
446                                    ( len -
447                                      offsetof ( typeof (*dhcphdr), options ) ),
448                                    0 /* Always contains options */ );
449         }
450
451         /* Identify overloaded fields */
452         overloading = find_dhcp_num_option ( options, DHCP_OPTION_OVERLOAD );
453         
454         /* Merge in "file" and "sname" fields */
455         merge_dhcp_field ( options, dhcphdr->file, sizeof ( dhcphdr->file ),
456                            ( ( overloading & DHCP_OPTION_OVERLOAD_FILE ) ?
457                              0 : DHCP_BOOTFILE_NAME ) );
458         merge_dhcp_field ( options, dhcphdr->sname, sizeof ( dhcphdr->sname ),
459                            ( ( overloading & DHCP_OPTION_OVERLOAD_SNAME ) ?
460                              0 : DHCP_TFTP_SERVER_NAME ) );
461
462         /* Set magic options for "yiaddr" and "siaddr", if present */
463         if ( dhcphdr->yiaddr.s_addr ) {
464                 set_dhcp_option ( options, DHCP_EB_YIADDR,
465                                   &dhcphdr->yiaddr, sizeof (dhcphdr->yiaddr) );
466         }
467         if ( dhcphdr->siaddr.s_addr ) {
468                 set_dhcp_option ( options, DHCP_EB_SIADDR,
469                                   &dhcphdr->siaddr, sizeof (dhcphdr->siaddr) );
470         }
471         
472         assert ( options->len <= options->max_len );
473
474         return options;
475 }
476
477 /****************************************************************************
478  *
479  * DHCP to UDP interface
480  *
481  */
482
483 /** A DHCP session */
484 struct dhcp_session {
485         /** Reference counter */
486         struct refcnt refcnt;
487         /** Job control interface */
488         struct job_interface job;
489         /** Data transfer interface */
490         struct xfer_interface xfer;
491
492         /** Network device being configured */
493         struct net_device *netdev;
494         /** Option block registration routine */
495         int ( * register_options ) ( struct net_device *netdev,
496                                      struct dhcp_option_block *options );
497
498         /** State of the session
499          *
500          * This is a value for the @c DHCP_MESSAGE_TYPE option
501          * (e.g. @c DHCPDISCOVER).
502          */
503         int state;
504         /** Options obtained from server */
505         struct dhcp_option_block *options;
506         /** Retransmission timer */
507         struct retry_timer timer;
508 };
509
510 /**
511  * Free DHCP session
512  *
513  * @v refcnt            Reference counter
514  */
515 static void dhcp_free ( struct refcnt *refcnt ) {
516         struct dhcp_session *dhcp =
517                 container_of ( refcnt, struct dhcp_session, refcnt );
518
519         netdev_put ( dhcp->netdev );
520         dhcpopt_put ( dhcp->options );
521         free ( dhcp );
522 }
523
524 /**
525  * Mark DHCP session as complete
526  *
527  * @v dhcp              DHCP session
528  * @v rc                Return status code
529  */
530 static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) {
531
532         /* Block futher incoming messages */
533         job_nullify ( &dhcp->job );
534         xfer_nullify ( &dhcp->xfer );
535
536         /* Stop retry timer */
537         stop_timer ( &dhcp->timer );
538
539         /* Free resources and close interfaces */
540         xfer_close ( &dhcp->xfer, rc );
541         job_done ( &dhcp->job, rc );
542 }
543
544 /****************************************************************************
545  *
546  * Data transfer interface
547  *
548  */
549
550 /**
551  * Transmit DHCP request
552  *
553  * @v dhcp              DHCP session
554  * @ret rc              Return status code
555  */
556 static int dhcp_send_request ( struct dhcp_session *dhcp ) {
557         struct xfer_metadata meta = {
558                 .netdev = dhcp->netdev,
559         };
560         struct dhcp_packet dhcppkt;
561         struct io_buffer *iobuf;
562         int rc;
563         
564         DBGC ( dhcp, "DHCP %p transmitting %s\n",
565                dhcp, dhcp_msgtype_name ( dhcp->state ) );
566
567         assert ( ( dhcp->state == DHCPDISCOVER ) ||
568                  ( dhcp->state == DHCPREQUEST ) );
569
570         /* Start retry timer.  Do this first so that failures to
571          * transmit will be retried.
572          */
573         start_timer ( &dhcp->timer );
574
575         /* Allocate buffer for packet */
576         iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN );
577         if ( ! iobuf )
578                 return -ENOMEM;
579
580         /* Create DHCP packet in temporary buffer */
581         if ( ( rc = create_dhcp_packet ( dhcp->netdev, dhcp->state,
582                                          iobuf->data, iob_tailroom ( iobuf ),
583                                          &dhcppkt ) ) != 0 ) {
584                 DBGC ( dhcp, "DHCP %p could not create DHCP packet: %s\n",
585                        dhcp, strerror ( rc ) );
586                 goto done;
587         }
588
589         /* Copy in options common to all requests */
590         if ( ( rc = copy_dhcp_packet_options ( &dhcppkt,
591                                                &dhcp_request_options ) ) != 0){
592                 DBGC ( dhcp, "DHCP %p could not set common DHCP options: %s\n",
593                        dhcp, strerror ( rc ) );
594                 goto done;
595         }
596
597         /* Copy any required options from previous server repsonse */
598         if ( dhcp->options ) {
599                 if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
600                                             DHCP_SERVER_IDENTIFIER,
601                                             DHCP_SERVER_IDENTIFIER ) ) != 0 ) {
602                         DBGC ( dhcp, "DHCP %p could not set server identifier "
603                                "option: %s\n", dhcp, strerror ( rc ) );
604                         goto done;
605                 }
606                 if ( ( rc = copy_dhcp_packet_option ( &dhcppkt, dhcp->options,
607                                             DHCP_EB_YIADDR,
608                                             DHCP_REQUESTED_ADDRESS ) ) != 0 ) {
609                         DBGC ( dhcp, "DHCP %p could not set requested address "
610                                "option: %s\n", dhcp, strerror ( rc ) );
611                         goto done;
612                 }
613         }
614
615         /* Transmit the packet */
616         iob_put ( iobuf, dhcppkt.len );
617         rc = xfer_deliver_iob_meta ( &dhcp->xfer, iobuf, &meta );
618         iobuf = NULL;
619         if ( rc != 0 ) {
620                 DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n",
621                        dhcp, strerror ( rc ) );
622                 goto done;
623         }
624
625  done:
626         free_iob ( iobuf );
627         return rc;
628 }
629
630 /**
631  * Handle DHCP retry timer expiry
632  *
633  * @v timer             DHCP retry timer
634  * @v fail              Failure indicator
635  */
636 static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) {
637         struct dhcp_session *dhcp =
638                 container_of ( timer, struct dhcp_session, timer );
639
640         if ( fail ) {
641                 dhcp_finished ( dhcp, -ETIMEDOUT );
642         } else {
643                 dhcp_send_request ( dhcp );
644         }
645 }
646
647 /**
648  * Receive new data
649  *
650  * @v xfer              Data transfer interface
651  * @v iobuf             I/O buffer
652  * @v data              Received data
653  * @v len               Length of received data
654  * @ret rc              Return status code
655  */
656 static int dhcp_deliver_raw ( struct xfer_interface *xfer,
657                               const void *data, size_t len ) {
658         struct dhcp_session *dhcp =
659                 container_of ( xfer, struct dhcp_session, xfer );
660         const struct dhcphdr *dhcphdr = data;
661         struct dhcp_option_block *options;
662         unsigned int msgtype;
663
664         /* Check for matching transaction ID */
665         if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) {
666                 DBGC ( dhcp, "DHCP %p wrong transaction ID (wanted %08lx, "
667                         "got %08lx)\n", dhcp, ntohl ( dhcphdr->xid ),
668                         ntohl ( dhcp_xid ( dhcp->netdev ) ) );
669                 return 0;
670         };
671
672         /* Parse packet and create options structure */
673         options = dhcp_parse ( dhcphdr, len );
674         if ( ! options ) {
675                 DBGC ( dhcp, "DHCP %p could not parse DHCP packet\n", dhcp );
676                 return -EINVAL;
677         }
678
679         /* Determine message type */
680         msgtype = find_dhcp_num_option ( options, DHCP_MESSAGE_TYPE );
681         DBGC ( dhcp, "DHCP %p received %s\n",
682                dhcp, dhcp_msgtype_name ( msgtype ) );
683
684         /* Handle DHCP reply */
685         switch ( dhcp->state ) {
686         case DHCPDISCOVER:
687                 if ( msgtype != DHCPOFFER )
688                         goto out_discard;
689                 dhcp->state = DHCPREQUEST;
690                 break;
691         case DHCPREQUEST:
692                 if ( msgtype != DHCPACK )
693                         goto out_discard;
694                 dhcp->state = DHCPACK;
695                 break;
696         default:
697                 assert ( 0 );
698                 goto out_discard;
699         }
700
701         /* Stop timer and update stored options */
702         stop_timer ( &dhcp->timer );
703         if ( dhcp->options )
704                 dhcpopt_put ( dhcp->options );
705         dhcp->options = options;
706
707         /* Transmit next packet, or terminate session */
708         if ( dhcp->state < DHCPACK ) {
709                 dhcp_send_request ( dhcp );
710         } else {
711                 dhcp->register_options ( dhcp->netdev, dhcp->options );
712                 dhcp_finished ( dhcp, 0 );
713         }
714         return 0;
715
716  out_discard:
717         dhcpopt_put ( options );
718         return 0;
719 }
720
721 /** DHCP data transfer interface operations */
722 static struct xfer_interface_operations dhcp_xfer_operations = {
723         .close          = ignore_xfer_close,
724         .vredirect      = xfer_vopen,
725         .request        = ignore_xfer_request,
726         .seek           = ignore_xfer_seek,
727         .deliver_iob    = xfer_deliver_as_raw,
728         .deliver_raw    = dhcp_deliver_raw,
729 };
730
731 /****************************************************************************
732  *
733  * Job control interface
734  *
735  */
736
737 /**
738  * Handle kill() event received via job control interface
739  *
740  * @v job               DHCP job control interface
741  */
742 static void dhcp_job_kill ( struct job_interface *job ) {
743         struct dhcp_session *dhcp =
744                 container_of ( job, struct dhcp_session, job );
745
746         /* Terminate DHCP session */
747         dhcp_finished ( dhcp, -ECANCELED );
748 }
749
750 /** DHCP job control interface operations */
751 static struct job_interface_operations dhcp_job_operations = {
752         .done           = ignore_job_done,
753         .kill           = dhcp_job_kill,
754         .progress       = ignore_job_progress,
755 };
756
757 /****************************************************************************
758  *
759  * Instantiator
760  *
761  */
762
763 /**
764  * Start DHCP on a network device
765  *
766  * @v job               Job control interface
767  * @v netdev            Network device
768  * @v register_options  DHCP option block registration routine
769  * @ret rc              Return status code
770  *
771  * Starts DHCP on the specified network device.  If successful, the @c
772  * register_options() routine will be called with the acquired
773  * options.
774  */
775 int start_dhcp ( struct job_interface *job, struct net_device *netdev,
776                  int ( * register_options ) ( struct net_device *netdev,
777                                               struct dhcp_option_block * ) ) {
778         static struct sockaddr_in server = {
779                 .sin_family = AF_INET,
780                 .sin_addr.s_addr = INADDR_BROADCAST,
781                 .sin_port = htons ( BOOTPS_PORT ),
782         };
783         static struct sockaddr_in client = {
784                 .sin_family = AF_INET,
785                 .sin_port = htons ( BOOTPC_PORT ),
786         };
787         struct dhcp_session *dhcp;
788         int rc;
789
790         /* Allocate and initialise structure */
791         dhcp = malloc ( sizeof ( *dhcp ) );
792         if ( ! dhcp )
793                 return -ENOMEM;
794         memset ( dhcp, 0, sizeof ( *dhcp ) );
795         dhcp->refcnt.free = dhcp_free;
796         job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt );
797         xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt );
798         dhcp->netdev = netdev_get ( netdev );
799         dhcp->register_options = register_options;
800         dhcp->timer.expired = dhcp_timer_expired;
801         dhcp->state = DHCPDISCOVER;
802
803         /* Instantiate child objects and attach to our interfaces */
804         if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM,
805                                        ( struct sockaddr * ) &server,
806                                        ( struct sockaddr * ) &client ) ) != 0 )
807                 goto err;
808
809         /* Start timer to initiate initial DHCPREQUEST */
810         start_timer ( &dhcp->timer );
811
812         /* Attach parent interface, mortalise self, and return */
813         job_plug_plug ( &dhcp->job, job );
814         ref_put ( &dhcp->refcnt );
815         return 0;
816
817  err:
818         dhcp_finished ( dhcp, rc );
819         ref_put ( &dhcp->refcnt );
820         return rc;
821 }
822
823 /****************************************************************************
824  *
825  * Network device configurator
826  *
827  */
828
829 /* Avoid dragging in dns.o */
830 struct sockaddr_tcpip nameserver;
831
832 /* Avoid dragging in syslog.o */
833 struct in_addr syslogserver;
834
835 /**
836  * Configure network device from DHCP options
837  *
838  * @v netdev            Network device
839  * @v options           DHCP options block
840  * @ret rc              Return status code
841  */
842 int dhcp_configure_netdev ( struct net_device *netdev,
843                             struct dhcp_option_block *options ) {
844         struct in_addr address = { 0 };
845         struct in_addr netmask = { 0 };
846         struct in_addr gateway = { INADDR_NONE };
847         struct sockaddr_in *sin_nameserver;
848         struct in_addr tftp_server;
849         struct uri *uri;
850         char uri_string[32];
851         int rc;
852
853         /* Clear any existing routing table entry */
854         del_ipv4_address ( netdev );
855
856         /* Retrieve IP address configuration */
857         find_dhcp_ipv4_option ( options, DHCP_EB_YIADDR, &address );
858         find_dhcp_ipv4_option ( options, DHCP_SUBNET_MASK, &netmask );
859         find_dhcp_ipv4_option ( options, DHCP_ROUTERS, &gateway );
860
861         /* Set up new IP address configuration */
862         if ( ( rc = add_ipv4_address ( netdev, address, netmask,
863                                        gateway ) ) != 0 ) {
864                 DBG ( "Could not configure %s with DHCP results: %s\n",
865                       netdev->name, strerror ( rc ) );
866                 return rc;
867         }
868
869         /* Retrieve other DHCP options that we care about */
870         sin_nameserver = ( struct sockaddr_in * ) &nameserver;
871         sin_nameserver->sin_family = AF_INET;
872         find_dhcp_ipv4_option ( options, DHCP_DNS_SERVERS,
873                                 &sin_nameserver->sin_addr );
874         find_dhcp_ipv4_option ( options, DHCP_LOG_SERVERS,
875                                 &syslogserver );
876
877         /* Set current working URI based on TFTP server */
878         find_dhcp_ipv4_option ( options, DHCP_EB_SIADDR, &tftp_server );
879         snprintf ( uri_string, sizeof ( uri_string ),
880                    "tftp://%s/", inet_ntoa ( tftp_server ) );
881         uri = parse_uri ( uri_string );
882         if ( ! uri )
883                 return -ENOMEM;
884         churi ( uri );
885         uri_put ( uri );
886
887         return 0;
888 }