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