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