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 44KB

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