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