diff --git a/src/or/config.c b/src/or/config.c
index 8bcb5bea1de14f294d885e60cbb48986099967ed..af0fab8a6340130ebc7c930904c99f5652f03c79 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -99,6 +99,8 @@ RETURN VALUE: 0 on success, non-zero on error
          0, "how many seconds between directory rebuilds",     "<rebuildperiod>" },
       { "DirFetchPeriod",  'F',  POPT_ARG_INT,     &options->DirFetchPeriod,
          0, "how many seconds between directory fetches",     "<fetchperiod>" },
+      { "KeepalivePeriod", 'K',  POPT_ARG_INT,     &options->KeepalivePeriod,
+         0, "how many seconds between keepalives",            "<keepaliveperiod>" },
 //      { "ReconnectPeriod", 'e',  POPT_ARG_INT,     &options->ReconnectPeriod,
 //         0, "how many seconds between retrying all OR connections", "<reconnectperiod>" },
       { "Role",            'R',  POPT_ARG_INT,     &options->Role,
@@ -122,6 +124,7 @@ RETURN VALUE: 0 on success, non-zero on error
    options->LinkPadding = 0;
    options->DirRebuildPeriod = 600;
    options->DirFetchPeriod = 6000;
+   options->KeepalivePeriod = 300;
 //   options->ReconnectPeriod = 6001;
    options->Role = ROLE_OR_LISTEN | ROLE_OR_CONNECT_ALL | ROLE_OP_LISTEN | ROLE_AP_LISTEN;
 
@@ -170,9 +173,10 @@ RETURN VALUE: 0 on success, non-zero on error
              options->MaxConn,
              options->TrafficShaping,
              options->LinkPadding);
-      printf("DirRebuildPeriod=%d, DirFetchPeriod=%d\n",
+      printf("DirRebuildPeriod=%d, DirFetchPeriod=%d KeepalivePeriod=%d\n",
              options->DirRebuildPeriod,
-             options->DirFetchPeriod);
+             options->DirFetchPeriod,
+             options->KeepalivePeriod);
    }
 
    /* Validate options */
@@ -287,6 +291,12 @@ RETURN VALUE: 0 on success, non-zero on error
       code = -1;
    }
 
+   if ( options->KeepalivePeriod < 1)
+   {
+      log(LOG_ERR,"KeepalivePeriod option must be positive.");
+      code = -1;
+   }
+
    return code;
 }
 
diff --git a/src/or/connection.c b/src/or/connection.c
index d2445effea081832887aa668a4debb923128b307..f8333c0ade00fee60426c511dbd81e6d875be861 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -86,6 +86,10 @@ void tv_addms(struct timeval *a, long ms) {
 
 connection_t *connection_new(int type) {
   connection_t *conn;
+  struct timeval now;
+
+  if(gettimeofday(&now,NULL) < 0)
+    return NULL;
 
   conn = (connection_t *)malloc(sizeof(connection_t));
   if(!conn)
@@ -99,6 +103,7 @@ connection_t *connection_new(int type) {
 
   conn->receiver_bucket = 10240; /* should be enough to do the handshake */
   conn->bandwidth = conn->receiver_bucket / 10; /* give it a default */
+  conn->timestamp_created = now.tv_sec;
   
   if (connection_speaks_cells(conn)) {
     conn->f_crypto = crypto_new_cipher_env(CRYPTO_CIPHER_DES);
@@ -320,6 +325,7 @@ connection_t *connection_connect_to_router_as_op(routerinfo_t *router, uint16_t
 
 int connection_read_to_buf(connection_t *conn) {
   int read_result;
+  struct timeval now;
 
   if(connection_speaks_cells(conn)) {
     assert(conn->receiver_bucket >= 0);
@@ -327,6 +333,11 @@ int connection_read_to_buf(connection_t *conn) {
   if(!connection_speaks_cells(conn)) {
     assert(conn->receiver_bucket < 0);
   }
+
+  if(gettimeofday(&now,NULL) < 0)
+    return -1;
+  conn->timestamp_lastread = now.tv_sec;
+
   read_result = read_to_buf(conn->s, conn->receiver_bucket, &conn->inbuf, &conn->inbuflen,
                             &conn->inbuf_datalen, &conn->inbuf_reached_eof);
 //  log(LOG_DEBUG,"connection_read_to_buf(): read_to_buf returned %d.",read_result);
@@ -369,9 +380,16 @@ int connection_flush_buf(connection_t *conn) {
 }
 
 int connection_write_to_buf(char *string, int len, connection_t *conn) {
+  struct timeval now;
+
+  if(gettimeofday(&now,NULL) < 0)
+    return -1;
+
   if(!len)
     return 0;
 
+  conn->timestamp_lastwritten = now.tv_sec;
+
   if( (!connection_speaks_cells(conn)) ||
       (!connection_state_is_open(conn)) ||
       (options.LinkPadding == 0) ) {
diff --git a/src/or/main.c b/src/or/main.c
index 0e4af5280eb41b228c3b5680bcdf836097e52fe8..ff89dd7578ca3cdaa961fafa4d4f591816eef8f7 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -310,20 +310,11 @@ int prepare_for_poll(int *timeout) {
   connection_t *conn = NULL;
   connection_t *tmpconn;
   struct timeval now, soonest;
-  static int current_second = 0; /* from previous calls to gettimeofday */
-  static int time_to_rebuild_directory = 0;
-  static int time_to_fetch_directory = 0;
+  static long current_second = 0; /* from previous calls to gettimeofday */
+  static long time_to_rebuild_directory = 0;
+  static long time_to_fetch_directory = 0;
   int ms_until_conn;
-
-  *timeout = -1; /* set it to never timeout, possibly overridden below */
-  
-  /* first check if we need to refill buckets */
-  for(i=0;i<nfds;i++) {
-    if(connection_receiver_bucket_should_increase(connection_array[i])) {
-      need_to_refill_buckets = 1;
-      break;
-    }
-  }
+  cell_t cell;
 
   if(gettimeofday(&now,NULL) < 0)
     return -1;
@@ -356,6 +347,50 @@ int prepare_for_poll(int *timeout) {
     *timeout = 1000*(time_to_fetch_directory - now.tv_sec) + (1000 - (now.tv_usec / 1000));
   }
 
+  /* check connections to see whether we should send a keepalive, expire, or wait */
+  for(i=0;i<nfds;i++) {
+    tmpconn = connection_array[i];
+    if(!connection_speaks_cells(tmpconn))
+      continue; /* this conn type doesn't send cells */
+    if(!connection_state_is_open(tmpconn)) {
+      continue; /* only conns in state 'open' need a keepalive */
+      /* XXX should time-out unfinished connections someday too */
+    }    
+    if(now.tv_sec >= tmpconn->timestamp_lastwritten + options.KeepalivePeriod) {
+      if(!(options.Role & ROLE_OR_CONNECT_ALL) && !circuit_get_by_conn(tmpconn)) {
+        /* we're an onion proxy, with no circuits. kill it. */
+        log(LOG_DEBUG,"prepare_for_poll(): Expiring connection to %d (%s:%d).",
+            i,tmpconn->address, tmpconn->port);
+        tmpconn->marked_for_close = 1;
+      } else {
+        /* either a full router, or we've got a circuit. send a padding cell. */
+//        log(LOG_DEBUG,"prepare_for_poll(): Sending keepalive to (%s:%d)",
+//            tmpconn->address, tmpconn->port);
+        memset(&cell,0,sizeof(cell_t));
+        cell.command = CELL_PADDING;
+        connection_write_cell_to_buf(&cell, tmpconn);
+      }
+    }
+    if(!tmpconn->marked_for_close &&
+       *timeout > 1000*(tmpconn->timestamp_lastwritten + options.KeepalivePeriod - now.tv_sec)) {
+      *timeout = 1000*(tmpconn->timestamp_lastwritten + options.KeepalivePeriod - now.tv_sec);
+    }
+  }
+  assert(*timeout >= 0);
+  /* blow away any connections that need to die. can't do this later
+   * because we might open up a circuit and not realize it.
+   */
+  for(i=0;i<nfds;i++)
+    check_conn_marked(i); 
+
+  /* check if we need to refill buckets */
+  for(i=0;i<nfds;i++) {
+    if(connection_receiver_bucket_should_increase(connection_array[i])) {
+      need_to_refill_buckets = 1;
+      break;
+    }
+  }
+
   if(need_to_refill_buckets) {
     if(now.tv_sec > current_second) { /* the second has already rolled over! */
 //      log(LOG_DEBUG,"prepare_for_poll(): The second has rolled over, immediately refilling.");
@@ -363,7 +398,7 @@ int prepare_for_poll(int *timeout) {
         connection_increment_receiver_bucket(connection_array[i]);
       current_second = now.tv_sec; /* remember which second it is, for next time */
     }
-    /* this timeout is definitely sooner than either of the above two */
+    /* this timeout is definitely sooner than any of the above ones */
     *timeout = 1000 - (now.tv_usec / 1000); /* how many milliseconds til the next second? */
   }
 
@@ -394,7 +429,7 @@ int prepare_for_poll(int *timeout) {
       ms_until_conn = (soonest.tv_sec - now.tv_sec)*1000 +
                     (soonest.tv_usec - now.tv_usec)/1000;
 //      log(LOG_DEBUG,"prepare_for_poll(): conn %d times out in %d ms.",conn->s, ms_until_conn);
-      if(*timeout == -1 || ms_until_conn < *timeout) { /* use the new one */
+      if(ms_until_conn < *timeout) { /* use the new one */
 //        log(LOG_DEBUG,"prepare_for_poll(): conn %d soonest, in %d ms.",conn->s,ms_until_conn);
         *timeout = ms_until_conn;
       }
@@ -517,18 +552,25 @@ static void catch(int the_signal) {
 void dumpstats (void) { /* dump stats to stdout */
   int i;
   connection_t *conn;
+  struct timeval now;
   extern char *conn_type_to_string[];
   extern char *conn_state_to_string[][15];
 
   printf("Dumping stats:\n");
+  if(gettimeofday(&now,NULL) < 0)
+    return ;
 
   for(i=0;i<nfds;i++) {
     conn = connection_array[i];
-    printf("Conn %d (socket %d) type %d (%s), state %d (%s)\n",
+    printf("Conn %d (socket %d) type %d (%s), state %d (%s), created %ld secs ago\n",
       i, conn->s, conn->type, conn_type_to_string[conn->type],
-      conn->state, conn_state_to_string[conn->type][conn->state]);
+      conn->state, conn_state_to_string[conn->type][conn->state], now.tv_sec - conn->timestamp_created);
     if(!connection_is_listener(conn)) {
       printf("Conn %d is to '%s:%d'.\n",i,conn->address, conn->port);
+      printf("Conn %d: %d bytes waiting on inbuf (last read %ld secs ago)\n",i,conn->inbuf_datalen,
+        now.tv_sec - conn->timestamp_lastread);
+      printf("Conn %d: %d bytes waiting on outbuf (last written %ld secs ago)\n",i,conn->outbuf_datalen, 
+        now.tv_sec - conn->timestamp_lastwritten);
     }
     circuit_dump_by_conn(conn); /* dump info about all the circuits using this conn */
     printf("\n");
diff --git a/src/or/or.h b/src/or/or.h
index 72eb79176f761220a07ff5fac43d58e86e043ada..7720ad3ed9c02cacaa4ed5702dc5c82fa811125d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -188,11 +188,15 @@ typedef struct
   int inbuflen;
   int inbuf_datalen;
   int inbuf_reached_eof;
+  long timestamp_lastread;
 
   char *outbuf;
   int outbuflen; /* how many bytes are allocated for the outbuf? */
   int outbuf_flushlen; /* how much data should we try to flush from the outbuf? */
   int outbuf_datalen; /* how much data is there total on the outbuf? */
+  long timestamp_lastwritten;
+
+  long timestamp_created;
 
 //  uint16_t aci; /* anonymous connection identifier */
 
@@ -357,6 +361,7 @@ typedef struct
    int LinkPadding;
    int DirRebuildPeriod;
    int DirFetchPeriod;
+   int KeepalivePeriod;
    int Role;
    int loglevel;
 } or_options_t;