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