[or-cvs] checkpoint: revamp relay cell packaging and handling

Roger Dingledine arma at seul.org
Tue Dec 23 07:45:34 UTC 2003


Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/home2/arma/work/onion/cvs/src/or

Modified Files:
	circuit.c command.c connection_edge.c connection_or.c or.h 
Log Message:
checkpoint: revamp relay cell packaging and handling
include the infrastructure for inserting padding cells when there's
a relay-recognized conflict, but it does not work currently.


Index: circuit.c
===================================================================
RCS file: /home/or/cvsroot/src/or/circuit.c,v
retrieving revision 1.126
retrieving revision 1.127
diff -u -d -r1.126 -r1.127
--- circuit.c	19 Dec 2003 19:55:02 -0000	1.126
+++ circuit.c	23 Dec 2003 07:45:31 -0000	1.127
@@ -311,7 +311,7 @@
 }
 
 /* update digest from the payload of cell. assign integrity part to cell. */
-void relay_set_digest(crypto_digest_env_t *digest, cell_t *cell) {
+static void relay_set_digest(crypto_digest_env_t *digest, cell_t *cell) {
   uint32_t integrity;
   relay_header_t rh;
 
@@ -326,7 +326,7 @@
 /* update digest from the payload of cell (with the integrity part set
  * to 0). If the integrity part is valid return 0, else return -1.
  */
-int relay_check_digest(crypto_digest_env_t *digest, cell_t *cell) {
+static int relay_check_digest(crypto_digest_env_t *digest, cell_t *cell) {
   uint32_t received_integrity, calculated_integrity;
   relay_header_t rh;
 
@@ -348,16 +348,40 @@
   return 0;
 }
 
-int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ,
-                               int cell_direction, crypt_path_t *layer_hint) {
+static int relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
+                                   int encrypt_mode) {
+  char out[CELL_PAYLOAD_SIZE]; /* 'in' must be this size too */
+  relay_header_t rh;
+
+  relay_header_unpack(&rh, in);
+  log_fn(LOG_INFO,"before crypt: %d",rh.recognized);
+  if(( encrypt_mode && crypto_cipher_encrypt(cipher, in, CELL_PAYLOAD_SIZE, out)) ||
+     (!encrypt_mode && crypto_cipher_decrypt(cipher, in, CELL_PAYLOAD_SIZE, out))) {
+    log_fn(LOG_WARN,"Error during crypt: %s", crypto_perror());
+    return -1;
+  }
+  memcpy(in,out,CELL_PAYLOAD_SIZE);
+  relay_header_unpack(&rh, in);
+  log_fn(LOG_INFO,"after crypt: %d",rh.recognized);
+  return 0;
+}
+
+/*
+receive a relay cell:
+  - crypt it (encrypt APward, decrypt at AP, decrypt exitward)
+  - check if recognized (if exitward)
+  - if recognized, check digest, find right conn, deliver to edge.
+  - else connection_or_write_cell_to_buf to the right conn
+*/
+int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
+                               int cell_direction) {
   connection_t *conn=NULL;
+  crypt_path_t *layer_hint=NULL;
   char recognized=0;
 
   assert(cell && circ);
   assert(cell_direction == CELL_DIRECTION_OUT || cell_direction == CELL_DIRECTION_IN);
 
-  log_fn(LOG_DEBUG,"direction %d, stream_id %d before crypt.", cell_direction, *(uint16_t*)(cell->payload+1));/*XXX*/
-
   if(relay_crypt(circ, cell->payload, cell_direction,
                  &layer_hint, &recognized) < 0) {
     log_fn(LOG_WARN,"relay crypt failed. Dropping connection.");
@@ -394,15 +418,17 @@
   }
 
   /* not recognized. pass it on. */
-  if(cell_direction == CELL_DIRECTION_OUT)
+  if(cell_direction == CELL_DIRECTION_OUT) {
+    cell->circ_id = circ->n_circ_id; /* switch it */
     conn = circ->n_conn;
-  else
+  } else {
+    cell->circ_id = circ->p_circ_id; /* switch it */
     conn = circ->p_conn;
+  }
 
   if(!conn) {
-    log_fn(LOG_INFO,"Didn't recognize cell (%d), but circ stops here! Dropping.",
-           *(int *)(cell->payload+1));
-    return 0;
+    log_fn(LOG_WARN,"Didn't recognize cell, but circ stops here! Dropping.");
+    return 0; /* XXX if this warning never triggers, then return -1 */
   }
 
   log_fn(LOG_DEBUG,"Passing on unrecognized cell.");
@@ -411,10 +437,10 @@
   return 0;
 }
 
+/* wrap this into receive_relay_cell one day */
 static int relay_crypt(circuit_t *circ, char *in, char cell_direction,
                        crypt_path_t **layer_hint, char *recognized) {
   crypt_path_t *thishop;
-  char out[CELL_PAYLOAD_SIZE]; /* 'in' must be this size too */
   relay_header_t rh;
 
   assert(circ && in && recognized);
@@ -431,16 +457,11 @@
       do { /* Remember: cpath is in forward order, that is, first hop first. */
         assert(thishop);
 
-        log_fn(LOG_DEBUG,"before decrypt: %d",*(uint16_t*)(in+1));
-        /* decrypt */
-        if(crypto_cipher_decrypt(thishop->b_crypto, in, CELL_PAYLOAD_SIZE, out)) {
-          log_fn(LOG_WARN,"Error performing onion decryption: %s", crypto_perror());
+        if(relay_crypt_one_payload(thishop->b_crypto, in, 0) < 0)
           return -1;
-        }
-        memcpy(in,out,CELL_PAYLOAD_SIZE);
-        log_fn(LOG_DEBUG,"after decrypt: %d",*(uint16_t*)(in+1));
 
-        if(*(uint16_t*)(in+1) == 0) { /* XXX */
+        relay_header_unpack(&rh, in);
+        if(rh.recognized == 0) {
           *recognized = 1;
           *layer_hint = thishop;
           return 0;
@@ -448,59 +469,196 @@
 
         thishop = thishop->next;
       } while(thishop != circ->cpath && thishop->state == CPATH_STATE_OPEN);
-      log_fn(LOG_INFO,"in-cell at OP not recognized. Dropping.");
+      log_fn(LOG_WARN,"in-cell at OP not recognized. Dropping.");
       return 0;
     } else { /* we're in the middle. Just one crypt. */
-
-      log_fn(LOG_DEBUG,"before encrypt: %d",*(uint16_t*)(in+1));
-      if(crypto_cipher_encrypt(circ->p_crypto, in, CELL_PAYLOAD_SIZE, out)) {
-        log_fn(LOG_WARN,"Onion encryption failed for circID %u: %s",
-               circ->p_circ_id, crypto_perror());
+      if(relay_crypt_one_payload(circ->p_crypto, in, 1) < 0)
         return -1;
-      }
-      memcpy(in,out,CELL_PAYLOAD_SIZE);
-      log_fn(LOG_DEBUG,"after encrypt: %d",*(uint16_t*)(in+1));
-
       log_fn(LOG_DEBUG,"Skipping recognized check, because we're not the OP.");
-      /* don't check for recognized. only the OP can recognize a stream on the way back. */
     }
   } else /* cell_direction == CELL_DIRECTION_OUT */ {
-    if(circ->cpath) { /* we're at the beginning of the circuit. We'll want to do layered crypts. */
+    /* we're in the middle. Just one crypt. */
 
-      thishop = *layer_hint; /* we already know which layer, from when we package_raw_inbuf'ed */
-      /* moving from last to first hop */
-      do {
-        assert(thishop);
+    if(relay_crypt_one_payload(circ->n_crypto, in, 0) < 0)
+      return -1;
 
-        log_fn(LOG_DEBUG,"before encrypt: %d",*(uint16_t*)(in+1));
-        if(crypto_cipher_encrypt(thishop->f_crypto, in, CELL_PAYLOAD_SIZE, out)) {
-          log_fn(LOG_WARN,"Error performing encryption: %s", crypto_perror());
+    relay_header_unpack(&rh, in);
+    if (rh.recognized == 0 ||
+        (!circ->n_conn && rh.recognized == 11257)) {
+      /* as a special exception, hops at the end of the circuit
+       * recognize 01 as well. */
+      log_fn(LOG_INFO,"Recognized '%d'", rh.recognized);
+      *recognized = 1;
+      return 0;
+    }
+  }
+  return 0;
+}
+
+/* Helper function for circuit_package_relay_cell().
+ * crypt the cell from layer_hint backward. if there's a conflict,
+ * set *conflicthop to the nearest conflict, rewind all the ciphers,
+ * and restore the digest state.
+ *
+ * Conflicthop is set to the node whose cipher is going to output
+ * recognized=0. That is, the hop after conflicthop needs to receive
+ * the dummy cell.
+ */
+static int
+relay_try_all_crypts(cell_t *cell, circuit_t *circ, crypt_path_t *layer_hint,
+                     uint16_t recognized, crypt_path_t **conflicthop)
+{
+  crypt_path_t *thishop; /* counter for repeated crypts */
+  relay_header_t rh; /* to set/check 'recognized' */
+  crypto_digest_env_t *backup_digest=NULL;
+
+  backup_digest = crypto_digest_dup(layer_hint->f_digest);
+  relay_header_unpack(&rh, cell->payload);
+  rh.recognized = recognized;
+  relay_header_pack(cell->payload, &rh);
+  relay_set_digest(layer_hint->f_digest, cell);
+
+  *conflicthop = NULL;
+  thishop = layer_hint;
+
+  /* moving from farthest to nearest hop */
+  do {
+    assert(thishop);
+
+    log_fn(LOG_INFO,"crypting a layer of the relay cell.");
+    if(relay_crypt_one_payload(thishop->f_crypto, cell->payload, 1) < 0) {
+      crypto_free_digest_env(backup_digest);
+      return -1;
+    }
+
+    if(thishop != circ->cpath) {
+      /* if it's not the nearest hop, make sure it didn't accidentally
+       * encrypt recognized to 00. For the nearest hop it doesn't matter
+       * because it will be blindly decrypting whatever it gets. */
+      relay_header_unpack(&rh, cell->payload);
+//      if(rh.recognized == 0) {
+      if(rh.recognized < 10000) {
+        log_fn(LOG_INFO,"Found conflict");
+        *conflicthop = thishop;
+      }
+    }
+    thishop = thishop->prev;
+  } while (thishop != circ->cpath->prev);
+
+  if(*conflicthop) {
+    /* 1. Restore the old digest state */
+    crypto_digest_assign(layer_hint->f_digest, backup_digest);
+
+    /* 2. Restore old state for the ciphers that changed. */
+    thishop = layer_hint;
+    do {
+      log_fn(LOG_INFO,"Rewinding a cipher layer...");
+      crypto_cipher_rewind(thishop->f_crypto, CELL_PAYLOAD_SIZE);
+      thishop = thishop->prev;
+    } while (thishop != circ->cpath->prev);
+  }
+  /* else no conflict. success. */
+  crypto_free_digest_env(backup_digest);
+  return 0;
+}
+
+/*
+package a relay cell:
+  - if from AP: encrypt it to the right conn if 'recognized' doesn't
+    conflict, else insert dummies first as appropriate
+  - if from exit: encrypt it
+  - connection_or_write_cell_to_buf to the right conn
+*/
+int
+circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
+                           int cell_direction,
+                           crypt_path_t *layer_hint)
+{
+  connection_t *conn; /* where to send the cell */
+  crypt_path_t *conflicthop; /* for padding to prematurely recognized hops */
+  crypt_path_t *tmpconflicthop;
+  relay_header_t rh; /* to make the dummy cell */
+  /* backup cell and digest if we need to send a dummy instead */
+  cell_t backup_cell;
+
+  if(cell_direction == CELL_DIRECTION_OUT) {
+    conn = circ->n_conn;
+    if(!conn) {
+      log_fn(LOG_INFO,"outgoing relay cell has n_conn==NULL. Dropping.");
+      return 0; /* just drop it */
+    }
+
+    again:
+
+    /* copy cell in case we need to send a dummy relay cell instead */
+    memcpy(&backup_cell, cell, sizeof(cell_t));
+
+    log_fn(LOG_INFO,"Starting to crypt my relay cell.");
+    if(relay_try_all_crypts(cell, circ, layer_hint, 0, &conflicthop) < 0) {
+      log_fn(LOG_WARN,"relay_try_deliver_cell (1) failed. Closing.");
+      return -1;
+    }
+    if(conflicthop) {
+      log_fn(LOG_WARN,"'recognized' conflict found. Sending padding.");
+      /* 1a. build and set_digest a padding cell. */
+      cell->circ_id = backup_cell.circ_id;
+      cell->command = CELL_RELAY;
+      rh.command = RELAY_COMMAND_DROP;
+      rh.recognized = rh.stream_id = rh.integrity = rh.length = 0;
+      relay_header_pack(cell->payload, &rh);
+      memset(cell->payload+RELAY_HEADER_SIZE, 0, RELAY_PAYLOAD_SIZE);
+
+      /* if conflicthop is the last hop in the circuit... */
+      if(conflicthop == circ->cpath->prev ||
+         conflicthop->next->state == CPATH_STATE_CLOSED) {
+        log_fn(LOG_WARN,"Got a conflict at the end of the circuit, using recognized=1");
+        if(relay_try_all_crypts(cell, circ, conflicthop, 11257, &tmpconflicthop) < 0) {
+          log_fn(LOG_WARN,"relay_try_deliver_cell (2) failed. Closing.");
           return -1;
         }
-        memcpy(in,out,CELL_PAYLOAD_SIZE);
-        log_fn(LOG_DEBUG,"after encrypt: %d",*(uint16_t*)(in+1));
-
-        thishop = thishop->prev;
-      } while(thishop != circ->cpath->prev);
-    } else { /* we're in the middle. Just one crypt. */
+      } else {
+        log_fn(LOG_WARN,"Got a conflict inside the circuit, using recognized=0");
+        if(relay_try_all_crypts(cell, circ, conflicthop->next, 0, &tmpconflicthop) < 0) {
+          log_fn(LOG_WARN,"relay_try_deliver_cell (3) failed. Closing.");
+          return -1;
+        }
+      }
 
-      if(crypto_cipher_decrypt(circ->n_crypto,in, CELL_PAYLOAD_SIZE, out)) {
-        log_fn(LOG_WARN,"Decryption failed for circID %u: %s",
-               circ->n_circ_id, crypto_perror());
+      if(tmpconflicthop) { /* conflict with this one too?! */
+        log_fn(LOG_WARN,"Conflict with sending padding relay cell. Closing.");
         return -1;
       }
-      memcpy(in,out,CELL_PAYLOAD_SIZE);
 
-      if(*(uint16_t*)(in+1) == 0) { /* XXX */
-        *recognized = 1;
-        return 0;
-      }
+      /* send it. */
+      ++stats_n_relay_cells_relayed;
+      connection_or_write_cell_to_buf(cell, conn);
+      log_fn(LOG_INFO,"Delivered my padding cell.");
+
+      /* 2. Restore the original relay cell */
+      memcpy(cell, &backup_cell, sizeof(cell_t));
+
+      /* 3. Try again. */
+      goto again;
     }
+    /* no conflict, we succeeded. send it and be done. */
+  } else { /* incoming cell */
+    conn = circ->p_conn;
+    if(!conn) {
+      log_fn(LOG_INFO,"incoming relay cell has p_conn==NULL. Dropping.");
+      return 0; /* just drop it */
+    }
+    relay_set_digest(circ->p_digest, cell);
+    if(relay_crypt_one_payload(circ->p_crypto, cell->payload, 1) < 0)
+      return -1;
   }
+  ++stats_n_relay_cells_relayed;
+  connection_or_write_cell_to_buf(cell, conn);
   return 0;
 }
 
-static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction) {
+static connection_t *
+relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction)
+{
   connection_t *tmpconn;
   relay_header_t rh;
 
@@ -916,7 +1074,7 @@
      * it to a create cell and then send to hop */
     if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTEND,
                                payload, sizeof(payload), hop->prev) < 0)
-      return -1; /* circuit is closed */
+      return 0; /* circuit is closed */
 
     hop->state = CPATH_STATE_AWAITING_KEYS;
   }

Index: command.c
===================================================================
RCS file: /home/or/cvsroot/src/or/command.c,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -d -r1.51 -r1.52
--- command.c	17 Dec 2003 21:09:31 -0000	1.51
+++ command.c	23 Dec 2003 07:45:31 -0000	1.52
@@ -168,16 +168,14 @@
   }
 
   if(cell->circ_id == circ->p_circ_id) { /* it's an outgoing cell */
-    cell->circ_id = circ->n_circ_id; /* switch it */
-    if(circuit_deliver_relay_cell(cell, circ, CELL_DIRECTION_OUT, conn->cpath_layer) < 0) {
-      log_fn(LOG_WARN,"circuit_deliver_relay_cell (forward) failed. Closing.");
+    if(circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_OUT) < 0) {
+      log_fn(LOG_WARN,"circuit_receive_relay_cell (forward) failed. Closing.");
       circuit_close(circ);
       return;
     }
   } else { /* it's an ingoing cell */
-    cell->circ_id = circ->p_circ_id; /* switch it */
-    if(circuit_deliver_relay_cell(cell, circ, CELL_DIRECTION_IN, NULL) < 0) {
-      log_fn(LOG_WARN,"circuit_deliver_relay_cell (backward) failed. Closing.");
+    if(circuit_receive_relay_cell(cell, circ, CELL_DIRECTION_IN) < 0) {
+      log_fn(LOG_WARN,"circuit_receive_relay_cell (backward) failed. Closing.");
       circuit_close(circ);
       return;
     }

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_edge.c,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -d -r1.82 -r1.83
--- connection_edge.c	19 Dec 2003 21:25:43 -0000	1.82
+++ connection_edge.c	23 Dec 2003 07:45:31 -0000	1.83
@@ -171,16 +171,11 @@
   if(payload_len)
     memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len);
 
-  if(cell_direction == CELL_DIRECTION_OUT) /* AP */
-    relay_set_digest(cpath_layer->f_digest, &cell);
-  else /* exit */
-    relay_set_digest(circ->p_digest, &cell);
-
   log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command,
          cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
 
-  if(circuit_deliver_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) {
-    log_fn(LOG_WARN,"circuit_deliver_relay_cell failed. Closing.");
+  if(circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) {
+    log_fn(LOG_WARN,"circuit_package_relay_cell failed. Closing.");
     circuit_close(circ);
     return -1;
   }
@@ -226,6 +221,9 @@
   }
 
   switch(rh.command) {
+    case RELAY_COMMAND_DROP:
+      log_fn(LOG_INFO,"Got a relay-level padding cell. Dropping.");
+      return 0;
     case RELAY_COMMAND_BEGIN:
       if(edge_type == EDGE_AP) {
         log_fn(LOG_WARN,"relay begin request unsupported at AP. Dropping.");

Index: connection_or.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_or.c,v
retrieving revision 1.81
retrieving revision 1.82
diff -u -d -r1.81 -r1.82
--- connection_or.c	17 Dec 2003 21:09:31 -0000	1.81
+++ connection_or.c	23 Dec 2003 07:45:31 -0000	1.82
@@ -241,13 +241,14 @@
 
 /* ********************************** */
 
-void connection_or_write_cell_to_buf(const cell_t *cellp, connection_t *conn) {
+void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn) {
   char networkcell[CELL_NETWORK_SIZE];
   char *n = networkcell;
 
+  assert(cell && conn);
   assert(connection_speaks_cells(conn));
 
-  cell_pack(n, cellp);
+  cell_pack(n, cell);
 
   connection_write_to_buf(n, CELL_NETWORK_SIZE, conn);
 }

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.214
retrieving revision 1.215
diff -u -d -r1.214 -r1.215
--- or.h	19 Dec 2003 21:25:44 -0000	1.214
+++ or.h	23 Dec 2003 07:45:31 -0000	1.215
@@ -565,9 +565,9 @@
 int circuit_count_building(void);
 int circuit_stream_is_being_handled(connection_t *conn);
 
-void relay_set_digest(crypto_digest_env_t *digest, cell_t *cell);
-int relay_check_digest(crypto_digest_env_t *digest, cell_t *cell);
-int circuit_deliver_relay_cell(cell_t *cell, circuit_t *circ,
+int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
+                               int cell_direction);
+int circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
                                int cell_direction, crypt_path_t *layer_hint);
 
 void circuit_resume_edge_reading(circuit_t *circ, int edge_type, crypt_path_t *layer_hint);
@@ -695,7 +695,7 @@
 int connection_tls_start_handshake(connection_t *conn, int receiving);
 int connection_tls_continue_handshake(connection_t *conn);
 
-void connection_or_write_cell_to_buf(const cell_t *cellp, connection_t *conn);
+void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn);
 
 /********************************* cpuworker.c *****************************/
 



More information about the tor-commits mailing list