Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / net / udp / dhcpv6.c
1 /*
2  * Copyright (C) 2013 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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <byteswap.h>
27 #include <ipxe/interface.h>
28 #include <ipxe/xfer.h>
29 #include <ipxe/iobuf.h>
30 #include <ipxe/open.h>
31 #include <ipxe/netdevice.h>
32 #include <ipxe/settings.h>
33 #include <ipxe/retry.h>
34 #include <ipxe/timer.h>
35 #include <ipxe/in.h>
36 #include <ipxe/crc32.h>
37 #include <ipxe/errortab.h>
38 #include <ipxe/ipv6.h>
39 #include <ipxe/dhcpv6.h>
40
41 /** @file
42  *
43  * Dynamic Host Configuration Protocol for IPv6
44  *
45  */
46
47 /* Disambiguate the various error causes */
48 #define EPROTO_UNSPECFAIL __einfo_error ( EINFO_EPROTO_UNSPECFAIL )
49 #define EINFO_EPROTO_UNSPECFAIL \
50         __einfo_uniqify ( EINFO_EPROTO, 1, "Unspecified server failure" )
51 #define EPROTO_NOADDRSAVAIL __einfo_error ( EINFO_EPROTO_NOADDRSAVAIL )
52 #define EINFO_EPROTO_NOADDRSAVAIL \
53         __einfo_uniqify ( EINFO_EPROTO, 2, "No addresses available" )
54 #define EPROTO_NOBINDING __einfo_error ( EINFO_EPROTO_NOBINDING )
55 #define EINFO_EPROTO_NOBINDING \
56         __einfo_uniqify ( EINFO_EPROTO, 3, "Client record unavailable" )
57 #define EPROTO_NOTONLINK __einfo_error ( EINFO_EPROTO_NOTONLINK )
58 #define EINFO_EPROTO_NOTONLINK \
59         __einfo_uniqify ( EINFO_EPROTO, 4, "Prefix not on link" )
60 #define EPROTO_USEMULTICAST __einfo_error ( EINFO_EPROTO_USEMULTICAST )
61 #define EINFO_EPROTO_USEMULTICAST \
62         __einfo_uniqify ( EINFO_EPROTO, 5, "Use multicast address" )
63 #define EPROTO_STATUS( status )                                         \
64         EUNIQ ( EINFO_EPROTO, ( (status) & 0x0f ), EPROTO_UNSPECFAIL,   \
65                 EPROTO_NOADDRSAVAIL, EPROTO_NOBINDING,                  \
66                 EPROTO_NOTONLINK, EPROTO_USEMULTICAST )
67
68 /** Human-readable error messages */
69 struct errortab dhcpv6_errors[] __errortab = {
70         __einfo_errortab ( EINFO_EPROTO_NOADDRSAVAIL ),
71 };
72
73 /****************************************************************************
74  *
75  * DHCPv6 option lists
76  *
77  */
78
79 /** A DHCPv6 option list */
80 struct dhcpv6_option_list {
81         /** Data buffer */
82         const void *data;
83         /** Length of data buffer */
84         size_t len;
85 };
86
87 /**
88  * Find DHCPv6 option
89  *
90  * @v options           DHCPv6 option list
91  * @v code              Option code
92  * @ret option          DHCPv6 option, or NULL if not found
93  */
94 static const union dhcpv6_any_option *
95 dhcpv6_option ( struct dhcpv6_option_list *options, unsigned int code ) {
96         const union dhcpv6_any_option *option = options->data;
97         size_t remaining = options->len;
98         size_t data_len;
99
100         /* Scan through list of options */
101         while ( remaining >= sizeof ( option->header ) ) {
102
103                 /* Calculate and validate option length */
104                 remaining -= sizeof ( option->header );
105                 data_len = ntohs ( option->header.len );
106                 if ( data_len > remaining ) {
107                         /* Malformed option list */
108                         return NULL;
109                 }
110
111                 /* Return if we have found the specified option */
112                 if ( option->header.code == htons ( code ) )
113                         return option;
114
115                 /* Otherwise, move to the next option */
116                 option = ( ( ( void * ) option->header.data ) + data_len );
117                 remaining -= data_len;
118         }
119
120         return NULL;
121 }
122
123 /**
124  * Check DHCPv6 client or server identifier
125  *
126  * @v options           DHCPv6 option list
127  * @v code              Option code
128  * @v expected          Expected value
129  * @v len               Length of expected value
130  * @ret rc              Return status code
131  */
132 static int dhcpv6_check_duid ( struct dhcpv6_option_list *options,
133                                unsigned int code, const void *expected,
134                                size_t len ) {
135         const union dhcpv6_any_option *option;
136         const struct dhcpv6_duid_option *duid;
137
138         /* Find option */
139         option = dhcpv6_option ( options, code );
140         if ( ! option )
141                 return -ENOENT;
142         duid = &option->duid;
143
144         /* Check option length */
145         if ( ntohs ( duid->header.len ) != len )
146                 return -EINVAL;
147
148         /* Compare option value */
149         if ( memcmp ( duid->duid, expected, len ) != 0 )
150                 return -EINVAL;
151
152         return 0;
153 }
154
155 /**
156  * Get DHCPv6 status code
157  *
158  * @v options           DHCPv6 option list
159  * @ret rc              Return status code
160  */
161 static int dhcpv6_status_code ( struct dhcpv6_option_list *options ) {
162         const union dhcpv6_any_option *option;
163         const struct dhcpv6_status_code_option *status_code;
164         unsigned int status;
165
166         /* Find status code option, if present */
167         option = dhcpv6_option ( options, DHCPV6_STATUS_CODE );
168         if ( ! option ) {
169                 /* Omitted status code should be treated as "success" */
170                 return 0;
171         }
172         status_code = &option->status_code;
173
174         /* Sanity check */
175         if ( ntohs ( status_code->header.len ) <
176              ( sizeof ( *status_code ) - sizeof ( status_code->header ) ) ) {
177                 return -EINVAL;
178         }
179
180         /* Calculate iPXE error code from DHCPv6 status code */
181         status = ntohs ( status_code->status );
182         return ( status ? -EPROTO_STATUS ( status ) : 0 );
183 }
184
185 /**
186  * Get DHCPv6 identity association address
187  *
188  * @v options           DHCPv6 option list
189  * @v iaid              Identity association ID
190  * @v address           IPv6 address to fill in
191  * @ret rc              Return status code
192  */
193 static int dhcpv6_iaaddr ( struct dhcpv6_option_list *options, uint32_t iaid,
194                            struct in6_addr *address ) {
195         const union dhcpv6_any_option *option;
196         const struct dhcpv6_ia_na_option *ia_na;
197         const struct dhcpv6_iaaddr_option *iaaddr;
198         struct dhcpv6_option_list suboptions;
199         size_t len;
200         int rc;
201
202         /* Find identity association option, if present */
203         option = dhcpv6_option ( options, DHCPV6_IA_NA );
204         if ( ! option )
205                 return -ENOENT;
206         ia_na = &option->ia_na;
207
208         /* Sanity check */
209         len = ntohs ( ia_na->header.len );
210         if ( len < ( sizeof ( *ia_na ) - sizeof ( ia_na->header ) ) )
211                 return -EINVAL;
212
213         /* Check identity association ID */
214         if ( ia_na->iaid != htonl ( iaid ) )
215                 return -EINVAL;
216
217         /* Construct IA_NA sub-options list */
218         suboptions.data = ia_na->options;
219         suboptions.len = ( len + sizeof ( ia_na->header ) -
220                            offsetof ( typeof ( *ia_na ), options ) );
221
222         /* Check IA_NA status code */
223         if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
224                 return rc;
225
226         /* Find identity association address, if present */
227         option = dhcpv6_option ( &suboptions, DHCPV6_IAADDR );
228         if ( ! option )
229                 return -ENOENT;
230         iaaddr = &option->iaaddr;
231
232         /* Sanity check */
233         len = ntohs ( iaaddr->header.len );
234         if ( len < ( sizeof ( *iaaddr ) - sizeof ( iaaddr->header ) ) )
235                 return -EINVAL;
236
237         /* Construct IAADDR sub-options list */
238         suboptions.data = iaaddr->options;
239         suboptions.len = ( len + sizeof ( iaaddr->header ) -
240                            offsetof ( typeof ( *iaaddr ), options ) );
241
242         /* Check IAADDR status code */
243         if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
244                 return rc;
245
246         /* Extract IPv6 address */
247         memcpy ( address, &iaaddr->address, sizeof ( *address ) );
248
249         return 0;
250 }
251
252 /****************************************************************************
253  *
254  * DHCPv6 settings blocks
255  *
256  */
257
258 /** A DHCPv6 settings block */
259 struct dhcpv6_settings {
260         /** Reference count */
261         struct refcnt refcnt;
262         /** Settings block */
263         struct settings settings;
264         /** Option list */
265         struct dhcpv6_option_list options;
266 };
267
268 /**
269  * Check applicability of DHCPv6 setting
270  *
271  * @v settings          Settings block
272  * @v setting           Setting
273  * @ret applies         Setting applies within this settings block
274  */
275 static int dhcpv6_applies ( struct settings *settings __unused,
276                             const struct setting *setting ) {
277
278         return ( setting->scope == &ipv6_scope );
279 }
280
281 /**
282  * Fetch value of DHCPv6 setting
283  *
284  * @v settings          Settings block
285  * @v setting           Setting to fetch
286  * @v data              Buffer to fill with setting data
287  * @v len               Length of buffer
288  * @ret len             Length of setting data, or negative error
289  */
290 static int dhcpv6_fetch ( struct settings *settings,
291                           struct setting *setting,
292                           void *data, size_t len ) {
293         struct dhcpv6_settings *dhcpv6set =
294                 container_of ( settings, struct dhcpv6_settings, settings );
295         const union dhcpv6_any_option *option;
296         size_t option_len;
297
298         /* Find option */
299         option = dhcpv6_option ( &dhcpv6set->options, setting->tag );
300         if ( ! option )
301                 return -ENOENT;
302
303         /* Copy option to data buffer */
304         option_len = ntohs ( option->header.len );
305         if ( len > option_len )
306                 len = option_len;
307         memcpy ( data, option->header.data, len );
308         return option_len;
309 }
310
311 /** DHCPv6 settings operations */
312 static struct settings_operations dhcpv6_settings_operations = {
313         .applies = dhcpv6_applies,
314         .fetch = dhcpv6_fetch,
315 };
316
317 /**
318  * Register DHCPv6 options as network device settings
319  *
320  * @v options           DHCPv6 option list
321  * @v parent            Parent settings block
322  * @ret rc              Return status code
323  */
324 static int dhcpv6_register ( struct dhcpv6_option_list *options,
325                              struct settings *parent ) {
326         struct dhcpv6_settings *dhcpv6set;
327         void *data;
328         size_t len;
329         int rc;
330
331         /* Allocate and initialise structure */
332         dhcpv6set = zalloc ( sizeof ( *dhcpv6set ) + options->len );
333         if ( ! dhcpv6set ) {
334                 rc = -ENOMEM;
335                 goto err_alloc;
336         }
337         ref_init ( &dhcpv6set->refcnt, NULL );
338         settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations,
339                         &dhcpv6set->refcnt, &ipv6_scope );
340         data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) );
341         len = options->len;
342         memcpy ( data, options->data, len );
343         dhcpv6set->options.data = data;
344         dhcpv6set->options.len = len;
345
346         /* Register settings */
347         if ( ( rc = register_settings ( &dhcpv6set->settings, parent,
348                                         DHCPV6_SETTINGS_NAME ) ) != 0 )
349                 goto err_register;
350
351  err_register:
352         ref_put ( &dhcpv6set->refcnt );
353  err_alloc:
354         return rc;
355 }
356
357 /****************************************************************************
358  *
359  * DHCPv6 protocol
360  *
361  */
362
363 /** Options to be requested */
364 static uint16_t dhcpv6_requested_options[] = {
365         htons ( DHCPV6_DNS_SERVERS ), htons ( DHCPV6_DOMAIN_LIST ),
366         htons ( DHCPV6_BOOTFILE_URL ), htons ( DHCPV6_BOOTFILE_PARAM ),
367 };
368
369 /**
370  * Name a DHCPv6 packet type
371  *
372  * @v type              DHCPv6 packet type
373  * @ret name            DHCPv6 packet type name
374  */
375 static __attribute__ (( unused )) const char *
376 dhcpv6_type_name ( unsigned int type ) {
377         static char buf[ 12 /* "UNKNOWN-xxx" + NUL */ ];
378
379         switch ( type ) {
380         case DHCPV6_SOLICIT:                    return "SOLICIT";
381         case DHCPV6_ADVERTISE:                  return "ADVERTISE";
382         case DHCPV6_REQUEST:                    return "REQUEST";
383         case DHCPV6_REPLY:                      return "REPLY";
384         case DHCPV6_INFORMATION_REQUEST:        return "INFORMATION-REQUEST";
385         default:
386                 snprintf ( buf, sizeof ( buf ), "UNKNOWN-%d", type );
387                 return buf;
388         }
389 }
390
391 /** A DHCPv6 session state */
392 struct dhcpv6_session_state {
393         /** Current transmitted packet type */
394         uint8_t tx_type;
395         /** Current expected received packet type */
396         uint8_t rx_type;
397         /** Flags */
398         uint8_t flags;
399         /** Next state (or NULL to terminate) */
400         struct dhcpv6_session_state *next;
401 };
402
403 /** DHCPv6 session state flags */
404 enum dhcpv6_session_state_flags {
405         /** Include identity association within request */
406         DHCPV6_TX_IA_NA = 0x01,
407         /** Include leased IPv6 address within request */
408         DHCPV6_TX_IAADDR = 0x02,
409         /** Record received server ID */
410         DHCPV6_RX_RECORD_SERVER_ID = 0x04,
411         /** Record received IPv6 address */
412         DHCPV6_RX_RECORD_IAADDR = 0x08,
413         /** Apply received IPv6 address */
414         DHCPV6_RX_APPLY_IAADDR = 0x10,
415 };
416
417 /** DHCPv6 request state */
418 static struct dhcpv6_session_state dhcpv6_request = {
419         .tx_type = DHCPV6_REQUEST,
420         .rx_type = DHCPV6_REPLY,
421         .flags = ( DHCPV6_TX_IA_NA | DHCPV6_TX_IAADDR |
422                    DHCPV6_RX_RECORD_IAADDR | DHCPV6_RX_APPLY_IAADDR ),
423         .next = NULL,
424 };
425
426 /** DHCPv6 solicitation state */
427 static struct dhcpv6_session_state dhcpv6_solicit = {
428         .tx_type = DHCPV6_SOLICIT,
429         .rx_type = DHCPV6_ADVERTISE,
430         .flags = ( DHCPV6_TX_IA_NA | DHCPV6_RX_RECORD_SERVER_ID |
431                    DHCPV6_RX_RECORD_IAADDR ),
432         .next = &dhcpv6_request,
433 };
434
435 /** DHCPv6 information request state */
436 static struct dhcpv6_session_state dhcpv6_information_request = {
437         .tx_type = DHCPV6_INFORMATION_REQUEST,
438         .rx_type = DHCPV6_REPLY,
439         .flags = 0,
440         .next = NULL,
441 };
442
443 /** A DHCPv6 session */
444 struct dhcpv6_session {
445         /** Reference counter */
446         struct refcnt refcnt;
447         /** Job control interface */
448         struct interface job;
449         /** Data transfer interface */
450         struct interface xfer;
451
452         /** Network device being configured */
453         struct net_device *netdev;
454         /** Transaction ID */
455         uint8_t xid[3];
456         /** Identity association ID */
457         uint32_t iaid;
458         /** Start time (in ticks) */
459         unsigned long start;
460         /** Client DUID */
461         struct dhcpv6_duid_uuid client_duid;
462         /** Server DUID, if known */
463         void *server_duid;
464         /** Server DUID length */
465         size_t server_duid_len;
466         /** Leased IPv6 address */
467         struct in6_addr lease;
468
469         /** Retransmission timer */
470         struct retry_timer timer;
471
472         /** Current session state */
473         struct dhcpv6_session_state *state;
474         /** Current timeout status code */
475         int rc;
476 };
477
478 /**
479  * Free DHCPv6 session
480  *
481  * @v refcnt            Reference count
482  */
483 static void dhcpv6_free ( struct refcnt *refcnt ) {
484         struct dhcpv6_session *dhcpv6 =
485                 container_of ( refcnt, struct dhcpv6_session, refcnt );
486
487         netdev_put ( dhcpv6->netdev );
488         free ( dhcpv6->server_duid );
489         free ( dhcpv6 );
490 }
491
492 /**
493  * Terminate DHCPv6 session
494  *
495  * @v dhcpv6            DHCPv6 session
496  * @v rc                Reason for close
497  */
498 static void dhcpv6_finished ( struct dhcpv6_session *dhcpv6, int rc ) {
499
500         /* Stop timer */
501         stop_timer ( &dhcpv6->timer );
502
503         /* Shut down interfaces */
504         intf_shutdown ( &dhcpv6->xfer, rc );
505         intf_shutdown ( &dhcpv6->job, rc );
506 }
507
508 /**
509  * Transition to new DHCPv6 session state
510  *
511  * @v dhcpv6            DHCPv6 session
512  * @v state             New session state
513  */
514 static void dhcpv6_set_state ( struct dhcpv6_session *dhcpv6,
515                                struct dhcpv6_session_state *state ) {
516
517         DBGC ( dhcpv6, "DHCPv6 %s entering %s state\n", dhcpv6->netdev->name,
518                dhcpv6_type_name ( state->tx_type ) );
519
520         /* Record state */
521         dhcpv6->state = state;
522
523         /* Default to -ETIMEDOUT if no more specific error is recorded */
524         dhcpv6->rc = -ETIMEDOUT;
525
526         /* Start timer to trigger transmission */
527         start_timer_nodelay ( &dhcpv6->timer );
528 }
529
530 /**
531  * Get DHCPv6 user class
532  *
533  * @v data              Data buffer
534  * @v len               Length of data buffer
535  * @ret len             Length of user class
536  */
537 static size_t dhcpv6_user_class ( void *data, size_t len ) {
538         static const char default_user_class[4] = { 'i', 'P', 'X', 'E' };
539         int actual_len;
540
541         /* Fetch user-class setting, if defined */
542         actual_len = fetch_raw_setting ( NULL, &user_class_setting, data, len );
543         if ( actual_len >= 0 )
544                 return actual_len;
545
546         /* Otherwise, use the default user class ("iPXE") */
547         if ( len > sizeof ( default_user_class ) )
548                 len = sizeof ( default_user_class );
549         memcpy ( data, default_user_class, len );
550         return sizeof ( default_user_class );
551 }
552
553 /**
554  * Transmit current request
555  *
556  * @v dhcpv6            DHCPv6 session
557  * @ret rc              Return status code
558  */
559 static int dhcpv6_tx ( struct dhcpv6_session *dhcpv6 ) {
560         struct dhcpv6_duid_option *client_id;
561         struct dhcpv6_duid_option *server_id;
562         struct dhcpv6_ia_na_option *ia_na;
563         struct dhcpv6_iaaddr_option *iaaddr;
564         struct dhcpv6_option_request_option *option_request;
565         struct dhcpv6_user_class_option *user_class;
566         struct dhcpv6_elapsed_time_option *elapsed;
567         struct dhcpv6_header *dhcphdr;
568         struct io_buffer *iobuf;
569         size_t client_id_len;
570         size_t server_id_len;
571         size_t ia_na_len;
572         size_t option_request_len;
573         size_t user_class_string_len;
574         size_t user_class_len;
575         size_t elapsed_len;
576         size_t total_len;
577         int rc;
578
579         /* Calculate lengths */
580         client_id_len = ( sizeof ( *client_id ) +
581                           sizeof ( dhcpv6->client_duid ) );
582         server_id_len = ( dhcpv6->server_duid ? ( sizeof ( *server_id ) +
583                                                   dhcpv6->server_duid_len ) :0);
584         if ( dhcpv6->state->flags & DHCPV6_TX_IA_NA ) {
585                 ia_na_len = sizeof ( *ia_na );
586                 if ( dhcpv6->state->flags & DHCPV6_TX_IAADDR )
587                         ia_na_len += sizeof ( *iaaddr );
588         } else {
589                 ia_na_len = 0;
590         }
591         option_request_len = ( sizeof ( *option_request ) +
592                                sizeof ( dhcpv6_requested_options ) );
593         user_class_string_len = dhcpv6_user_class ( NULL, 0 );
594         user_class_len = ( sizeof ( *user_class ) +
595                            sizeof ( user_class->user_class[0] ) +
596                            user_class_string_len );
597         elapsed_len = sizeof ( *elapsed );
598         total_len = ( sizeof ( *dhcphdr ) + client_id_len + server_id_len +
599                       ia_na_len + option_request_len + user_class_len +
600                       elapsed_len );
601
602         /* Allocate packet */
603         iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len );
604         if ( ! iobuf )
605                 return -ENOMEM;
606
607         /* Construct header */
608         dhcphdr = iob_put ( iobuf, sizeof ( *dhcphdr ) );
609         dhcphdr->type = dhcpv6->state->tx_type;
610         memcpy ( dhcphdr->xid, dhcpv6->xid, sizeof ( dhcphdr->xid ) );
611
612         /* Construct client identifier */
613         client_id = iob_put ( iobuf, client_id_len );
614         client_id->header.code = htons ( DHCPV6_CLIENT_ID );
615         client_id->header.len = htons ( client_id_len -
616                                         sizeof ( client_id->header ) );
617         memcpy ( client_id->duid, &dhcpv6->client_duid,
618                  sizeof ( dhcpv6->client_duid ) );
619
620         /* Construct server identifier, if applicable */
621         if ( server_id_len ) {
622                 server_id = iob_put ( iobuf, server_id_len );
623                 server_id->header.code = htons ( DHCPV6_SERVER_ID );
624                 server_id->header.len = htons ( server_id_len -
625                                                 sizeof ( server_id->header ) );
626                 memcpy ( server_id->duid, dhcpv6->server_duid,
627                          dhcpv6->server_duid_len );
628         }
629
630         /* Construct identity association, if applicable */
631         if ( ia_na_len ) {
632                 ia_na = iob_put ( iobuf, ia_na_len );
633                 ia_na->header.code = htons ( DHCPV6_IA_NA );
634                 ia_na->header.len = htons ( ia_na_len -
635                                             sizeof ( ia_na->header ) );
636                 ia_na->iaid = htonl ( dhcpv6->iaid );
637                 ia_na->renew = htonl ( 0 );
638                 ia_na->rebind = htonl ( 0 );
639                 if ( dhcpv6->state->flags & DHCPV6_TX_IAADDR ) {
640                         iaaddr = ( ( void * ) ia_na->options );
641                         iaaddr->header.code = htons ( DHCPV6_IAADDR );
642                         iaaddr->header.len = htons ( sizeof ( *iaaddr ) -
643                                                      sizeof ( iaaddr->header ));
644                         memcpy ( &iaaddr->address, &dhcpv6->lease,
645                                  sizeof ( iaaddr->address ) );
646                         iaaddr->preferred = htonl ( 0 );
647                         iaaddr->valid = htonl ( 0 );
648                 }
649         }
650
651         /* Construct option request */
652         option_request = iob_put ( iobuf, option_request_len );
653         option_request->header.code = htons ( DHCPV6_OPTION_REQUEST );
654         option_request->header.len = htons ( option_request_len -
655                                              sizeof ( option_request->header ));
656         memcpy ( option_request->requested, dhcpv6_requested_options,
657                  sizeof ( dhcpv6_requested_options ) );
658
659         /* Construct user class */
660         user_class = iob_put ( iobuf, user_class_len );
661         user_class->header.code = htons ( DHCPV6_USER_CLASS );
662         user_class->header.len = htons ( user_class_len -
663                                          sizeof ( user_class->header ) );
664         user_class->user_class[0].len = htons ( user_class_string_len );
665         dhcpv6_user_class ( user_class->user_class[0].string,
666                             user_class_string_len );
667
668         /* Construct elapsed time */
669         elapsed = iob_put ( iobuf, elapsed_len );
670         elapsed->header.code = htons ( DHCPV6_ELAPSED_TIME );
671         elapsed->header.len = htons ( elapsed_len -
672                                       sizeof ( elapsed->header ) );
673         elapsed->elapsed = htons ( ( ( currticks() - dhcpv6->start ) * 100 ) /
674                                    TICKS_PER_SEC );
675
676         /* Sanity check */
677         assert ( iob_len ( iobuf ) == total_len );
678
679         /* Transmit packet */
680         if ( ( rc = xfer_deliver_iob ( &dhcpv6->xfer, iobuf ) ) != 0 ) {
681                 DBGC ( dhcpv6, "DHCPv6 %s could not transmit: %s\n",
682                        dhcpv6->netdev->name, strerror ( rc ) );
683                 return rc;
684         }
685
686         return 0;
687 }
688
689 /**
690  * Handle timer expiry
691  *
692  * @v timer             Retransmission timer
693  * @v fail              Failure indicator
694  */
695 static void dhcpv6_timer_expired ( struct retry_timer *timer, int fail ) {
696         struct dhcpv6_session *dhcpv6 =
697                 container_of ( timer, struct dhcpv6_session, timer );
698
699         /* If we have failed, terminate DHCPv6 */
700         if ( fail ) {
701                 dhcpv6_finished ( dhcpv6, dhcpv6->rc );
702                 return;
703         }
704
705         /* Restart timer */
706         start_timer ( &dhcpv6->timer );
707
708         /* (Re)transmit current request */
709         dhcpv6_tx ( dhcpv6 );
710 }
711
712 /**
713  * Receive new data
714  *
715  * @v dhcpv6            DHCPv6 session
716  * @v iobuf             I/O buffer
717  * @v meta              Data transfer metadata
718  * @ret rc              Return status code
719  */
720 static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6,
721                        struct io_buffer *iobuf,
722                        struct xfer_metadata *meta ) {
723         struct settings *parent = netdev_settings ( dhcpv6->netdev );
724         struct sockaddr_in6 *src = ( ( struct sockaddr_in6 * ) meta->src );
725         struct dhcpv6_header *dhcphdr = iobuf->data;
726         struct dhcpv6_option_list options;
727         const union dhcpv6_any_option *option;
728         int rc;
729
730         /* Sanity checks */
731         if ( iob_len ( iobuf ) < sizeof ( *dhcphdr ) ) {
732                 DBGC ( dhcpv6, "DHCPv6 %s received packet too short (%zd "
733                        "bytes, min %zd bytes)\n", dhcpv6->netdev->name,
734                        iob_len ( iobuf ), sizeof ( *dhcphdr ) );
735                 rc = -EINVAL;
736                 goto done;
737         }
738         assert ( src != NULL );
739         assert ( src->sin6_family == AF_INET6 );
740         DBGC ( dhcpv6, "DHCPv6 %s received %s from %s\n",
741                dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
742                inet6_ntoa ( &src->sin6_addr ) );
743
744         /* Construct option list */
745         options.data = dhcphdr->options;
746         options.len = ( iob_len ( iobuf ) -
747                         offsetof ( typeof ( *dhcphdr ), options ) );
748
749         /* Verify client identifier */
750         if ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_CLIENT_ID,
751                                         &dhcpv6->client_duid,
752                                         sizeof ( dhcpv6->client_duid ) ) ) !=0){
753                 DBGC ( dhcpv6, "DHCPv6 %s received %s without correct client "
754                        "ID: %s\n", dhcpv6->netdev->name,
755                        dhcpv6_type_name ( dhcphdr->type ), strerror ( rc ) );
756                 goto done;
757         }
758
759         /* Verify server identifier, if applicable */
760         if ( dhcpv6->server_duid &&
761              ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_SERVER_ID,
762                                           dhcpv6->server_duid,
763                                           dhcpv6->server_duid_len ) ) != 0 ) ) {
764                 DBGC ( dhcpv6, "DHCPv6 %s received %s without correct server "
765                        "ID: %s\n", dhcpv6->netdev->name,
766                        dhcpv6_type_name ( dhcphdr->type ), strerror ( rc ) );
767                 goto done;
768         }
769
770         /* Check message type */
771         if ( dhcphdr->type != dhcpv6->state->rx_type ) {
772                 DBGC ( dhcpv6, "DHCPv6 %s received %s while expecting %s\n",
773                        dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
774                        dhcpv6_type_name ( dhcpv6->state->rx_type ) );
775                 rc = -ENOTTY;
776                 goto done;
777         }
778
779         /* Fetch status code, if present */
780         if ( ( rc = dhcpv6_status_code ( &options ) ) != 0 ) {
781                 DBGC ( dhcpv6, "DHCPv6 %s received %s with error status: %s\n",
782                        dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
783                        strerror ( rc ) );
784                 /* This is plausibly the error we want to return */
785                 dhcpv6->rc = rc;
786                 goto done;
787         }
788
789         /* Record identity association address, if applicable */
790         if ( dhcpv6->state->flags & DHCPV6_RX_RECORD_IAADDR ) {
791                 if ( ( rc = dhcpv6_iaaddr ( &options, dhcpv6->iaid,
792                                             &dhcpv6->lease ) ) != 0 ) {
793                         DBGC ( dhcpv6, "DHCPv6 %s received %s with unusable "
794                                "IAADDR: %s\n", dhcpv6->netdev->name,
795                                dhcpv6_type_name ( dhcphdr->type ),
796                                strerror ( rc ) );
797                         /* This is plausibly the error we want to return */
798                         dhcpv6->rc = rc;
799                         goto done;
800                 }
801                 DBGC ( dhcpv6, "DHCPv6 %s received %s is for %s\n",
802                        dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
803                        inet6_ntoa ( &dhcpv6->lease ) );
804         }
805
806         /* Record server ID, if applicable */
807         if ( dhcpv6->state->flags & DHCPV6_RX_RECORD_SERVER_ID ) {
808                 assert ( dhcpv6->server_duid == NULL );
809                 option = dhcpv6_option ( &options, DHCPV6_SERVER_ID );
810                 if ( ! option ) {
811                         DBGC ( dhcpv6, "DHCPv6 %s received %s missing server "
812                                "ID\n", dhcpv6->netdev->name,
813                                dhcpv6_type_name ( dhcphdr->type ) );
814                         rc = -EINVAL;
815                         goto done;
816                 }
817                 dhcpv6->server_duid_len = ntohs ( option->duid.header.len );
818                 dhcpv6->server_duid = malloc ( dhcpv6->server_duid_len );
819                 if ( ! dhcpv6->server_duid ) {
820                         rc = -ENOMEM;
821                         goto done;
822                 }
823                 memcpy ( dhcpv6->server_duid, option->duid.duid,
824                          dhcpv6->server_duid_len );
825         }
826
827         /* Apply identity association address, if applicable */
828         if ( dhcpv6->state->flags & DHCPV6_RX_APPLY_IAADDR ) {
829                 if ( ( rc = ipv6_set_address ( dhcpv6->netdev,
830                                                &dhcpv6->lease ) ) != 0 ) {
831                         DBGC ( dhcpv6, "DHCPv6 %s could not apply %s: %s\n",
832                                dhcpv6->netdev->name,
833                                inet6_ntoa ( &dhcpv6->lease ), strerror ( rc ) );
834                         /* This is plausibly the error we want to return */
835                         dhcpv6->rc = rc;
836                         goto done;
837                 }
838         }
839
840         /* Transition to next state or complete DHCPv6, as applicable */
841         if ( dhcpv6->state->next ) {
842
843                 /* Transition to next state */
844                 dhcpv6_set_state ( dhcpv6, dhcpv6->state->next );
845                 rc = 0;
846
847         } else {
848
849                 /* Register settings */
850                 if ( ( rc = dhcpv6_register ( &options, parent ) ) != 0 ) {
851                         DBGC ( dhcpv6, "DHCPv6 %s could not register "
852                                "settings: %s\n", dhcpv6->netdev->name,
853                                strerror ( rc ) );
854                         goto done;
855                 }
856
857                 /* Mark as complete */
858                 dhcpv6_finished ( dhcpv6, 0 );
859                 DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name );
860         }
861
862  done:
863         free_iob ( iobuf );
864         return rc;
865 }
866
867 /** DHCPv6 job control interface operations */
868 static struct interface_operation dhcpv6_job_op[] = {
869         INTF_OP ( intf_close, struct dhcpv6_session *, dhcpv6_finished ),
870 };
871
872 /** DHCPv6 job control interface descriptor */
873 static struct interface_descriptor dhcpv6_job_desc =
874         INTF_DESC ( struct dhcpv6_session, job, dhcpv6_job_op );
875
876 /** DHCPv6 data transfer interface operations */
877 static struct interface_operation dhcpv6_xfer_op[] = {
878         INTF_OP ( xfer_deliver, struct dhcpv6_session *, dhcpv6_rx ),
879 };
880
881 /** DHCPv6 data transfer interface descriptor */
882 static struct interface_descriptor dhcpv6_xfer_desc =
883         INTF_DESC ( struct dhcpv6_session, xfer, dhcpv6_xfer_op );
884
885 /**
886  * Start DHCPv6
887  *
888  * @v job               Job control interface
889  * @v netdev            Network device
890  * @v stateful          Perform stateful address autoconfiguration
891  * @ret rc              Return status code
892  */
893 int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
894                    int stateful ) {
895         struct ll_protocol *ll_protocol = netdev->ll_protocol;
896         struct dhcpv6_session *dhcpv6;
897         struct {
898                 union {
899                         struct sockaddr_in6 sin6;
900                         struct sockaddr sa;
901                 } client;
902                 union {
903                         struct sockaddr_in6 sin6;
904                         struct sockaddr sa;
905                 } server;
906         } addresses;
907         uint32_t xid;
908         int len;
909         int rc;
910
911         /* Allocate and initialise structure */
912         dhcpv6 = zalloc ( sizeof ( *dhcpv6 ) );
913         if ( ! dhcpv6 )
914                 return -ENOMEM;
915         ref_init ( &dhcpv6->refcnt, dhcpv6_free );
916         intf_init ( &dhcpv6->job, &dhcpv6_job_desc, &dhcpv6->refcnt );
917         intf_init ( &dhcpv6->xfer, &dhcpv6_xfer_desc, &dhcpv6->refcnt );
918         dhcpv6->netdev = netdev_get ( netdev );
919         xid = random();
920         memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) );
921         dhcpv6->start = currticks();
922         timer_init ( &dhcpv6->timer, dhcpv6_timer_expired, &dhcpv6->refcnt );
923
924         /* Construct client and server addresses */
925         memset ( &addresses, 0, sizeof ( addresses ) );
926         addresses.client.sin6.sin6_family = AF_INET6;
927         addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT );
928         addresses.server.sin6.sin6_family = AF_INET6;
929         ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr );
930         addresses.server.sin6.sin6_scope_id = netdev->index;
931         addresses.server.sin6.sin6_port = htons ( DHCPV6_SERVER_PORT );
932
933         /* Construct client DUID from system UUID */
934         dhcpv6->client_duid.type = htons ( DHCPV6_DUID_UUID );
935         if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
936                                           &dhcpv6->client_duid.uuid ) ) < 0 ) {
937                 rc = len;
938                 DBGC ( dhcpv6, "DHCPv6 %s could not create DUID-UUID: %s\n",
939                        dhcpv6->netdev->name, strerror ( rc ) );
940                 goto err_client_duid;
941         }
942
943         /* Construct IAID from link-layer address */
944         dhcpv6->iaid = crc32_le ( 0, netdev->ll_addr, ll_protocol->ll_addr_len);
945         DBGC ( dhcpv6, "DHCPv6 %s has XID %02x%02x%02x\n", dhcpv6->netdev->name,
946                dhcpv6->xid[0], dhcpv6->xid[1], dhcpv6->xid[2] );
947
948         /* Enter initial state */
949         dhcpv6_set_state ( dhcpv6, ( stateful ? &dhcpv6_solicit :
950                                      &dhcpv6_information_request ) );
951
952         /* Open socket */
953         if ( ( rc = xfer_open_socket ( &dhcpv6->xfer, SOCK_DGRAM,
954                                        &addresses.server.sa,
955                                        &addresses.client.sa ) ) != 0 ) {
956                 DBGC ( dhcpv6, "DHCPv6 %s could not open socket: %s\n",
957                        dhcpv6->netdev->name, strerror ( rc ) );
958                 goto err_open_socket;
959         }
960
961         /* Attach parent interface, mortalise self, and return */
962         intf_plug_plug ( &dhcpv6->job, job );
963         ref_put ( &dhcpv6->refcnt );
964         return 0;
965
966  err_open_socket:
967         dhcpv6_finished ( dhcpv6, rc );
968  err_client_duid:
969         ref_put ( &dhcpv6->refcnt );
970         return rc;
971 }
972
973 /** Boot filename setting */
974 const struct setting filename6_setting __setting ( SETTING_BOOT, filename ) = {
975         .name = "filename",
976         .description = "Boot filename",
977         .tag = DHCPV6_BOOTFILE_URL,
978         .type = &setting_type_string,
979         .scope = &ipv6_scope,
980 };
981
982 /** DNS search list setting */
983 const struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA, dnssl ) = {
984         .name = "dnssl",
985         .description = "DNS search list",
986         .tag = DHCPV6_DOMAIN_LIST,
987         .type = &setting_type_dnssl,
988         .scope = &ipv6_scope,
989 };