You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dhcpv6.c 28KB

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