[efi] Include installed protocol list in unknown handle names
[ipxe.git] / 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 };