Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369
  1. /*
  2. * Copyright (C) 2015 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 (at your option) 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 <errno.h>
  27. #include <ipxe/http.h>
  28. #include <ipxe/iobuf.h>
  29. #include <ipxe/xfer.h>
  30. #include <ipxe/uri.h>
  31. #include <ipxe/timer.h>
  32. #include <ipxe/profile.h>
  33. #include <ipxe/fault.h>
  34. #include <ipxe/pccrr.h>
  35. #include <ipxe/peerblk.h>
  36. /** @file
  37. *
  38. * Peer Content Caching and Retrieval (PeerDist) protocol block downloads
  39. *
  40. */
  41. /** PeerDist decryption chunksize
  42. *
  43. * This is a policy decision.
  44. */
  45. #define PEERBLK_DECRYPT_CHUNKSIZE 2048
  46. /** PeerDist raw block download attempt initial progress timeout
  47. *
  48. * This is a policy decision.
  49. */
  50. #define PEERBLK_RAW_OPEN_TIMEOUT ( 10 * TICKS_PER_SEC )
  51. /** PeerDist raw block download attempt ongoing progress timeout
  52. *
  53. * This is a policy decision.
  54. */
  55. #define PEERBLK_RAW_RX_TIMEOUT ( 15 * TICKS_PER_SEC )
  56. /** PeerDist retrieval protocol block download attempt initial progress timeout
  57. *
  58. * This is a policy decision.
  59. */
  60. #define PEERBLK_RETRIEVAL_OPEN_TIMEOUT ( 3 * TICKS_PER_SEC )
  61. /** PeerDist retrieval protocol block download attempt ongoing progress timeout
  62. *
  63. * This is a policy decision.
  64. */
  65. #define PEERBLK_RETRIEVAL_RX_TIMEOUT ( 5 * TICKS_PER_SEC )
  66. /** PeerDist maximum number of full download attempt cycles
  67. *
  68. * This is the maximum number of times that we will try a full cycle
  69. * of download attempts (i.e. a retrieval protocol download attempt
  70. * from each discovered peer plus a raw download attempt from the
  71. * origin server).
  72. *
  73. * This is a policy decision.
  74. */
  75. #define PEERBLK_MAX_ATTEMPT_CYCLES 4
  76. /** PeerDist block download profiler */
  77. static struct profiler peerblk_download_profiler __profiler =
  78. { .name = "peerblk.download" };
  79. /** PeerDist block download attempt success profiler */
  80. static struct profiler peerblk_attempt_success_profiler __profiler =
  81. { .name = "peerblk.attempt.success" };
  82. /** PeerDist block download attempt failure profiler */
  83. static struct profiler peerblk_attempt_failure_profiler __profiler =
  84. { .name = "peerblk.attempt.failure" };
  85. /** PeerDist block download attempt timeout profiler */
  86. static struct profiler peerblk_attempt_timeout_profiler __profiler =
  87. { .name = "peerblk.attempt.timeout" };
  88. /** PeerDist block download discovery success profiler */
  89. static struct profiler peerblk_discovery_success_profiler __profiler =
  90. { .name = "peerblk.discovery.success" };
  91. /** PeerDist block download discovery timeout profiler */
  92. static struct profiler peerblk_discovery_timeout_profiler __profiler =
  93. { .name = "peerblk.discovery.timeout" };
  94. /**
  95. * Get profiling timestamp
  96. *
  97. * @ret timestamp Timestamp
  98. */
  99. static inline __attribute__ (( always_inline )) unsigned long
  100. peerblk_timestamp ( void ) {
  101. if ( PROFILING ) {
  102. return currticks();
  103. } else {
  104. return 0;
  105. }
  106. }
  107. /**
  108. * Free PeerDist block download
  109. *
  110. * @v refcnt Reference count
  111. */
  112. static void peerblk_free ( struct refcnt *refcnt ) {
  113. struct peerdist_block *peerblk =
  114. container_of ( refcnt, struct peerdist_block, refcnt );
  115. uri_put ( peerblk->uri );
  116. free ( peerblk->cipherctx );
  117. free ( peerblk );
  118. }
  119. /**
  120. * Reset PeerDist block download attempt
  121. *
  122. * @v peerblk PeerDist block download
  123. * @v rc Reason for reset
  124. */
  125. static void peerblk_reset ( struct peerdist_block *peerblk, int rc ) {
  126. /* Stop decryption process */
  127. process_del ( &peerblk->process );
  128. /* Stop timer */
  129. stop_timer ( &peerblk->timer );
  130. /* Abort any current download attempt */
  131. intf_restart ( &peerblk->raw, rc );
  132. intf_restart ( &peerblk->retrieval, rc );
  133. /* Empty received data buffer */
  134. xferbuf_free ( &peerblk->buffer );
  135. peerblk->pos = 0;
  136. /* Reset digest and free cipher context */
  137. digest_init ( peerblk->digest, peerblk->digestctx );
  138. free ( peerblk->cipherctx );
  139. peerblk->cipherctx = NULL;
  140. peerblk->cipher = NULL;
  141. /* Reset trim thresholds */
  142. peerblk->start = ( peerblk->trim.start - peerblk->range.start );
  143. peerblk->end = ( peerblk->trim.end - peerblk->range.start );
  144. assert ( peerblk->start <= peerblk->end );
  145. }
  146. /**
  147. * Close PeerDist block download
  148. *
  149. * @v peerblk PeerDist block download
  150. * @v rc Reason for close
  151. */
  152. static void peerblk_close ( struct peerdist_block *peerblk, int rc ) {
  153. unsigned long now = peerblk_timestamp();
  154. /* Profile overall block download */
  155. profile_custom ( &peerblk_download_profiler,
  156. ( now - peerblk->started ) );
  157. /* Reset download attempt */
  158. peerblk_reset ( peerblk, rc );
  159. /* Close discovery */
  160. peerdisc_close ( &peerblk->discovery );
  161. /* Shut down all interfaces */
  162. intf_shutdown ( &peerblk->retrieval, rc );
  163. intf_shutdown ( &peerblk->raw, rc );
  164. intf_shutdown ( &peerblk->xfer, rc );
  165. }
  166. /**
  167. * Calculate offset within overall download
  168. *
  169. * @v peerblk PeerDist block download
  170. * @v pos Position within incoming data stream
  171. * @ret offset Offset within overall download
  172. */
  173. static inline __attribute__ (( always_inline )) size_t
  174. peerblk_offset ( struct peerdist_block *peerblk, size_t pos ) {
  175. return ( ( pos - peerblk->start ) + peerblk->offset );
  176. }
  177. /**
  178. * Deliver download attempt data block
  179. *
  180. * @v peerblk PeerDist block download
  181. * @v iobuf I/O buffer
  182. * @v meta Original data transfer metadata
  183. * @v pos Position within incoming data stream
  184. * @ret rc Return status code
  185. */
  186. static int peerblk_deliver ( struct peerdist_block *peerblk,
  187. struct io_buffer *iobuf,
  188. struct xfer_metadata *meta, size_t pos ) {
  189. struct xfer_metadata xfer_meta;
  190. size_t len = iob_len ( iobuf );
  191. size_t start = pos;
  192. size_t end = ( pos + len );
  193. int rc;
  194. /* Discard zero-length packets and packets which lie entirely
  195. * outside the trimmed range.
  196. */
  197. if ( ( start >= peerblk->end ) || ( end <= peerblk->start ) ||
  198. ( len == 0 ) ) {
  199. free_iob ( iobuf );
  200. return 0;
  201. }
  202. /* Truncate data to within trimmed range */
  203. if ( start < peerblk->start ) {
  204. iob_pull ( iobuf, ( peerblk->start - start ) );
  205. start = peerblk->start;
  206. }
  207. if ( end > peerblk->end ) {
  208. iob_unput ( iobuf, ( end - peerblk->end ) );
  209. end = peerblk->end;
  210. }
  211. /* Construct metadata */
  212. memcpy ( &xfer_meta, meta, sizeof ( xfer_meta ) );
  213. xfer_meta.flags |= XFER_FL_ABS_OFFSET;
  214. xfer_meta.offset = peerblk_offset ( peerblk, start );
  215. /* Deliver data */
  216. if ( ( rc = xfer_deliver ( &peerblk->xfer, iob_disown ( iobuf ),
  217. &xfer_meta ) ) != 0 ) {
  218. DBGC ( peerblk, "PEERBLK %p %d.%d could not deliver data: %s\n",
  219. peerblk, peerblk->segment, peerblk->block,
  220. strerror ( rc ) );
  221. return rc;
  222. }
  223. return 0;
  224. }
  225. /**
  226. * Finish PeerDist block download attempt
  227. *
  228. * @v peerblk PeerDist block download
  229. * @v rc Reason for close
  230. */
  231. static void peerblk_done ( struct peerdist_block *peerblk, int rc ) {
  232. struct digest_algorithm *digest = peerblk->digest;
  233. uint8_t hash[digest->digestsize];
  234. unsigned long now = peerblk_timestamp();
  235. /* Check for errors on completion */
  236. if ( rc != 0 ) {
  237. DBGC ( peerblk, "PEERBLK %p %d.%d attempt failed: %s\n",
  238. peerblk, peerblk->segment, peerblk->block,
  239. strerror ( rc ) );
  240. goto err;
  241. }
  242. /* Check digest */
  243. digest_final ( digest, peerblk->digestctx, hash );
  244. if ( memcmp ( hash, peerblk->hash, peerblk->digestsize ) != 0 ) {
  245. DBGC ( peerblk, "PEERBLK %p %d.%d digest mismatch:\n",
  246. peerblk, peerblk->segment, peerblk->block );
  247. DBGC_HDA ( peerblk, 0, hash, peerblk->digestsize );
  248. DBGC_HDA ( peerblk, 0, peerblk->hash, peerblk->digestsize );
  249. rc = -EIO;
  250. goto err;
  251. }
  252. /* Profile successful attempt */
  253. profile_custom ( &peerblk_attempt_success_profiler,
  254. ( now - peerblk->attempted ) );
  255. /* Close download */
  256. peerblk_close ( peerblk, 0 );
  257. return;
  258. err:
  259. /* Record failure reason and schedule a retry attempt */
  260. profile_custom ( &peerblk_attempt_failure_profiler,
  261. ( now - peerblk->attempted ) );
  262. peerblk_reset ( peerblk, rc );
  263. peerblk->rc = rc;
  264. start_timer_nodelay ( &peerblk->timer );
  265. }
  266. /******************************************************************************
  267. *
  268. * Raw block download attempts (using an HTTP range request)
  269. *
  270. ******************************************************************************
  271. */
  272. /**
  273. * Open PeerDist raw block download attempt
  274. *
  275. * @v peerblk PeerDist block download
  276. * @ret rc Return status code
  277. */
  278. static int peerblk_raw_open ( struct peerdist_block *peerblk ) {
  279. struct http_request_range range;
  280. int rc;
  281. DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting raw range request\n",
  282. peerblk, peerblk->segment, peerblk->block );
  283. /* Construct HTTP range */
  284. memset ( &range, 0, sizeof ( range ) );
  285. range.start = peerblk->range.start;
  286. range.len = ( peerblk->range.end - peerblk->range.start );
  287. /* Initiate range request to retrieve block */
  288. if ( ( rc = http_open ( &peerblk->raw, &http_get, peerblk->uri,
  289. &range, NULL ) ) != 0 ) {
  290. DBGC ( peerblk, "PEERBLK %p %d.%d could not create range "
  291. "request: %s\n", peerblk, peerblk->segment,
  292. peerblk->block, strerror ( rc ) );
  293. return rc;
  294. }
  295. /* Annul HTTP connection (for testing) if applicable. Do not
  296. * report as an immediate error, in order to test our ability
  297. * to recover from a totally unresponsive HTTP server.
  298. */
  299. if ( inject_fault ( PEERBLK_ANNUL_RATE ) )
  300. intf_restart ( &peerblk->raw, 0 );
  301. return 0;
  302. }
  303. /**
  304. * Receive PeerDist raw data
  305. *
  306. * @v peerblk PeerDist block download
  307. * @v iobuf I/O buffer
  308. * @v meta Data transfer metadata
  309. * @ret rc Return status code
  310. */
  311. static int peerblk_raw_rx ( struct peerdist_block *peerblk,
  312. struct io_buffer *iobuf,
  313. struct xfer_metadata *meta ) {
  314. size_t len = iob_len ( iobuf );
  315. size_t pos = peerblk->pos;
  316. size_t mid = ( ( peerblk->range.end - peerblk->range.start ) / 2 );
  317. int rc;
  318. /* Corrupt received data (for testing) if applicable */
  319. inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len );
  320. /* Fail if data is delivered out of order, since the streaming
  321. * digest requires strict ordering.
  322. */
  323. if ( ( rc = xfer_check_order ( meta, &peerblk->pos, len ) ) != 0 )
  324. goto err;
  325. /* Add data to digest */
  326. digest_update ( peerblk->digest, peerblk->digestctx, iobuf->data, len );
  327. /* Deliver data */
  328. if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta,
  329. pos ) ) != 0 )
  330. goto err;
  331. /* Extend download attempt timer */
  332. start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_RX_TIMEOUT );
  333. /* Stall download attempt (for testing) if applicable */
  334. if ( ( pos < mid ) && ( ( pos + len ) >= mid ) &&
  335. ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) {
  336. intf_restart ( &peerblk->raw, rc );
  337. }
  338. return 0;
  339. err:
  340. free_iob ( iobuf );
  341. peerblk_done ( peerblk, rc );
  342. return rc;
  343. }
  344. /**
  345. * Close PeerDist raw block download attempt
  346. *
  347. * @v peerblk PeerDist block download
  348. * @v rc Reason for close
  349. */
  350. static void peerblk_raw_close ( struct peerdist_block *peerblk, int rc ) {
  351. /* Restart interface */
  352. intf_restart ( &peerblk->raw, rc );
  353. /* Fail immediately if we have an error */
  354. if ( rc != 0 )
  355. goto done;
  356. /* Abort download attempt (for testing) if applicable */
  357. if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
  358. goto done;
  359. done:
  360. /* Complete download attempt */
  361. peerblk_done ( peerblk, rc );
  362. }
  363. /******************************************************************************
  364. *
  365. * Retrieval protocol block download attempts (using HTTP POST)
  366. *
  367. ******************************************************************************
  368. */
  369. /**
  370. * Construct PeerDist retrieval protocol URI
  371. *
  372. * @v location Peer location
  373. * @ret uri Retrieval URI, or NULL on error
  374. */
  375. static struct uri * peerblk_retrieval_uri ( const char *location ) {
  376. char uri_string[ 7 /* "http://" */ + strlen ( location ) +
  377. sizeof ( PEERDIST_MAGIC_PATH /* includes NUL */ ) ];
  378. /* Construct URI string */
  379. snprintf ( uri_string, sizeof ( uri_string ),
  380. ( "http://%s" PEERDIST_MAGIC_PATH ), location );
  381. /* Parse URI string */
  382. return parse_uri ( uri_string );
  383. }
  384. /**
  385. * Open PeerDist retrieval protocol block download attempt
  386. *
  387. * @v peerblk PeerDist block download
  388. * @v location Peer location
  389. * @ret rc Return status code
  390. */
  391. static int peerblk_retrieval_open ( struct peerdist_block *peerblk,
  392. const char *location ) {
  393. size_t digestsize = peerblk->digestsize;
  394. peerdist_msg_getblks_t ( digestsize, 1, 0 ) req;
  395. peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *rsp;
  396. struct http_request_content content;
  397. struct uri *uri;
  398. int rc;
  399. DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting retrieval from %s\n",
  400. peerblk, peerblk->segment, peerblk->block, location );
  401. /* Construct block fetch request */
  402. memset ( &req, 0, sizeof ( req ) );
  403. req.getblks.hdr.version.raw = htonl ( PEERDIST_MSG_GETBLKS_VERSION );
  404. req.getblks.hdr.type = htonl ( PEERDIST_MSG_GETBLKS_TYPE );
  405. req.getblks.hdr.len = htonl ( sizeof ( req ) );
  406. req.getblks.hdr.algorithm = htonl ( PEERDIST_MSG_AES_128_CBC );
  407. req.segment.segment.digestsize = htonl ( digestsize );
  408. memcpy ( req.segment.id, peerblk->id, digestsize );
  409. req.ranges.ranges.count = htonl ( 1 );
  410. req.ranges.range[0].first = htonl ( peerblk->block );
  411. req.ranges.range[0].count = htonl ( 1 );
  412. /* Construct POST request content */
  413. memset ( &content, 0, sizeof ( content ) );
  414. content.data = &req;
  415. content.len = sizeof ( req );
  416. /* Construct URI */
  417. if ( ( uri = peerblk_retrieval_uri ( location ) ) == NULL ) {
  418. rc = -ENOMEM;
  419. goto err_uri;
  420. }
  421. /* Update trim thresholds */
  422. peerblk->start += offsetof ( typeof ( *rsp ), msg.vrf );
  423. peerblk->end += offsetof ( typeof ( *rsp ), msg.vrf );
  424. /* Initiate HTTP POST to retrieve block */
  425. if ( ( rc = http_open ( &peerblk->retrieval, &http_post, uri,
  426. NULL, &content ) ) != 0 ) {
  427. DBGC ( peerblk, "PEERBLK %p %d.%d could not create retrieval "
  428. "request: %s\n", peerblk, peerblk->segment,
  429. peerblk->block, strerror ( rc ) );
  430. goto err_open;
  431. }
  432. /* Annul HTTP connection (for testing) if applicable. Do not
  433. * report as an immediate error, in order to test our ability
  434. * to recover from a totally unresponsive HTTP server.
  435. */
  436. if ( inject_fault ( PEERBLK_ANNUL_RATE ) )
  437. intf_restart ( &peerblk->retrieval, 0 );
  438. err_open:
  439. uri_put ( uri );
  440. err_uri:
  441. return rc;
  442. }
  443. /**
  444. * Receive PeerDist retrieval protocol data
  445. *
  446. * @v peerblk PeerDist block download
  447. * @v iobuf I/O buffer
  448. * @v meta Data transfer metadata
  449. * @ret rc Return status code
  450. */
  451. static int peerblk_retrieval_rx ( struct peerdist_block *peerblk,
  452. struct io_buffer *iobuf,
  453. struct xfer_metadata *meta ) {
  454. size_t len = iob_len ( iobuf );
  455. size_t start;
  456. size_t end;
  457. size_t before;
  458. size_t after;
  459. size_t cut;
  460. int rc;
  461. /* Some genius at Microsoft thought it would be a great idea
  462. * to place the AES-CBC initialisation vector *after* the
  463. * encrypted data, thereby making it logically impossible to
  464. * decrypt each packet as it arrives.
  465. *
  466. * To work around this mindless stupidity, we deliver the
  467. * ciphertext as-is and later use xfer_buffer() to obtain
  468. * access to the underlying data transfer buffer in order to
  469. * perform the decryption.
  470. *
  471. * There will be some data both before and after the bytes
  472. * corresponding to the trimmed plaintext: a MSG_BLK
  473. * header/footer, some block padding for the AES-CBC cipher,
  474. * and a possibly large quantity of unwanted ciphertext which
  475. * is excluded from the trimmed content range. We store this
  476. * data in a local data transfer buffer. If the amount of
  477. * data to be stored is too large, we will fail allocation and
  478. * so eventually fall back to using a range request (which
  479. * does not require this kind of temporary storage
  480. * allocation).
  481. */
  482. /* Corrupt received data (for testing) if applicable */
  483. inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len );
  484. /* Calculate start and end positions of this buffer */
  485. start = peerblk->pos;
  486. if ( meta->flags & XFER_FL_ABS_OFFSET )
  487. start = 0;
  488. start += meta->offset;
  489. end = ( start + len );
  490. /* Buffer any data before the trimmed content */
  491. if ( ( start < peerblk->start ) && ( len > 0 ) ) {
  492. /* Calculate length of data before the trimmed content */
  493. before = ( peerblk->start - start );
  494. if ( before > len )
  495. before = len;
  496. /* Buffer data before the trimmed content */
  497. if ( ( rc = xferbuf_write ( &peerblk->buffer, start,
  498. iobuf->data, before ) ) != 0 ) {
  499. DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer "
  500. "data: %s\n", peerblk, peerblk->segment,
  501. peerblk->block, strerror ( rc ) );
  502. goto err;
  503. }
  504. }
  505. /* Buffer any data after the trimmed content */
  506. if ( ( end > peerblk->end ) && ( len > 0 ) ) {
  507. /* Calculate length of data after the trimmed content */
  508. after = ( end - peerblk->end );
  509. if ( after > len )
  510. after = len;
  511. /* Buffer data after the trimmed content */
  512. cut = ( peerblk->end - peerblk->start );
  513. if ( ( rc = xferbuf_write ( &peerblk->buffer,
  514. ( end - after - cut ),
  515. ( iobuf->data + len - after ),
  516. after ) ) != 0 ) {
  517. DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer "
  518. "data: %s\n", peerblk, peerblk->segment,
  519. peerblk->block, strerror ( rc ) );
  520. goto err;
  521. }
  522. }
  523. /* Deliver any remaining data */
  524. if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta,
  525. start ) ) != 0 )
  526. goto err;
  527. /* Update position */
  528. peerblk->pos = end;
  529. /* Extend download attempt timer */
  530. start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_RX_TIMEOUT );
  531. /* Stall download attempt (for testing) if applicable */
  532. if ( ( start < peerblk->end ) && ( end >= peerblk->end ) &&
  533. ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) {
  534. intf_restart ( &peerblk->retrieval, rc );
  535. }
  536. return 0;
  537. err:
  538. free_iob ( iobuf );
  539. peerblk_done ( peerblk, rc );
  540. return rc;
  541. }
  542. /**
  543. * Parse retrieval protocol message header
  544. *
  545. * @v peerblk PeerDist block download
  546. * @ret rc Return status code
  547. */
  548. static int peerblk_parse_header ( struct peerdist_block *peerblk ) {
  549. struct {
  550. struct peerdist_msg_transport_header hdr;
  551. struct peerdist_msg_header msg;
  552. } __attribute__ (( packed )) *msg = peerblk->buffer.data;
  553. struct cipher_algorithm *cipher;
  554. size_t len = peerblk->buffer.len;
  555. size_t keylen = 0;
  556. int rc;
  557. /* Check message length */
  558. if ( len < sizeof ( *msg ) ) {
  559. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for header "
  560. "(%zd bytes)\n", peerblk, peerblk->segment,
  561. peerblk->block, len );
  562. return -ERANGE;
  563. }
  564. /* Check message type */
  565. if ( msg->msg.type != htonl ( PEERDIST_MSG_BLK_TYPE ) ) {
  566. DBGC ( peerblk, "PEERBLK %p %d.%d unexpected message type "
  567. "%#08x\n", peerblk, peerblk->segment, peerblk->block,
  568. ntohl ( msg->msg.type ) );
  569. return -EPROTO;
  570. }
  571. /* Determine cipher algorithm and key length */
  572. cipher = &aes_cbc_algorithm;
  573. switch ( msg->msg.algorithm ) {
  574. case htonl ( PEERDIST_MSG_PLAINTEXT ) :
  575. cipher = NULL;
  576. break;
  577. case htonl ( PEERDIST_MSG_AES_128_CBC ) :
  578. keylen = ( 128 / 8 );
  579. break;
  580. case htonl ( PEERDIST_MSG_AES_192_CBC ) :
  581. keylen = ( 192 / 8 );
  582. break;
  583. case htonl ( PEERDIST_MSG_AES_256_CBC ) :
  584. keylen = ( 256 / 8 );
  585. break;
  586. default:
  587. DBGC ( peerblk, "PEERBLK %p %d.%d unrecognised algorithm "
  588. "%#08x\n", peerblk, peerblk->segment, peerblk->block,
  589. ntohl ( msg->msg.algorithm ) );
  590. return -ENOTSUP;
  591. }
  592. DBGC2 ( peerblk, "PEERBLK %p %d.%d using %s with %zd-bit key\n",
  593. peerblk, peerblk->segment, peerblk->block,
  594. ( cipher ? cipher->name : "plaintext" ), ( 8 * keylen ) );
  595. /* Sanity check key length against maximum secret length */
  596. if ( keylen > peerblk->digestsize ) {
  597. DBGC ( peerblk, "PEERBLK %p %d.%d %zd-byte secret too short "
  598. "for %zd-bit key\n", peerblk, peerblk->segment,
  599. peerblk->block, peerblk->digestsize, ( 8 * keylen ) );
  600. return -EPROTO;
  601. }
  602. /* Allocate cipher context, if applicable. Freeing the cipher
  603. * context (on error or otherwise) is handled by peerblk_reset().
  604. */
  605. peerblk->cipher = cipher;
  606. assert ( peerblk->cipherctx == NULL );
  607. if ( cipher ) {
  608. peerblk->cipherctx = malloc ( cipher->ctxsize );
  609. if ( ! peerblk->cipherctx )
  610. return -ENOMEM;
  611. }
  612. /* Initialise cipher, if applicable */
  613. if ( cipher &&
  614. ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret,
  615. keylen ) ) != 0 ) {
  616. DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n",
  617. peerblk, peerblk->segment, peerblk->block,
  618. strerror ( rc ) );
  619. return rc;
  620. }
  621. return 0;
  622. }
  623. /**
  624. * Parse retrieval protocol message segment and block details
  625. *
  626. * @v peerblk PeerDist block download
  627. * @v buf_len Length of buffered data to fill in
  628. * @ret rc Return status code
  629. */
  630. static int peerblk_parse_block ( struct peerdist_block *peerblk,
  631. size_t *buf_len ) {
  632. size_t digestsize = peerblk->digestsize;
  633. peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data;
  634. size_t len = peerblk->buffer.len;
  635. size_t data_len;
  636. size_t total;
  637. /* Check message length */
  638. if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) {
  639. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
  640. "zero-length data (%zd bytes)\n", peerblk,
  641. peerblk->segment, peerblk->block, len );
  642. return -ERANGE;
  643. }
  644. /* Check digest size */
  645. if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) {
  646. DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n",
  647. peerblk, peerblk->segment, peerblk->block,
  648. ntohl ( msg->msg.segment.segment.digestsize ) );
  649. return -EPROTO;
  650. }
  651. /* Check segment ID */
  652. if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) {
  653. DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n",
  654. peerblk, peerblk->segment, peerblk->block );
  655. return -EPROTO;
  656. }
  657. /* Check block ID */
  658. if ( ntohl ( msg->msg.index ) != peerblk->block ) {
  659. DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n",
  660. peerblk, peerblk->segment, peerblk->block,
  661. ntohl ( msg->msg.index ) );
  662. return -EPROTO;
  663. }
  664. /* Check for missing blocks */
  665. data_len = be32_to_cpu ( msg->msg.block.block.len );
  666. if ( ! data_len ) {
  667. DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n",
  668. peerblk, peerblk->segment, peerblk->block );
  669. return -ENOENT;
  670. }
  671. /* Check for underlength blocks */
  672. if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) {
  673. DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd "
  674. "bytes)\n", peerblk, peerblk->segment, peerblk->block,
  675. data_len );
  676. return -ERANGE;
  677. }
  678. /* Calculate buffered data length (i.e. excluding data which
  679. * was delivered to the final data transfer buffer).
  680. */
  681. *buf_len = ( data_len - ( peerblk->end - peerblk->start ) );
  682. /* Describe data before the trimmed content */
  683. peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer;
  684. peerblk->decrypt[PEERBLK_BEFORE].offset =
  685. offsetof ( typeof ( *msg ), msg.block.data );
  686. peerblk->decrypt[PEERBLK_BEFORE].len =
  687. ( peerblk->start -
  688. offsetof ( typeof ( *msg ), msg.block.data ) );
  689. total = peerblk->decrypt[PEERBLK_BEFORE].len;
  690. /* Describe data within the trimmed content */
  691. peerblk->decrypt[PEERBLK_DURING].offset =
  692. peerblk_offset ( peerblk, peerblk->start );
  693. peerblk->decrypt[PEERBLK_DURING].len =
  694. ( peerblk->end - peerblk->start );
  695. total += peerblk->decrypt[PEERBLK_DURING].len;
  696. /* Describe data after the trimmed content */
  697. peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer;
  698. peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start;
  699. peerblk->decrypt[PEERBLK_AFTER].len =
  700. ( offsetof ( typeof ( *msg ), msg.block.data )
  701. + *buf_len - peerblk->start );
  702. total += peerblk->decrypt[PEERBLK_AFTER].len;
  703. /* Sanity check */
  704. assert ( total == be32_to_cpu ( msg->msg.block.block.len ) );
  705. /* Initialise cipher and digest lengths */
  706. peerblk->cipher_remaining = total;
  707. peerblk->digest_remaining =
  708. ( peerblk->range.end - peerblk->range.start );
  709. assert ( peerblk->cipher_remaining >= peerblk->digest_remaining );
  710. return 0;
  711. }
  712. /**
  713. * Parse retrieval protocol message useless details
  714. *
  715. * @v peerblk PeerDist block download
  716. * @v buf_len Length of buffered data
  717. * @v vrf_len Length of uselessness to fill in
  718. * @ret rc Return status code
  719. */
  720. static int peerblk_parse_useless ( struct peerdist_block *peerblk,
  721. size_t buf_len, size_t *vrf_len ) {
  722. size_t digestsize = peerblk->digestsize;
  723. peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg =
  724. peerblk->buffer.data;
  725. size_t len = peerblk->buffer.len;
  726. /* Check message length */
  727. if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) {
  728. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
  729. "zero-length uselessness (%zd bytes)\n", peerblk,
  730. peerblk->segment, peerblk->block, len );
  731. return -ERANGE;
  732. }
  733. /* Extract length of uselessness */
  734. *vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len );
  735. return 0;
  736. }
  737. /**
  738. * Parse retrieval protocol message initialisation vector details
  739. *
  740. * @v peerblk PeerDist block download
  741. * @v buf_len Length of buffered data
  742. * @v vrf_len Length of uselessness
  743. * @ret rc Return status code
  744. */
  745. static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len,
  746. size_t vrf_len ) {
  747. size_t digestsize = peerblk->digestsize;
  748. size_t blksize = peerblk->cipher->blocksize;
  749. peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg =
  750. peerblk->buffer.data;
  751. size_t len = peerblk->buffer.len;
  752. /* Check message length */
  753. if ( len < sizeof ( *msg ) ) {
  754. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
  755. "initialisation vector (%zd bytes)\n", peerblk,
  756. peerblk->segment, peerblk->block, len );
  757. return -ERANGE;
  758. }
  759. /* Check initialisation vector size */
  760. if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) {
  761. DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n",
  762. peerblk, peerblk->segment, peerblk->block,
  763. ntohl ( msg->msg.iv.iv.blksize ) );
  764. return -EPROTO;
  765. }
  766. /* Set initialisation vector */
  767. cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data );
  768. return 0;
  769. }
  770. /**
  771. * Read from decryption buffers
  772. *
  773. * @v peerblk PeerDist block download
  774. * @v data Data buffer
  775. * @v len Length to read
  776. * @ret rc Return status code
  777. */
  778. static int peerblk_decrypt_read ( struct peerdist_block *peerblk,
  779. void *data, size_t len ) {
  780. struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
  781. size_t frag_len;
  782. int rc;
  783. /* Read from each decryption buffer in turn */
  784. for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
  785. /* Calculate length to use from this buffer */
  786. frag_len = decrypt->len;
  787. if ( frag_len > len )
  788. frag_len = len;
  789. if ( ! frag_len )
  790. continue;
  791. /* Read from this buffer */
  792. if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset,
  793. data, frag_len ) ) != 0 )
  794. return rc;
  795. }
  796. return 0;
  797. }
  798. /**
  799. * Write to decryption buffers and update offsets and lengths
  800. *
  801. * @v peerblk PeerDist block download
  802. * @v data Data buffer
  803. * @v len Length to read
  804. * @ret rc Return status code
  805. */
  806. static int peerblk_decrypt_write ( struct peerdist_block *peerblk,
  807. const void *data, size_t len ) {
  808. struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
  809. size_t frag_len;
  810. int rc;
  811. /* Write to each decryption buffer in turn */
  812. for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
  813. /* Calculate length to use from this buffer */
  814. frag_len = decrypt->len;
  815. if ( frag_len > len )
  816. frag_len = len;
  817. if ( ! frag_len )
  818. continue;
  819. /* Write to this buffer */
  820. if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset,
  821. data, frag_len ) ) != 0 )
  822. return rc;
  823. /* Update offset and length */
  824. decrypt->offset += frag_len;
  825. decrypt->len -= frag_len;
  826. }
  827. return 0;
  828. }
  829. /**
  830. * Decrypt one chunk of PeerDist retrieval protocol data
  831. *
  832. * @v peerblk PeerDist block download
  833. */
  834. static void peerblk_decrypt ( struct peerdist_block *peerblk ) {
  835. struct cipher_algorithm *cipher = peerblk->cipher;
  836. struct digest_algorithm *digest = peerblk->digest;
  837. struct xfer_buffer *xferbuf;
  838. size_t cipher_len;
  839. size_t digest_len;
  840. void *data;
  841. int rc;
  842. /* Sanity check */
  843. assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 );
  844. /* Get the underlying data transfer buffer */
  845. xferbuf = xfer_buffer ( &peerblk->xfer );
  846. if ( ! xferbuf ) {
  847. DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data "
  848. "transfer buffer\n", peerblk, peerblk->segment,
  849. peerblk->block );
  850. rc = -ENOTSUP;
  851. goto err_xfer_buffer;
  852. }
  853. peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf;
  854. /* Calculate cipher and digest lengths */
  855. cipher_len = PEERBLK_DECRYPT_CHUNKSIZE;
  856. if ( cipher_len > peerblk->cipher_remaining )
  857. cipher_len = peerblk->cipher_remaining;
  858. digest_len = cipher_len;
  859. if ( digest_len > peerblk->digest_remaining )
  860. digest_len = peerblk->digest_remaining;
  861. assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 );
  862. /* Allocate temporary data buffer */
  863. data = malloc ( cipher_len );
  864. if ( ! data ) {
  865. rc = -ENOMEM;
  866. goto err_alloc_data;
  867. }
  868. /* Read ciphertext */
  869. if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){
  870. DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: "
  871. "%s\n", peerblk, peerblk->segment, peerblk->block,
  872. strerror ( rc ) );
  873. goto err_read;
  874. }
  875. /* Decrypt data */
  876. cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len );
  877. /* Add data to digest */
  878. digest_update ( digest, peerblk->digestctx, data, digest_len );
  879. /* Write plaintext */
  880. if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){
  881. DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: "
  882. "%s\n", peerblk, peerblk->segment, peerblk->block,
  883. strerror ( rc ) );
  884. goto err_write;
  885. }
  886. /* Consume input */
  887. peerblk->cipher_remaining -= cipher_len;
  888. peerblk->digest_remaining -= digest_len;
  889. /* Free temporary data buffer */
  890. free ( data );
  891. /* Continue processing until all input is consumed */
  892. if ( peerblk->cipher_remaining )
  893. return;
  894. /* Complete download attempt */
  895. peerblk_done ( peerblk, 0 );
  896. return;
  897. err_write:
  898. err_read:
  899. free ( data );
  900. err_alloc_data:
  901. err_xfer_buffer:
  902. peerblk_done ( peerblk, rc );
  903. }
  904. /**
  905. * Close PeerDist retrieval protocol block download attempt
  906. *
  907. * @v peerblk PeerDist block download
  908. * @v rc Reason for close
  909. */
  910. static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) {
  911. size_t buf_len;
  912. size_t vrf_len;
  913. /* Restart interface */
  914. intf_restart ( &peerblk->retrieval, rc );
  915. /* Fail immediately if we have an error */
  916. if ( rc != 0 )
  917. goto done;
  918. /* Abort download attempt (for testing) if applicable */
  919. if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
  920. goto done;
  921. /* Parse message header */
  922. if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 )
  923. goto done;
  924. /* Parse message segment and block details */
  925. if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 )
  926. goto done;
  927. /* If the block was plaintext, then there is nothing more to do */
  928. if ( ! peerblk->cipher )
  929. goto done;
  930. /* Parse message useless details */
  931. if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0)
  932. goto done;
  933. /* Parse message initialisation vector details */
  934. if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 )
  935. goto done;
  936. /* Fail if decryption length is not aligned to the cipher block size */
  937. if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) {
  938. DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n",
  939. peerblk, peerblk->segment, peerblk->block,
  940. peerblk->cipher_remaining );
  941. rc = -EPROTO;
  942. goto done;
  943. }
  944. /* Stop the download attempt timer: there is no point in
  945. * timing out while decrypting.
  946. */
  947. stop_timer ( &peerblk->timer );
  948. /* Start decryption process */
  949. process_add ( &peerblk->process );
  950. return;
  951. done:
  952. /* Complete download attempt */
  953. peerblk_done ( peerblk, rc );
  954. }
  955. /******************************************************************************
  956. *
  957. * Retry policy
  958. *
  959. ******************************************************************************
  960. */
  961. /**
  962. * Handle PeerDist retry timer expiry
  963. *
  964. * @v timer Retry timer
  965. * @v over Failure indicator
  966. */
  967. static void peerblk_expired ( struct retry_timer *timer, int over __unused ) {
  968. struct peerdist_block *peerblk =
  969. container_of ( timer, struct peerdist_block, timer );
  970. struct peerdisc_segment *segment = peerblk->discovery.segment;
  971. struct peerdisc_peer *head;
  972. unsigned long now = peerblk_timestamp();
  973. const char *location;
  974. int rc;
  975. /* Profile discovery timeout, if applicable */
  976. if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) {
  977. profile_custom ( &peerblk_discovery_timeout_profiler,
  978. ( now - peerblk->started ) );
  979. DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after "
  980. "%ld ticks\n", peerblk, peerblk->segment,
  981. peerblk->block, timer->timeout );
  982. }
  983. /* Profile download timeout, if applicable */
  984. if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) {
  985. profile_custom ( &peerblk_attempt_timeout_profiler,
  986. ( now - peerblk->attempted ) );
  987. DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n",
  988. peerblk, peerblk->segment, peerblk->block,
  989. timer->timeout );
  990. }
  991. /* Abort any current download attempt */
  992. peerblk_reset ( peerblk, -ETIMEDOUT );
  993. /* Record attempt start time */
  994. peerblk->attempted = now;
  995. /* If we have exceeded our maximum number of attempt cycles
  996. * (each cycle comprising a retrieval protocol download from
  997. * each peer in the list followed by a raw download from the
  998. * origin server), then abort the overall download.
  999. */
  1000. head = list_entry ( &segment->peers, struct peerdisc_peer, list );
  1001. if ( ( peerblk->peer == head ) &&
  1002. ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) {
  1003. rc = peerblk->rc;
  1004. assert ( rc != 0 );
  1005. goto err;
  1006. }
  1007. /* If we have not yet made any download attempts, then move to
  1008. * the start of the peer list.
  1009. */
  1010. if ( peerblk->peer == NULL )
  1011. peerblk->peer = head;
  1012. /* Attempt retrieval protocol download from next usable peer */
  1013. list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) {
  1014. /* Attempt retrieval protocol download from this peer */
  1015. location = peerblk->peer->location;
  1016. if ( ( rc = peerblk_retrieval_open ( peerblk,
  1017. location ) ) != 0 ) {
  1018. /* Non-fatal: continue to try next peer */
  1019. continue;
  1020. }
  1021. /* Start download attempt timer */
  1022. peerblk->rc = -ETIMEDOUT;
  1023. start_timer_fixed ( &peerblk->timer,
  1024. PEERBLK_RETRIEVAL_OPEN_TIMEOUT );
  1025. return;
  1026. }
  1027. /* Attempt raw download */
  1028. if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 )
  1029. goto err;
  1030. /* Start download attempt timer */
  1031. peerblk->rc = -ETIMEDOUT;
  1032. start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT );
  1033. return;
  1034. err:
  1035. peerblk_close ( peerblk, rc );
  1036. }
  1037. /**
  1038. * Handle PeerDist peer discovery
  1039. *
  1040. * @v discovery PeerDist discovery client
  1041. */
  1042. static void peerblk_discovered ( struct peerdisc_client *discovery ) {
  1043. struct peerdist_block *peerblk =
  1044. container_of ( discovery, struct peerdist_block, discovery );
  1045. unsigned long now = peerblk_timestamp();
  1046. /* Do nothing unless we are still waiting for the initial
  1047. * discovery timeout.
  1048. */
  1049. if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) )
  1050. return;
  1051. /* Schedule an immediate retry */
  1052. start_timer_nodelay ( &peerblk->timer );
  1053. /* Profile discovery success */
  1054. profile_custom ( &peerblk_discovery_success_profiler,
  1055. ( now - peerblk->started ) );
  1056. }
  1057. /******************************************************************************
  1058. *
  1059. * Opener
  1060. *
  1061. ******************************************************************************
  1062. */
  1063. /** PeerDist block download data transfer interface operations */
  1064. static struct interface_operation peerblk_xfer_operations[] = {
  1065. INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ),
  1066. };
  1067. /** PeerDist block download data transfer interface descriptor */
  1068. static struct interface_descriptor peerblk_xfer_desc =
  1069. INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations );
  1070. /** PeerDist block download raw data interface operations */
  1071. static struct interface_operation peerblk_raw_operations[] = {
  1072. INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ),
  1073. INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ),
  1074. };
  1075. /** PeerDist block download raw data interface descriptor */
  1076. static struct interface_descriptor peerblk_raw_desc =
  1077. INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations );
  1078. /** PeerDist block download retrieval protocol interface operations */
  1079. static struct interface_operation peerblk_retrieval_operations[] = {
  1080. INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ),
  1081. INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close),
  1082. };
  1083. /** PeerDist block download retrieval protocol interface descriptor */
  1084. static struct interface_descriptor peerblk_retrieval_desc =
  1085. INTF_DESC ( struct peerdist_block, retrieval,
  1086. peerblk_retrieval_operations );
  1087. /** PeerDist block download decryption process descriptor */
  1088. static struct process_descriptor peerblk_process_desc =
  1089. PROC_DESC ( struct peerdist_block, process, peerblk_decrypt );
  1090. /** PeerDist block download discovery operations */
  1091. static struct peerdisc_client_operations peerblk_discovery_operations = {
  1092. .discovered = peerblk_discovered,
  1093. };
  1094. /**
  1095. * Open PeerDist block download
  1096. *
  1097. * @v xfer Data transfer interface
  1098. * @v uri Original URI
  1099. * @v info Content information block
  1100. * @ret rc Return status code
  1101. */
  1102. int peerblk_open ( struct interface *xfer, struct uri *uri,
  1103. struct peerdist_info_block *block ) {
  1104. const struct peerdist_info_segment *segment = block->segment;
  1105. const struct peerdist_info *info = segment->info;
  1106. struct digest_algorithm *digest = info->digest;
  1107. struct peerdist_block *peerblk;
  1108. unsigned long timeout;
  1109. size_t digestsize;
  1110. int rc;
  1111. /* Allocate and initialise structure */
  1112. peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize );
  1113. if ( ! peerblk ) {
  1114. rc = -ENOMEM;
  1115. goto err_alloc;
  1116. }
  1117. ref_init ( &peerblk->refcnt, peerblk_free );
  1118. intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt );
  1119. intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt );
  1120. intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc,
  1121. &peerblk->refcnt );
  1122. peerblk->uri = uri_get ( uri );
  1123. memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) );
  1124. memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) );
  1125. peerblk->offset = ( block->trim.start - info->trim.start );
  1126. peerblk->digest = info->digest;
  1127. peerblk->digestsize = digestsize = info->digestsize;
  1128. peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) );
  1129. peerblk->segment = segment->index;
  1130. memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) );
  1131. memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) );
  1132. peerblk->block = block->index;
  1133. memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) );
  1134. xferbuf_malloc_init ( &peerblk->buffer );
  1135. process_init_stopped ( &peerblk->process, &peerblk_process_desc,
  1136. &peerblk->refcnt );
  1137. peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations );
  1138. timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt );
  1139. DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..."
  1140. "%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment,
  1141. peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2],
  1142. peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ],
  1143. peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ],
  1144. peerblk->range.start, peerblk->range.end );
  1145. if ( ( peerblk->trim.start != peerblk->range.start ) ||
  1146. ( peerblk->trim.end != peerblk->range.end ) ) {
  1147. DBGC2 ( peerblk, " covers [%08zx,%08zx)",
  1148. peerblk->trim.start, peerblk->trim.end );
  1149. }
  1150. DBGC2 ( peerblk, "\n" );
  1151. /* Open discovery */
  1152. if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id,
  1153. peerblk->digestsize ) ) != 0 )
  1154. goto err_open_discovery;
  1155. /* Schedule a retry attempt either immediately (if we already
  1156. * have some peers) or after the discovery timeout.
  1157. */
  1158. timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ?
  1159. ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 );
  1160. start_timer_fixed ( &peerblk->timer, timeout );
  1161. /* Record start time */
  1162. peerblk->started = peerblk_timestamp();
  1163. /* Attach to parent interface, mortalise self, and return */
  1164. intf_plug_plug ( xfer, &peerblk->xfer );
  1165. ref_put ( &peerblk->refcnt );
  1166. return 0;
  1167. err_open_discovery:
  1168. peerblk_close ( peerblk, rc );
  1169. err_alloc:
  1170. return rc;
  1171. }