dhcpv6.c 30KB

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