2 * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
20 FILE_LICENCE ( GPL2_OR_LATER );
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>
36 #include <ipxe/crc32.h>
37 #include <ipxe/errortab.h>
38 #include <ipxe/ipv6.h>
39 #include <ipxe/dhcpv6.h>
43 * Dynamic Host Configuration Protocol for IPv6
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 )
68 /** Human-readable error messages */
69 struct errortab dhcpv6_errors[] __errortab = {
70 __einfo_errortab ( EINFO_EPROTO_NOADDRSAVAIL ),
73 /****************************************************************************
79 /** A DHCPv6 option list */
80 struct dhcpv6_option_list {
83 /** Length of data buffer */
90 * @v options DHCPv6 option list
92 * @ret option DHCPv6 option, or NULL if not found
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;
100 /* Scan through list of options */
101 while ( remaining >= sizeof ( option->header ) ) {
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 */
111 /* Return if we have found the specified option */
112 if ( option->header.code == htons ( code ) )
115 /* Otherwise, move to the next option */
116 option = ( ( ( void * ) option->header.data ) + data_len );
117 remaining -= data_len;
124 * Check DHCPv6 client or server identifier
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
132 static int dhcpv6_check_duid ( struct dhcpv6_option_list *options,
133 unsigned int code, const void *expected,
135 const union dhcpv6_any_option *option;
136 const struct dhcpv6_duid_option *duid;
139 option = dhcpv6_option ( options, code );
142 duid = &option->duid;
144 /* Check option length */
145 if ( ntohs ( duid->header.len ) != len )
148 /* Compare option value */
149 if ( memcmp ( duid->duid, expected, len ) != 0 )
156 * Get DHCPv6 status code
158 * @v options DHCPv6 option list
159 * @ret rc Return status code
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;
166 /* Find status code option, if present */
167 option = dhcpv6_option ( options, DHCPV6_STATUS_CODE );
169 /* Omitted status code should be treated as "success" */
172 status_code = &option->status_code;
175 if ( ntohs ( status_code->header.len ) <
176 ( sizeof ( *status_code ) - sizeof ( status_code->header ) ) ) {
180 /* Calculate iPXE error code from DHCPv6 status code */
181 status = ntohs ( status_code->status );
182 return ( status ? -EPROTO_STATUS ( status ) : 0 );
186 * Get DHCPv6 identity association address
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
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;
202 /* Find identity association option, if present */
203 option = dhcpv6_option ( options, DHCPV6_IA_NA );
206 ia_na = &option->ia_na;
209 len = ntohs ( ia_na->header.len );
210 if ( len < ( sizeof ( *ia_na ) - sizeof ( ia_na->header ) ) )
213 /* Check identity association ID */
214 if ( ia_na->iaid != htonl ( iaid ) )
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 ) );
222 /* Check IA_NA status code */
223 if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
226 /* Find identity association address, if present */
227 option = dhcpv6_option ( &suboptions, DHCPV6_IAADDR );
230 iaaddr = &option->iaaddr;
233 len = ntohs ( iaaddr->header.len );
234 if ( len < ( sizeof ( *iaaddr ) - sizeof ( iaaddr->header ) ) )
237 /* Construct IAADDR sub-options list */
238 suboptions.data = iaaddr->options;
239 suboptions.len = ( len + sizeof ( iaaddr->header ) -
240 offsetof ( typeof ( *iaaddr ), options ) );
242 /* Check IAADDR status code */
243 if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
246 /* Extract IPv6 address */
247 memcpy ( address, &iaaddr->address, sizeof ( *address ) );
252 /****************************************************************************
254 * DHCPv6 settings blocks
258 /** A DHCPv6 settings block */
259 struct dhcpv6_settings {
260 /** Reference count */
261 struct refcnt refcnt;
262 /** Settings block */
263 struct settings settings;
265 struct dhcpv6_option_list options;
269 * Check applicability of DHCPv6 setting
271 * @v settings Settings block
273 * @ret applies Setting applies within this settings block
275 static int dhcpv6_applies ( struct settings *settings __unused,
276 const struct setting *setting ) {
278 return ( setting->scope == &ipv6_scope );
282 * Fetch value of DHCPv6 setting
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
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;
299 option = dhcpv6_option ( &dhcpv6set->options, setting->tag );
303 /* Copy option to data buffer */
304 option_len = ntohs ( option->header.len );
305 if ( len > option_len )
307 memcpy ( data, option->header.data, len );
311 /** DHCPv6 settings operations */
312 static struct settings_operations dhcpv6_settings_operations = {
313 .applies = dhcpv6_applies,
314 .fetch = dhcpv6_fetch,
318 * Register DHCPv6 options as network device settings
320 * @v options DHCPv6 option list
321 * @v parent Parent settings block
322 * @ret rc Return status code
324 static int dhcpv6_register ( struct dhcpv6_option_list *options,
325 struct settings *parent ) {
326 struct dhcpv6_settings *dhcpv6set;
331 /* Allocate and initialise structure */
332 dhcpv6set = zalloc ( sizeof ( *dhcpv6set ) + options->len );
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 ) );
342 memcpy ( data, options->data, len );
343 dhcpv6set->options.data = data;
344 dhcpv6set->options.len = len;
346 /* Register settings */
347 if ( ( rc = register_settings ( &dhcpv6set->settings, parent,
348 DHCPV6_SETTINGS_NAME ) ) != 0 )
352 ref_put ( &dhcpv6set->refcnt );
357 /****************************************************************************
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 ),
370 * Name a DHCPv6 packet type
372 * @v type DHCPv6 packet type
373 * @ret name DHCPv6 packet type name
375 static __attribute__ (( unused )) const char *
376 dhcpv6_type_name ( unsigned int type ) {
377 static char buf[ 12 /* "UNKNOWN-xxx" + NUL */ ];
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";
386 snprintf ( buf, sizeof ( buf ), "UNKNOWN-%d", type );
391 /** A DHCPv6 session state */
392 struct dhcpv6_session_state {
393 /** Current transmitted packet type */
395 /** Current expected received packet type */
399 /** Next state (or NULL to terminate) */
400 struct dhcpv6_session_state *next;
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,
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 ),
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,
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,
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;
452 /** Network device being configured */
453 struct net_device *netdev;
454 /** Transaction ID */
456 /** Identity association ID */
458 /** Start time (in ticks) */
461 struct dhcpv6_duid_uuid client_duid;
462 /** Server DUID, if known */
464 /** Server DUID length */
465 size_t server_duid_len;
466 /** Leased IPv6 address */
467 struct in6_addr lease;
469 /** Retransmission timer */
470 struct retry_timer timer;
472 /** Current session state */
473 struct dhcpv6_session_state *state;
474 /** Current timeout status code */
479 * Free DHCPv6 session
481 * @v refcnt Reference count
483 static void dhcpv6_free ( struct refcnt *refcnt ) {
484 struct dhcpv6_session *dhcpv6 =
485 container_of ( refcnt, struct dhcpv6_session, refcnt );
487 netdev_put ( dhcpv6->netdev );
488 free ( dhcpv6->server_duid );
493 * Terminate DHCPv6 session
495 * @v dhcpv6 DHCPv6 session
496 * @v rc Reason for close
498 static void dhcpv6_finished ( struct dhcpv6_session *dhcpv6, int rc ) {
501 stop_timer ( &dhcpv6->timer );
503 /* Shut down interfaces */
504 intf_shutdown ( &dhcpv6->xfer, rc );
505 intf_shutdown ( &dhcpv6->job, rc );
509 * Transition to new DHCPv6 session state
511 * @v dhcpv6 DHCPv6 session
512 * @v state New session state
514 static void dhcpv6_set_state ( struct dhcpv6_session *dhcpv6,
515 struct dhcpv6_session_state *state ) {
517 DBGC ( dhcpv6, "DHCPv6 %s entering %s state\n", dhcpv6->netdev->name,
518 dhcpv6_type_name ( state->tx_type ) );
521 dhcpv6->state = state;
523 /* Default to -ETIMEDOUT if no more specific error is recorded */
524 dhcpv6->rc = -ETIMEDOUT;
526 /* Start timer to trigger transmission */
527 start_timer_nodelay ( &dhcpv6->timer );
531 * Get DHCPv6 user class
533 * @v data Data buffer
534 * @v len Length of data buffer
535 * @ret len Length of user class
537 static size_t dhcpv6_user_class ( void *data, size_t len ) {
538 static const char default_user_class[4] = { 'i', 'P', 'X', 'E' };
541 /* Fetch user-class setting, if defined */
542 actual_len = fetch_raw_setting ( NULL, &user_class_setting, data, len );
543 if ( actual_len >= 0 )
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 );
554 * Transmit current request
556 * @v dhcpv6 DHCPv6 session
557 * @ret rc Return status code
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;
572 size_t option_request_len;
573 size_t user_class_string_len;
574 size_t user_class_len;
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 );
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 +
602 /* Allocate packet */
603 iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len );
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 ) );
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 ) );
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 );
630 /* Construct identity association, if applicable */
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 );
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 ) );
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 );
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 ) /
677 assert ( iob_len ( iobuf ) == total_len );
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 ) );
690 * Handle timer expiry
692 * @v timer Retransmission timer
693 * @v fail Failure indicator
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 );
699 /* If we have failed, terminate DHCPv6 */
701 dhcpv6_finished ( dhcpv6, dhcpv6->rc );
706 start_timer ( &dhcpv6->timer );
708 /* (Re)transmit current request */
709 dhcpv6_tx ( dhcpv6 );
715 * @v dhcpv6 DHCPv6 session
716 * @v iobuf I/O buffer
717 * @v meta Data transfer metadata
718 * @ret rc Return status code
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;
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 ) );
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 ) );
744 /* Construct option list */
745 options.data = dhcphdr->options;
746 options.len = ( iob_len ( iobuf ) -
747 offsetof ( typeof ( *dhcphdr ), options ) );
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 ) );
759 /* Verify server identifier, if applicable */
760 if ( dhcpv6->server_duid &&
761 ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_SERVER_ID,
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 ) );
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 ) );
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 ),
784 /* This is plausibly the error we want to return */
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 ),
797 /* This is plausibly the error we want to return */
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 ) );
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 );
811 DBGC ( dhcpv6, "DHCPv6 %s received %s missing server "
812 "ID\n", dhcpv6->netdev->name,
813 dhcpv6_type_name ( dhcphdr->type ) );
817 dhcpv6->server_duid_len = ntohs ( option->duid.header.len );
818 dhcpv6->server_duid = malloc ( dhcpv6->server_duid_len );
819 if ( ! dhcpv6->server_duid ) {
823 memcpy ( dhcpv6->server_duid, option->duid.duid,
824 dhcpv6->server_duid_len );
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 */
840 /* Transition to next state or complete DHCPv6, as applicable */
841 if ( dhcpv6->state->next ) {
843 /* Transition to next state */
844 dhcpv6_set_state ( dhcpv6, dhcpv6->state->next );
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,
857 /* Mark as complete */
858 dhcpv6_finished ( dhcpv6, 0 );
859 DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name );
867 /** DHCPv6 job control interface operations */
868 static struct interface_operation dhcpv6_job_op[] = {
869 INTF_OP ( intf_close, struct dhcpv6_session *, dhcpv6_finished ),
872 /** DHCPv6 job control interface descriptor */
873 static struct interface_descriptor dhcpv6_job_desc =
874 INTF_DESC ( struct dhcpv6_session, job, dhcpv6_job_op );
876 /** DHCPv6 data transfer interface operations */
877 static struct interface_operation dhcpv6_xfer_op[] = {
878 INTF_OP ( xfer_deliver, struct dhcpv6_session *, dhcpv6_rx ),
881 /** DHCPv6 data transfer interface descriptor */
882 static struct interface_descriptor dhcpv6_xfer_desc =
883 INTF_DESC ( struct dhcpv6_session, xfer, dhcpv6_xfer_op );
888 * @v job Job control interface
889 * @v netdev Network device
890 * @v stateful Perform stateful address autoconfiguration
891 * @ret rc Return status code
893 int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
895 struct ll_protocol *ll_protocol = netdev->ll_protocol;
896 struct dhcpv6_session *dhcpv6;
899 struct sockaddr_in6 sin6;
903 struct sockaddr_in6 sin6;
911 /* Allocate and initialise structure */
912 dhcpv6 = zalloc ( sizeof ( *dhcpv6 ) );
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 );
920 memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) );
921 dhcpv6->start = currticks();
922 timer_init ( &dhcpv6->timer, dhcpv6_timer_expired, &dhcpv6->refcnt );
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 );
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 ) {
938 DBGC ( dhcpv6, "DHCPv6 %s could not create DUID-UUID: %s\n",
939 dhcpv6->netdev->name, strerror ( rc ) );
940 goto err_client_duid;
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] );
948 /* Enter initial state */
949 dhcpv6_set_state ( dhcpv6, ( stateful ? &dhcpv6_solicit :
950 &dhcpv6_information_request ) );
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;
961 /* Attach parent interface, mortalise self, and return */
962 intf_plug_plug ( &dhcpv6->job, job );
963 ref_put ( &dhcpv6->refcnt );
967 dhcpv6_finished ( dhcpv6, rc );
969 ref_put ( &dhcpv6->refcnt );
973 /** Boot filename setting */
974 const struct setting filename6_setting __setting ( SETTING_BOOT, filename ) = {
976 .description = "Boot filename",
977 .tag = DHCPV6_BOOTFILE_URL,
978 .type = &setting_type_string,
979 .scope = &ipv6_scope,
982 /** DNS search list setting */
983 const struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA, dnssl ) = {
985 .description = "DNS search list",
986 .tag = DHCPV6_DOMAIN_LIST,
987 .type = &setting_type_dnssl,
988 .scope = &ipv6_scope,