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.

peerblk.c 40KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366
  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. Freeing the cipher context (on
  603. * error or otherwise) is handled by peerblk_reset().
  604. */
  605. peerblk->cipher = cipher;
  606. assert ( peerblk->cipherctx == NULL );
  607. peerblk->cipherctx = malloc ( cipher->ctxsize );
  608. if ( ! peerblk->cipherctx )
  609. return -ENOMEM;
  610. /* Initialise cipher */
  611. if ( ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret,
  612. keylen ) ) != 0 ) {
  613. DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n",
  614. peerblk, peerblk->segment, peerblk->block,
  615. strerror ( rc ) );
  616. return rc;
  617. }
  618. return 0;
  619. }
  620. /**
  621. * Parse retrieval protocol message segment and block details
  622. *
  623. * @v peerblk PeerDist block download
  624. * @v buf_len Length of buffered data to fill in
  625. * @ret rc Return status code
  626. */
  627. static int peerblk_parse_block ( struct peerdist_block *peerblk,
  628. size_t *buf_len ) {
  629. size_t digestsize = peerblk->digestsize;
  630. peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data;
  631. size_t len = peerblk->buffer.len;
  632. size_t data_len;
  633. size_t total;
  634. /* Check message length */
  635. if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) {
  636. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
  637. "zero-length data (%zd bytes)\n", peerblk,
  638. peerblk->segment, peerblk->block, len );
  639. return -ERANGE;
  640. }
  641. /* Check digest size */
  642. if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) {
  643. DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n",
  644. peerblk, peerblk->segment, peerblk->block,
  645. ntohl ( msg->msg.segment.segment.digestsize ) );
  646. return -EPROTO;
  647. }
  648. /* Check segment ID */
  649. if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) {
  650. DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n",
  651. peerblk, peerblk->segment, peerblk->block );
  652. return -EPROTO;
  653. }
  654. /* Check block ID */
  655. if ( ntohl ( msg->msg.index ) != peerblk->block ) {
  656. DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n",
  657. peerblk, peerblk->segment, peerblk->block,
  658. ntohl ( msg->msg.index ) );
  659. return -EPROTO;
  660. }
  661. /* Check for missing blocks */
  662. data_len = be32_to_cpu ( msg->msg.block.block.len );
  663. if ( ! data_len ) {
  664. DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n",
  665. peerblk, peerblk->segment, peerblk->block );
  666. return -ENOENT;
  667. }
  668. /* Check for underlength blocks */
  669. if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) {
  670. DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd "
  671. "bytes)\n", peerblk, peerblk->segment, peerblk->block,
  672. data_len );
  673. return -ERANGE;
  674. }
  675. /* Calculate buffered data length (i.e. excluding data which
  676. * was delivered to the final data transfer buffer).
  677. */
  678. *buf_len = ( data_len - ( peerblk->end - peerblk->start ) );
  679. /* Describe data before the trimmed content */
  680. peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer;
  681. peerblk->decrypt[PEERBLK_BEFORE].offset =
  682. offsetof ( typeof ( *msg ), msg.block.data );
  683. peerblk->decrypt[PEERBLK_BEFORE].len =
  684. ( peerblk->start -
  685. offsetof ( typeof ( *msg ), msg.block.data ) );
  686. total = peerblk->decrypt[PEERBLK_BEFORE].len;
  687. /* Describe data within the trimmed content */
  688. peerblk->decrypt[PEERBLK_DURING].offset =
  689. peerblk_offset ( peerblk, peerblk->start );
  690. peerblk->decrypt[PEERBLK_DURING].len =
  691. ( peerblk->end - peerblk->start );
  692. total += peerblk->decrypt[PEERBLK_DURING].len;
  693. /* Describe data after the trimmed content */
  694. peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer;
  695. peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start;
  696. peerblk->decrypt[PEERBLK_AFTER].len =
  697. ( offsetof ( typeof ( *msg ), msg.block.data )
  698. + *buf_len - peerblk->start );
  699. total += peerblk->decrypt[PEERBLK_AFTER].len;
  700. /* Sanity check */
  701. assert ( total == be32_to_cpu ( msg->msg.block.block.len ) );
  702. /* Initialise cipher and digest lengths */
  703. peerblk->cipher_remaining = total;
  704. peerblk->digest_remaining =
  705. ( peerblk->range.end - peerblk->range.start );
  706. assert ( peerblk->cipher_remaining >= peerblk->digest_remaining );
  707. return 0;
  708. }
  709. /**
  710. * Parse retrieval protocol message useless details
  711. *
  712. * @v peerblk PeerDist block download
  713. * @v buf_len Length of buffered data
  714. * @v vrf_len Length of uselessness to fill in
  715. * @ret rc Return status code
  716. */
  717. static int peerblk_parse_useless ( struct peerdist_block *peerblk,
  718. size_t buf_len, size_t *vrf_len ) {
  719. size_t digestsize = peerblk->digestsize;
  720. peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg =
  721. peerblk->buffer.data;
  722. size_t len = peerblk->buffer.len;
  723. /* Check message length */
  724. if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) {
  725. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
  726. "zero-length uselessness (%zd bytes)\n", peerblk,
  727. peerblk->segment, peerblk->block, len );
  728. return -ERANGE;
  729. }
  730. /* Extract length of uselessness */
  731. *vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len );
  732. return 0;
  733. }
  734. /**
  735. * Parse retrieval protocol message initialisation vector details
  736. *
  737. * @v peerblk PeerDist block download
  738. * @v buf_len Length of buffered data
  739. * @v vrf_len Length of uselessness
  740. * @ret rc Return status code
  741. */
  742. static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len,
  743. size_t vrf_len ) {
  744. size_t digestsize = peerblk->digestsize;
  745. size_t blksize = peerblk->cipher->blocksize;
  746. peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg =
  747. peerblk->buffer.data;
  748. size_t len = peerblk->buffer.len;
  749. /* Check message length */
  750. if ( len < sizeof ( *msg ) ) {
  751. DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
  752. "initialisation vector (%zd bytes)\n", peerblk,
  753. peerblk->segment, peerblk->block, len );
  754. return -ERANGE;
  755. }
  756. /* Check initialisation vector size */
  757. if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) {
  758. DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n",
  759. peerblk, peerblk->segment, peerblk->block,
  760. ntohl ( msg->msg.iv.iv.blksize ) );
  761. return -EPROTO;
  762. }
  763. /* Set initialisation vector */
  764. cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data );
  765. return 0;
  766. }
  767. /**
  768. * Read from decryption buffers
  769. *
  770. * @v peerblk PeerDist block download
  771. * @v data Data buffer
  772. * @v len Length to read
  773. * @ret rc Return status code
  774. */
  775. static int peerblk_decrypt_read ( struct peerdist_block *peerblk,
  776. void *data, size_t len ) {
  777. struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
  778. size_t frag_len;
  779. int rc;
  780. /* Read from each decryption buffer in turn */
  781. for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
  782. /* Calculate length to use from this buffer */
  783. frag_len = decrypt->len;
  784. if ( frag_len > len )
  785. frag_len = len;
  786. if ( ! frag_len )
  787. continue;
  788. /* Read from this buffer */
  789. if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset,
  790. data, frag_len ) ) != 0 )
  791. return rc;
  792. }
  793. return 0;
  794. }
  795. /**
  796. * Write to decryption buffers and update offsets and lengths
  797. *
  798. * @v peerblk PeerDist block download
  799. * @v data Data buffer
  800. * @v len Length to read
  801. * @ret rc Return status code
  802. */
  803. static int peerblk_decrypt_write ( struct peerdist_block *peerblk,
  804. const void *data, size_t len ) {
  805. struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
  806. size_t frag_len;
  807. int rc;
  808. /* Write to each decryption buffer in turn */
  809. for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
  810. /* Calculate length to use from this buffer */
  811. frag_len = decrypt->len;
  812. if ( frag_len > len )
  813. frag_len = len;
  814. if ( ! frag_len )
  815. continue;
  816. /* Write to this buffer */
  817. if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset,
  818. data, frag_len ) ) != 0 )
  819. return rc;
  820. /* Update offset and length */
  821. decrypt->offset += frag_len;
  822. decrypt->len -= frag_len;
  823. }
  824. return 0;
  825. }
  826. /**
  827. * Decrypt one chunk of PeerDist retrieval protocol data
  828. *
  829. * @v peerblk PeerDist block download
  830. */
  831. static void peerblk_decrypt ( struct peerdist_block *peerblk ) {
  832. struct cipher_algorithm *cipher = peerblk->cipher;
  833. struct digest_algorithm *digest = peerblk->digest;
  834. struct xfer_buffer *xferbuf;
  835. size_t cipher_len;
  836. size_t digest_len;
  837. void *data;
  838. int rc;
  839. /* Sanity check */
  840. assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 );
  841. /* Get the underlying data transfer buffer */
  842. xferbuf = xfer_buffer ( &peerblk->xfer );
  843. if ( ! xferbuf ) {
  844. DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data "
  845. "transfer buffer\n", peerblk, peerblk->segment,
  846. peerblk->block );
  847. rc = -ENOTSUP;
  848. goto err_xfer_buffer;
  849. }
  850. peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf;
  851. /* Calculate cipher and digest lengths */
  852. cipher_len = PEERBLK_DECRYPT_CHUNKSIZE;
  853. if ( cipher_len > peerblk->cipher_remaining )
  854. cipher_len = peerblk->cipher_remaining;
  855. digest_len = cipher_len;
  856. if ( digest_len > peerblk->digest_remaining )
  857. digest_len = peerblk->digest_remaining;
  858. assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 );
  859. /* Allocate temporary data buffer */
  860. data = malloc ( cipher_len );
  861. if ( ! data ) {
  862. rc = -ENOMEM;
  863. goto err_alloc_data;
  864. }
  865. /* Read ciphertext */
  866. if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){
  867. DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: "
  868. "%s\n", peerblk, peerblk->segment, peerblk->block,
  869. strerror ( rc ) );
  870. goto err_read;
  871. }
  872. /* Decrypt data */
  873. cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len );
  874. /* Add data to digest */
  875. digest_update ( digest, peerblk->digestctx, data, digest_len );
  876. /* Write plaintext */
  877. if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){
  878. DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: "
  879. "%s\n", peerblk, peerblk->segment, peerblk->block,
  880. strerror ( rc ) );
  881. goto err_write;
  882. }
  883. /* Consume input */
  884. peerblk->cipher_remaining -= cipher_len;
  885. peerblk->digest_remaining -= digest_len;
  886. /* Free temporary data buffer */
  887. free ( data );
  888. /* Continue processing until all input is consumed */
  889. if ( peerblk->cipher_remaining )
  890. return;
  891. /* Complete download attempt */
  892. peerblk_done ( peerblk, 0 );
  893. return;
  894. err_write:
  895. err_read:
  896. free ( data );
  897. err_alloc_data:
  898. err_xfer_buffer:
  899. peerblk_done ( peerblk, rc );
  900. }
  901. /**
  902. * Close PeerDist retrieval protocol block download attempt
  903. *
  904. * @v peerblk PeerDist block download
  905. * @v rc Reason for close
  906. */
  907. static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) {
  908. size_t buf_len;
  909. size_t vrf_len;
  910. /* Restart interface */
  911. intf_restart ( &peerblk->retrieval, rc );
  912. /* Fail immediately if we have an error */
  913. if ( rc != 0 )
  914. goto done;
  915. /* Abort download attempt (for testing) if applicable */
  916. if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
  917. goto done;
  918. /* Parse message header */
  919. if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 )
  920. goto done;
  921. /* Parse message segment and block details */
  922. if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 )
  923. goto done;
  924. /* If the block was plaintext, then there is nothing more to do */
  925. if ( ! peerblk->cipher )
  926. goto done;
  927. /* Parse message useless details */
  928. if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0)
  929. goto done;
  930. /* Parse message initialisation vector details */
  931. if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 )
  932. goto done;
  933. /* Fail if decryption length is not aligned to the cipher block size */
  934. if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) {
  935. DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n",
  936. peerblk, peerblk->segment, peerblk->block,
  937. peerblk->cipher_remaining );
  938. rc = -EPROTO;
  939. goto done;
  940. }
  941. /* Stop the download attempt timer: there is no point in
  942. * timing out while decrypting.
  943. */
  944. stop_timer ( &peerblk->timer );
  945. /* Start decryption process */
  946. process_add ( &peerblk->process );
  947. return;
  948. done:
  949. /* Complete download attempt */
  950. peerblk_done ( peerblk, rc );
  951. }
  952. /******************************************************************************
  953. *
  954. * Retry policy
  955. *
  956. ******************************************************************************
  957. */
  958. /**
  959. * Handle PeerDist retry timer expiry
  960. *
  961. * @v timer Retry timer
  962. * @v over Failure indicator
  963. */
  964. static void peerblk_expired ( struct retry_timer *timer, int over __unused ) {
  965. struct peerdist_block *peerblk =
  966. container_of ( timer, struct peerdist_block, timer );
  967. struct peerdisc_segment *segment = peerblk->discovery.segment;
  968. struct peerdisc_peer *head;
  969. unsigned long now = peerblk_timestamp();
  970. const char *location;
  971. int rc;
  972. /* Profile discovery timeout, if applicable */
  973. if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) {
  974. profile_custom ( &peerblk_discovery_timeout_profiler,
  975. ( now - peerblk->started ) );
  976. DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after "
  977. "%ld ticks\n", peerblk, peerblk->segment,
  978. peerblk->block, timer->timeout );
  979. }
  980. /* Profile download timeout, if applicable */
  981. if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) {
  982. profile_custom ( &peerblk_attempt_timeout_profiler,
  983. ( now - peerblk->attempted ) );
  984. DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n",
  985. peerblk, peerblk->segment, peerblk->block,
  986. timer->timeout );
  987. }
  988. /* Abort any current download attempt */
  989. peerblk_reset ( peerblk, -ETIMEDOUT );
  990. /* Record attempt start time */
  991. peerblk->attempted = now;
  992. /* If we have exceeded our maximum number of attempt cycles
  993. * (each cycle comprising a retrieval protocol download from
  994. * each peer in the list followed by a raw download from the
  995. * origin server), then abort the overall download.
  996. */
  997. head = list_entry ( &segment->peers, struct peerdisc_peer, list );
  998. if ( ( peerblk->peer == head ) &&
  999. ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) {
  1000. rc = peerblk->rc;
  1001. assert ( rc != 0 );
  1002. goto err;
  1003. }
  1004. /* If we have not yet made any download attempts, then move to
  1005. * the start of the peer list.
  1006. */
  1007. if ( peerblk->peer == NULL )
  1008. peerblk->peer = head;
  1009. /* Attempt retrieval protocol download from next usable peer */
  1010. list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) {
  1011. /* Attempt retrieval protocol download from this peer */
  1012. location = peerblk->peer->location;
  1013. if ( ( rc = peerblk_retrieval_open ( peerblk,
  1014. location ) ) != 0 ) {
  1015. /* Non-fatal: continue to try next peer */
  1016. continue;
  1017. }
  1018. /* Start download attempt timer */
  1019. peerblk->rc = -ETIMEDOUT;
  1020. start_timer_fixed ( &peerblk->timer,
  1021. PEERBLK_RETRIEVAL_OPEN_TIMEOUT );
  1022. return;
  1023. }
  1024. /* Attempt raw download */
  1025. if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 )
  1026. goto err;
  1027. /* Start download attempt timer */
  1028. peerblk->rc = -ETIMEDOUT;
  1029. start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT );
  1030. return;
  1031. err:
  1032. peerblk_close ( peerblk, rc );
  1033. }
  1034. /**
  1035. * Handle PeerDist peer discovery
  1036. *
  1037. * @v discovery PeerDist discovery client
  1038. */
  1039. static void peerblk_discovered ( struct peerdisc_client *discovery ) {
  1040. struct peerdist_block *peerblk =
  1041. container_of ( discovery, struct peerdist_block, discovery );
  1042. unsigned long now = peerblk_timestamp();
  1043. /* Do nothing unless we are still waiting for the initial
  1044. * discovery timeout.
  1045. */
  1046. if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) )
  1047. return;
  1048. /* Schedule an immediate retry */
  1049. start_timer_nodelay ( &peerblk->timer );
  1050. /* Profile discovery success */
  1051. profile_custom ( &peerblk_discovery_success_profiler,
  1052. ( now - peerblk->started ) );
  1053. }
  1054. /******************************************************************************
  1055. *
  1056. * Opener
  1057. *
  1058. ******************************************************************************
  1059. */
  1060. /** PeerDist block download data transfer interface operations */
  1061. static struct interface_operation peerblk_xfer_operations[] = {
  1062. INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ),
  1063. };
  1064. /** PeerDist block download data transfer interface descriptor */
  1065. static struct interface_descriptor peerblk_xfer_desc =
  1066. INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations );
  1067. /** PeerDist block download raw data interface operations */
  1068. static struct interface_operation peerblk_raw_operations[] = {
  1069. INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ),
  1070. INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ),
  1071. };
  1072. /** PeerDist block download raw data interface descriptor */
  1073. static struct interface_descriptor peerblk_raw_desc =
  1074. INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations );
  1075. /** PeerDist block download retrieval protocol interface operations */
  1076. static struct interface_operation peerblk_retrieval_operations[] = {
  1077. INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ),
  1078. INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close),
  1079. };
  1080. /** PeerDist block download retrieval protocol interface descriptor */
  1081. static struct interface_descriptor peerblk_retrieval_desc =
  1082. INTF_DESC ( struct peerdist_block, retrieval,
  1083. peerblk_retrieval_operations );
  1084. /** PeerDist block download decryption process descriptor */
  1085. static struct process_descriptor peerblk_process_desc =
  1086. PROC_DESC ( struct peerdist_block, process, peerblk_decrypt );
  1087. /** PeerDist block download discovery operations */
  1088. static struct peerdisc_client_operations peerblk_discovery_operations = {
  1089. .discovered = peerblk_discovered,
  1090. };
  1091. /**
  1092. * Open PeerDist block download
  1093. *
  1094. * @v xfer Data transfer interface
  1095. * @v uri Original URI
  1096. * @v info Content information block
  1097. * @ret rc Return status code
  1098. */
  1099. int peerblk_open ( struct interface *xfer, struct uri *uri,
  1100. struct peerdist_info_block *block ) {
  1101. const struct peerdist_info_segment *segment = block->segment;
  1102. const struct peerdist_info *info = segment->info;
  1103. struct digest_algorithm *digest = info->digest;
  1104. struct peerdist_block *peerblk;
  1105. unsigned long timeout;
  1106. size_t digestsize;
  1107. int rc;
  1108. /* Allocate and initialise structure */
  1109. peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize );
  1110. if ( ! peerblk ) {
  1111. rc = -ENOMEM;
  1112. goto err_alloc;
  1113. }
  1114. ref_init ( &peerblk->refcnt, peerblk_free );
  1115. intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt );
  1116. intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt );
  1117. intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc,
  1118. &peerblk->refcnt );
  1119. peerblk->uri = uri_get ( uri );
  1120. memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) );
  1121. memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) );
  1122. peerblk->offset = ( block->trim.start - info->trim.start );
  1123. peerblk->digest = info->digest;
  1124. peerblk->digestsize = digestsize = info->digestsize;
  1125. peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) );
  1126. peerblk->segment = segment->index;
  1127. memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) );
  1128. memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) );
  1129. peerblk->block = block->index;
  1130. memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) );
  1131. xferbuf_malloc_init ( &peerblk->buffer );
  1132. process_init_stopped ( &peerblk->process, &peerblk_process_desc,
  1133. &peerblk->refcnt );
  1134. peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations );
  1135. timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt );
  1136. DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..."
  1137. "%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment,
  1138. peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2],
  1139. peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ],
  1140. peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ],
  1141. peerblk->range.start, peerblk->range.end );
  1142. if ( ( peerblk->trim.start != peerblk->range.start ) ||
  1143. ( peerblk->trim.end != peerblk->range.end ) ) {
  1144. DBGC2 ( peerblk, " covers [%08zx,%08zx)",
  1145. peerblk->trim.start, peerblk->trim.end );
  1146. }
  1147. DBGC2 ( peerblk, "\n" );
  1148. /* Open discovery */
  1149. if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id,
  1150. peerblk->digestsize ) ) != 0 )
  1151. goto err_open_discovery;
  1152. /* Schedule a retry attempt either immediately (if we already
  1153. * have some peers) or after the discovery timeout.
  1154. */
  1155. timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ?
  1156. ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 );
  1157. start_timer_fixed ( &peerblk->timer, timeout );
  1158. /* Record start time */
  1159. peerblk->started = peerblk_timestamp();
  1160. /* Attach to parent interface, mortalise self, and return */
  1161. intf_plug_plug ( xfer, &peerblk->xfer );
  1162. ref_put ( &peerblk->refcnt );
  1163. return 0;
  1164. err_open_discovery:
  1165. peerblk_close ( peerblk, rc );
  1166. err_alloc:
  1167. return rc;
  1168. }