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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
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>
40 #include <ipxe/crc32.h>
41 #include <ipxe/errortab.h>
42 #include <ipxe/ipv6.h>
43 #include <ipxe/dhcpv6.h>
47 * Dynamic Host Configuration Protocol for IPv6
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 )
72 /** Human-readable error messages */
73 struct errortab dhcpv6_errors[] __errortab = {
74 __einfo_errortab ( EINFO_EPROTO_NOADDRSAVAIL ),
77 /****************************************************************************
83 /** A DHCPv6 option list */
84 struct dhcpv6_option_list {
87 /** Length of data buffer */
94 * @v options DHCPv6 option list
96 * @ret option DHCPv6 option, or NULL if not found
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;
104 /* Scan through list of options */
105 while ( remaining >= sizeof ( option->header ) ) {
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 */
115 /* Return if we have found the specified option */
116 if ( option->header.code == htons ( code ) )
119 /* Otherwise, move to the next option */
120 option = ( ( ( void * ) option->header.data ) + data_len );
121 remaining -= data_len;
128 * Check DHCPv6 client or server identifier
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
136 static int dhcpv6_check_duid ( struct dhcpv6_option_list *options,
137 unsigned int code, const void *expected,
139 const union dhcpv6_any_option *option;
140 const struct dhcpv6_duid_option *duid;
143 option = dhcpv6_option ( options, code );
146 duid = &option->duid;
148 /* Check option length */
149 if ( ntohs ( duid->header.len ) != len )
152 /* Compare option value */
153 if ( memcmp ( duid->duid, expected, len ) != 0 )
160 * Get DHCPv6 status code
162 * @v options DHCPv6 option list
163 * @ret rc Return status code
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;
170 /* Find status code option, if present */
171 option = dhcpv6_option ( options, DHCPV6_STATUS_CODE );
173 /* Omitted status code should be treated as "success" */
176 status_code = &option->status_code;
179 if ( ntohs ( status_code->header.len ) <
180 ( sizeof ( *status_code ) - sizeof ( status_code->header ) ) ) {
184 /* Calculate iPXE error code from DHCPv6 status code */
185 status = ntohs ( status_code->status );
186 return ( status ? -EPROTO_STATUS ( status ) : 0 );
190 * Get DHCPv6 identity association address
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
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;
206 /* Find identity association option, if present */
207 option = dhcpv6_option ( options, DHCPV6_IA_NA );
210 ia_na = &option->ia_na;
213 len = ntohs ( ia_na->header.len );
214 if ( len < ( sizeof ( *ia_na ) - sizeof ( ia_na->header ) ) )
217 /* Check identity association ID */
218 if ( ia_na->iaid != htonl ( iaid ) )
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 ) );
226 /* Check IA_NA status code */
227 if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
230 /* Find identity association address, if present */
231 option = dhcpv6_option ( &suboptions, DHCPV6_IAADDR );
234 iaaddr = &option->iaaddr;
237 len = ntohs ( iaaddr->header.len );
238 if ( len < ( sizeof ( *iaaddr ) - sizeof ( iaaddr->header ) ) )
241 /* Construct IAADDR sub-options list */
242 suboptions.data = iaaddr->options;
243 suboptions.len = ( len + sizeof ( iaaddr->header ) -
244 offsetof ( typeof ( *iaaddr ), options ) );
246 /* Check IAADDR status code */
247 if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
250 /* Extract IPv6 address */
251 memcpy ( address, &iaaddr->address, sizeof ( *address ) );
256 /****************************************************************************
258 * DHCPv6 settings blocks
262 /** A DHCPv6 settings block */
263 struct dhcpv6_settings {
264 /** Reference count */
265 struct refcnt refcnt;
266 /** Settings block */
267 struct settings settings;
269 struct dhcpv6_option_list options;
273 * Check applicability of DHCPv6 setting
275 * @v settings Settings block
277 * @ret applies Setting applies within this settings block
279 static int dhcpv6_applies ( struct settings *settings __unused,
280 const struct setting *setting ) {
282 return ( setting->scope == &ipv6_scope );
286 * Fetch value of DHCPv6 setting
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
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;
303 option = dhcpv6_option ( &dhcpv6set->options, setting->tag );
307 /* Copy option to data buffer */
308 option_len = ntohs ( option->header.len );
309 if ( len > option_len )
311 memcpy ( data, option->header.data, len );
315 /** DHCPv6 settings operations */
316 static struct settings_operations dhcpv6_settings_operations = {
317 .applies = dhcpv6_applies,
318 .fetch = dhcpv6_fetch,
322 * Register DHCPv6 options as network device settings
324 * @v options DHCPv6 option list
325 * @v parent Parent settings block
326 * @ret rc Return status code
328 static int dhcpv6_register ( struct dhcpv6_option_list *options,
329 struct settings *parent ) {
330 struct dhcpv6_settings *dhcpv6set;
335 /* Allocate and initialise structure */
336 dhcpv6set = zalloc ( sizeof ( *dhcpv6set ) + options->len );
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 ) );
346 memcpy ( data, options->data, len );
347 dhcpv6set->options.data = data;
348 dhcpv6set->options.len = len;
350 /* Register settings */
351 if ( ( rc = register_settings ( &dhcpv6set->settings, parent,
352 DHCPV6_SETTINGS_NAME ) ) != 0 )
356 ref_put ( &dhcpv6set->refcnt );
361 /****************************************************************************
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 ),
374 * Name a DHCPv6 packet type
376 * @v type DHCPv6 packet type
377 * @ret name DHCPv6 packet type name
379 static __attribute__ (( unused )) const char *
380 dhcpv6_type_name ( unsigned int type ) {
381 static char buf[ 12 /* "UNKNOWN-xxx" + NUL */ ];
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";
390 snprintf ( buf, sizeof ( buf ), "UNKNOWN-%d", type );
395 /** A DHCPv6 session state */
396 struct dhcpv6_session_state {
397 /** Current transmitted packet type */
399 /** Current expected received packet type */
403 /** Next state (or NULL to terminate) */
404 struct dhcpv6_session_state *next;
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,
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 ),
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,
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,
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;
456 /** Network device being configured */
457 struct net_device *netdev;
458 /** Transaction ID */
460 /** Identity association ID */
462 /** Start time (in ticks) */
465 struct dhcpv6_duid_uuid client_duid;
466 /** Server DUID, if known */
468 /** Server DUID length */
469 size_t server_duid_len;
470 /** Leased IPv6 address */
471 struct in6_addr lease;
473 /** Retransmission timer */
474 struct retry_timer timer;
476 /** Current session state */
477 struct dhcpv6_session_state *state;
478 /** Current timeout status code */
483 * Free DHCPv6 session
485 * @v refcnt Reference count
487 static void dhcpv6_free ( struct refcnt *refcnt ) {
488 struct dhcpv6_session *dhcpv6 =
489 container_of ( refcnt, struct dhcpv6_session, refcnt );
491 netdev_put ( dhcpv6->netdev );
492 free ( dhcpv6->server_duid );
497 * Terminate DHCPv6 session
499 * @v dhcpv6 DHCPv6 session
500 * @v rc Reason for close
502 static void dhcpv6_finished ( struct dhcpv6_session *dhcpv6, int rc ) {
505 stop_timer ( &dhcpv6->timer );
507 /* Shut down interfaces */
508 intf_shutdown ( &dhcpv6->xfer, rc );
509 intf_shutdown ( &dhcpv6->job, rc );
513 * Transition to new DHCPv6 session state
515 * @v dhcpv6 DHCPv6 session
516 * @v state New session state
518 static void dhcpv6_set_state ( struct dhcpv6_session *dhcpv6,
519 struct dhcpv6_session_state *state ) {
521 DBGC ( dhcpv6, "DHCPv6 %s entering %s state\n", dhcpv6->netdev->name,
522 dhcpv6_type_name ( state->tx_type ) );
525 dhcpv6->state = state;
527 /* Default to -ETIMEDOUT if no more specific error is recorded */
528 dhcpv6->rc = -ETIMEDOUT;
530 /* Start timer to trigger transmission */
531 start_timer_nodelay ( &dhcpv6->timer );
535 * Get DHCPv6 user class
537 * @v data Data buffer
538 * @v len Length of data buffer
539 * @ret len Length of user class
541 static size_t dhcpv6_user_class ( void *data, size_t len ) {
542 static const char default_user_class[4] = { 'i', 'P', 'X', 'E' };
545 /* Fetch user-class setting, if defined */
546 actual_len = fetch_raw_setting ( NULL, &user_class_setting, data, len );
547 if ( actual_len >= 0 )
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 );
558 * Transmit current request
560 * @v dhcpv6 DHCPv6 session
561 * @ret rc Return status code
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;
576 size_t option_request_len;
577 size_t user_class_string_len;
578 size_t user_class_len;
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 );
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 +
606 /* Allocate packet */
607 iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len );
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 ) );
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 ) );
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 );
634 /* Construct identity association, if applicable */
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 );
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 ) );
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 );
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 ) /
681 assert ( iob_len ( iobuf ) == total_len );
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 ) );
694 * Handle timer expiry
696 * @v timer Retransmission timer
697 * @v fail Failure indicator
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 );
703 /* If we have failed, terminate DHCPv6 */
705 dhcpv6_finished ( dhcpv6, dhcpv6->rc );
710 start_timer ( &dhcpv6->timer );
712 /* (Re)transmit current request */
713 dhcpv6_tx ( dhcpv6 );
719 * @v dhcpv6 DHCPv6 session
720 * @v iobuf I/O buffer
721 * @v meta Data transfer metadata
722 * @ret rc Return status code
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;
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 ) );
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 ) );
748 /* Construct option list */
749 options.data = dhcphdr->options;
750 options.len = ( iob_len ( iobuf ) -
751 offsetof ( typeof ( *dhcphdr ), options ) );
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 ) );
763 /* Verify server identifier, if applicable */
764 if ( dhcpv6->server_duid &&
765 ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_SERVER_ID,
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 ) );
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 ) );
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 ),
788 /* This is plausibly the error we want to return */
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 ),
801 /* This is plausibly the error we want to return */
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 ) );
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 );
815 DBGC ( dhcpv6, "DHCPv6 %s received %s missing server "
816 "ID\n", dhcpv6->netdev->name,
817 dhcpv6_type_name ( dhcphdr->type ) );
821 dhcpv6->server_duid_len = ntohs ( option->duid.header.len );
822 dhcpv6->server_duid = malloc ( dhcpv6->server_duid_len );
823 if ( ! dhcpv6->server_duid ) {
827 memcpy ( dhcpv6->server_duid, option->duid.duid,
828 dhcpv6->server_duid_len );
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 */
844 /* Transition to next state or complete DHCPv6, as applicable */
845 if ( dhcpv6->state->next ) {
847 /* Transition to next state */
848 dhcpv6_set_state ( dhcpv6, dhcpv6->state->next );
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,
861 /* Mark as complete */
862 dhcpv6_finished ( dhcpv6, 0 );
863 DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name );
871 /** DHCPv6 job control interface operations */
872 static struct interface_operation dhcpv6_job_op[] = {
873 INTF_OP ( intf_close, struct dhcpv6_session *, dhcpv6_finished ),
876 /** DHCPv6 job control interface descriptor */
877 static struct interface_descriptor dhcpv6_job_desc =
878 INTF_DESC ( struct dhcpv6_session, job, dhcpv6_job_op );
880 /** DHCPv6 data transfer interface operations */
881 static struct interface_operation dhcpv6_xfer_op[] = {
882 INTF_OP ( xfer_deliver, struct dhcpv6_session *, dhcpv6_rx ),
885 /** DHCPv6 data transfer interface descriptor */
886 static struct interface_descriptor dhcpv6_xfer_desc =
887 INTF_DESC ( struct dhcpv6_session, xfer, dhcpv6_xfer_op );
892 * @v job Job control interface
893 * @v netdev Network device
894 * @v stateful Perform stateful address autoconfiguration
895 * @ret rc Return status code
897 int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
899 struct ll_protocol *ll_protocol = netdev->ll_protocol;
900 struct dhcpv6_session *dhcpv6;
903 struct sockaddr_in6 sin6;
907 struct sockaddr_in6 sin6;
915 /* Allocate and initialise structure */
916 dhcpv6 = zalloc ( sizeof ( *dhcpv6 ) );
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 );
924 memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) );
925 dhcpv6->start = currticks();
926 timer_init ( &dhcpv6->timer, dhcpv6_timer_expired, &dhcpv6->refcnt );
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 );
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 ) {
942 DBGC ( dhcpv6, "DHCPv6 %s could not create DUID-UUID: %s\n",
943 dhcpv6->netdev->name, strerror ( rc ) );
944 goto err_client_duid;
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] );
952 /* Enter initial state */
953 dhcpv6_set_state ( dhcpv6, ( stateful ? &dhcpv6_solicit :
954 &dhcpv6_information_request ) );
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;
965 /* Attach parent interface, mortalise self, and return */
966 intf_plug_plug ( &dhcpv6->job, job );
967 ref_put ( &dhcpv6->refcnt );
971 dhcpv6_finished ( dhcpv6, rc );
973 ref_put ( &dhcpv6->refcnt );
977 /** Boot filename setting */
978 const struct setting filename6_setting __setting ( SETTING_BOOT, filename ) = {
980 .description = "Boot filename",
981 .tag = DHCPV6_BOOTFILE_URL,
982 .type = &setting_type_string,
983 .scope = &ipv6_scope,
986 /** DNS search list setting */
987 const struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA, dnssl ) = {
989 .description = "DNS search list",
990 .tag = DHCPV6_DOMAIN_LIST,
991 .type = &setting_type_dnssl,
992 .scope = &ipv6_scope,