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