chiark / gitweb /
Keepalives and pings.
authormdw <mdw>
Fri, 16 Sep 2005 13:08:42 +0000 (13:08 +0000)
committermdw <mdw>
Fri, 16 Sep 2005 13:08:42 +0000 (13:08 +0000)
admin.c
doc/tripe-admin.5
ethereal/packet-tripe.c
peer.c
tripe-protocol.h
tripe.h

diff --git a/admin.c b/admin.c
index bbfc0ed65b4d930f4df788e4297a2553e152b5a3..e081bdf6106ffd6faf982b6b54a88b3cbdf4bc41 100644 (file)
--- a/admin.c
+++ b/admin.c
@@ -71,6 +71,7 @@ static sig s_term, s_int, s_hup;
 #define F_INIT 2u
 
 #define T_RESOLVE SEC(30)
 #define F_INIT 2u
 
 #define T_RESOLVE SEC(30)
+#define T_PING SEC(5)
 
 static void a_destroy(admin */*a*/);
 static void a_lock(admin */*a*/);
 
 static void a_destroy(admin */*a*/);
 static void a_lock(admin */*a*/);
@@ -467,6 +468,29 @@ static void a_sighup(int sig, void *v)
   a_warn("SERVER ignore signal SIGHUP");
 }
 
   a_warn("SERVER ignore signal SIGHUP");
 }
 
+/* --- @a_parsetime@ --- *
+ *
+ * Arguments;  @const char *p@ = time string to parse
+ *
+ * Returns:    Time in seconds, or @< 0@ on error.
+ */
+
+static long a_parsetime(const char *p)
+{
+  char *q;
+  long t = strtol(p, &q, 0);
+
+  switch (*q) {
+    case 'd': t *= 24;
+    case 'h': t *= 60;
+    case 'm': t *= 60;
+    case 's': if (q[1] != 0)
+    default:    t = -1;
+    case 0:   break;
+  }
+  return (t);    
+}
+
 /*----- Adding peers ------------------------------------------------------*/
  
 /* --- @a_resolve@ --- *
 /*----- Adding peers ------------------------------------------------------*/
  
 /* --- @a_resolve@ --- *
@@ -489,18 +513,18 @@ static void a_resolve(struct hostent *h, void *v)
   sel_rmtimer(&a->t);
   if (!h)
     a_fail(a, "resolve-error %s", a->paddr);
   sel_rmtimer(&a->t);
   if (!h)
     a_fail(a, "resolve-error %s", a->paddr);
-  else if (p_find(a->pname))
-    a_fail(a, "peer-exists %s", a->pname);
+  else if (p_find(a->peer.name))
+    a_fail(a, "peer-exists %s", a->peer.name);
   else {
   else {
-    memcpy(&a->peer.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
-    if (!p_create(a->pname, a->tops, &a->peer.sa, a->sasz))
-      a_fail(a, "peer-create-fail %s", a->pname);
+    memcpy(&a->peer.sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
+    if (!p_create(&a->peer))
+      a_fail(a, "peer-create-fail %s", a->peer.name);
     else
       a_ok(a);
   }
     else
       a_ok(a);
   }
-  xfree(a->pname);
+  xfree(a->peer.name);
   xfree(a->paddr);
   xfree(a->paddr);
-  a->pname = 0;
+  a->peer.name = 0;
   selbuf_enable(&a->b);
   a_unlock(a);
 }
   selbuf_enable(&a->b);
   a_unlock(a);
 }
@@ -523,9 +547,9 @@ static void a_timer(struct timeval *tv, void *v)
   T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
   bres_abort(&a->r);
   a_fail(a, "resolver-timeout %s\n", a->paddr);
   T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
   bres_abort(&a->r);
   a_fail(a, "resolver-timeout %s\n", a->paddr);
-  xfree(a->pname);
+  xfree(a->peer.name);
   xfree(a->paddr);
   xfree(a->paddr);
-  a->pname = 0;
+  a->peer.name = 0;
   selbuf_enable(&a->b);
   a_unlock(a);
 }
   selbuf_enable(&a->b);
   a_unlock(a);
 }
@@ -546,7 +570,6 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
   unsigned long pt;
   struct timeval tv;
   unsigned i, j;
   unsigned long pt;
   struct timeval tv;
   unsigned i, j;
-  const tunnel_ops *tops = tun_default;
   char *p;
 
   /* --- Make sure someone's not got there already --- */
   char *p;
 
   /* --- Make sure someone's not got there already --- */
@@ -556,6 +579,12 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
     return;
   }
 
     return;
   }
 
+  /* --- Set stuff up --- */
+
+  a->peer.name = av[0];
+  a->peer.t_ka = 0;
+  a->peer.tops = tun_default;
+
   /* --- Parse options --- */
 
   i = 1;
   /* --- Parse options --- */
 
   i = 1;
@@ -563,37 +592,42 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
     if (!av[i])
       goto bad_syntax;
     if (mystrieq(av[i], "-tunnel")) {
     if (!av[i])
       goto bad_syntax;
     if (mystrieq(av[i], "-tunnel")) {
-      i++;
-      if (!av[i])
-       goto bad_syntax;
+      if (!av[++i]) goto bad_syntax;
       for (j = 0;; j++) {
        if (!tunnels[j]) {
          a_fail(a, "unknown-tunnel %s", av[i]);
          return;
        }
        if (mystrieq(av[i], tunnels[j]->name)) {
       for (j = 0;; j++) {
        if (!tunnels[j]) {
          a_fail(a, "unknown-tunnel %s", av[i]);
          return;
        }
        if (mystrieq(av[i], tunnels[j]->name)) {
-         tops = tunnels[j];
+         a->peer.tops = tunnels[j];
          break;
        }
       }
          break;
        }
       }
-      i++;
+    } else if (mystrieq(av[i], "-keepalive")) {
+      long t;
+      if (!av[++i]) goto bad_syntax;
+      if ((t = a_parsetime(av[i])) < 0) {
+       a_fail(a, "bad-time-spec %s", av[i]);
+       return;
+      }
+      a->peer.t_ka = t;
     } else if (mystrieq(av[i], "--")) {
       i++;
       break;
     } else
       break;
     } else if (mystrieq(av[i], "--")) {
       i++;
       break;
     } else
       break;
+    i++;
   }
 
   /* --- Fill in the easy bits of address --- */
 
   }
 
   /* --- Fill in the easy bits of address --- */
 
-  BURN(a->peer);
   if (mystrieq(av[i], "inet")) i++;
   if (ac - i != 2) {
   if (mystrieq(av[i], "inet")) i++;
   if (ac - i != 2) {
-    a_fail(a, "bad-syntax -- add PEER [-tunnel TUN] [inet] ADDRESS PORT");
+    a_fail(a, "bad-syntax -- add PEER [OPTIONS] [inet] ADDRESS PORT");
     return;
   }
     return;
   }
-  a->peer.sin.sin_family = AF_INET;
-  a->sasz = sizeof(a->peer.sin);
+  a->peer.sa.sin.sin_family = AF_INET;
+  a->peer.sasz = sizeof(a->peer.sa.sin);
   pt = strtoul(av[i + 1], &p, 0);
   if (*p) {
     struct servent *s = getservbyname(av[i + 1], "udp");
   pt = strtoul(av[i + 1], &p, 0);
   if (*p) {
     struct servent *s = getservbyname(av[i + 1], "udp");
@@ -607,15 +641,16 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
     a_fail(a, "invalid-port %lu", pt);
     return;
   }
     a_fail(a, "invalid-port %lu", pt);
     return;
   }
-  a->peer.sin.sin_port = htons(pt);
+  a->peer.sa.sin.sin_port = htons(pt);
 
   /* --- If the name is numeric, do it the easy way --- */
   
 
   /* --- If the name is numeric, do it the easy way --- */
   
-  if (inet_aton(av[i], &a->peer.sin.sin_addr)) {
-    if (!p_create(av[0], tops, &a->peer.sa, a->sasz))
+  if (inet_aton(av[i], &a->peer.sa.sin.sin_addr)) {
+    if (!p_create(&a->peer))
       a_fail(a, "peer-create-fail %s", av[0]);
     else
       a_ok(a);
       a_fail(a, "peer-create-fail %s", av[0]);
     else
       a_ok(a);
+    a->peer.name = 0;
     return;
   }
 
     return;
   }
 
@@ -626,9 +661,8 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
    * the system continues regardless), but makes life simpler for the client.
    */
 
    * the system continues regardless), but makes life simpler for the client.
    */
 
-  a->pname = xstrdup(av[0]);
+  a->peer.name = xstrdup(av[0]);
   a->paddr = xstrdup(av[i]);
   a->paddr = xstrdup(av[i]);
-  a->tops = tops;
   selbuf_disable(&a->b);
   gettimeofday(&tv, 0);
   tv.tv_sec += T_RESOLVE;
   selbuf_disable(&a->b);
   gettimeofday(&tv, 0);
   tv.tv_sec += T_RESOLVE;
@@ -639,10 +673,108 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
   return;
 
 bad_syntax:
   return;
 
 bad_syntax:
-  a_fail(a, "bad-syntax -- add PEER [-tunnel TUN] ADDR ...");
+  a_fail(a, "bad-syntax -- add PEER [OPTIONS] ADDR ...");
+  return;
+}
+
+/*----- Ping --------------------------------------------------------------*/
+
+/* --- @a_pong@ --- *
+ *
+ * Arguments:  @int rc@ = return code
+ *             @void *av@ = admin connection which requested the ping
+ *
+ * Returns:    ---
+ *
+ * Use:                Collects what happened to a ping message.
+ */
+
+static void a_pong(int rc, void *av)
+{
+  admin *a = av;
+  struct timeval tv;
+  double millis;
+
+  a_lock(a);
+  switch (rc) {
+    case PING_OK:
+      gettimeofday(&tv, 0);
+      tv_sub(&tv, &tv, &a->pingtime);
+      millis = (double)tv.tv_sec * 1000 + (double)tv.tv_usec/1000;
+      a_info(a, "ping-ok %.1f", millis);
+      a_ok(a);
+      break;
+    case PING_TIMEOUT:
+      a_info(a, "ping-timeout");
+      a_ok(a);
+      break;
+    case PING_PEERDIED:
+      a_info(a, "ping-peer-died");
+      a_ok(a);
+      break;
+    default:
+      abort();
+  }
+  a_unlock(a);
+}
+
+/* --- @acmd_ping@, @acmd_eping@ --- *
+ *
+ * Arguments:  @admin *a@ = connection which requested the ping
+ *             @unsigned ac@ = argument count
+ *             @char *av[]@ = pointer to the argument list
+ *
+ * Returns:    ---
+ *
+ * Use:                Pings a peer.
+ */
+
+static void a_ping(admin *a, unsigned ac, char *av[],
+                  const char *cmd, unsigned msg)
+{
+  long t = T_PING;
+  int i;
+  peer *p;
+
+  i = 0;
+  for (;;) {
+    if (!av[i])
+      goto bad_syntax;
+    if (mystrieq(av[i], "-timeout")) {
+      if (!av[++i]) goto bad_syntax;
+      if ((t = a_parsetime(av[i])) < 0) {
+       a_fail(a, "bad-time-spec %s", av[i]);
+       return;
+      }
+    } else if (mystrieq(av[i], "--")) {
+      i++;
+      break;
+    } else
+      break;
+    i++;
+  }
+
+  if (!av[i]) goto bad_syntax;
+  if ((p = p_find(av[i])) == 0) {
+    a_fail(a, "unknown-peer %s", av[i]);
+    return;
+  }
+  gettimeofday(&a->pingtime, 0);
+  if (p_pingsend(p, &a->ping, msg, t, a_pong, a))
+    a_fail(a, "ping-send-failed");
+  return;
+    
+bad_syntax:
+  a_fail(a, "bad-syntax -- %s [OPTIONS] PEER", cmd);
   return;
 }
 
   return;
 }
 
+static void acmd_ping(admin *a, unsigned ac, char *av[])
+  { a_ping(a, ac, av, "ping", MISC_PING); }
+static void acmd_eping(admin *a, unsigned ac, char *av[])
+  { a_ping(a, ac, av, "eping", MISC_EPING); }
+  
+
 /*----- Administration commands -------------------------------------------*/
 
 /* --- Miscellaneous commands --- */
 /*----- Administration commands -------------------------------------------*/
 
 /* --- Miscellaneous commands --- */
@@ -856,6 +988,17 @@ static void acmd_kill(admin *a, unsigned ac, char *av[])
   }
 }
 
   }
 }
 
+static void acmd_forcekx(admin *a, unsigned ac, char *av[])
+{
+  peer *p;
+  if ((p = p_find(av[0])) == 0)
+    a_fail(a, "unknown-peer %s", av[0]);
+  else {
+    kx_start(&p->kx);
+    a_ok(a);
+  }
+}
+
 static void acmd_quit(admin *a, unsigned ac, char *av[])
 {
   a_warn("SERVER quit admin-request");
 static void acmd_quit(admin *a, unsigned ac, char *av[])
 {
   a_warn("SERVER quit admin-request");
@@ -903,8 +1046,11 @@ static const acmd acmdtab[] = {
   { "ifname",  "ifname PEER",          1,      1,      acmd_ifname },
   { "addr",    "addr PEER",            1,      1,      acmd_addr },
   { "stats",   "stats PEER",           1,      1,      acmd_stats },
   { "ifname",  "ifname PEER",          1,      1,      acmd_ifname },
   { "addr",    "addr PEER",            1,      1,      acmd_addr },
   { "stats",   "stats PEER",           1,      1,      acmd_stats },
+  { "ping",    "ping [OPTIONS] PEER",  1,      0xffff, acmd_ping },
+  { "eping",   "eping [OPTIONS] PEER", 1,      0xffff, acmd_eping },
   { "kill",    "kill PEER",            1,      1,      acmd_kill },
   { "kill",    "kill PEER",            1,      1,      acmd_kill },
-  { "add",     "add PEER [-tunnel TUN] ADDR ...",
+  { "forcekx", "forcekx PEER",         1,      1,      acmd_forcekx },
+  { "add",     "add PEER [OPTIONS] ADDR ...",
                                        2,      0xffff, acmd_add },
   { "tunnels", "tunnels",              0,      0,      acmd_tunnels },
   { "quit",    "quit",                 0,      0,      acmd_quit },
                                        2,      0xffff, acmd_add },
   { "tunnels", "tunnels",              0,      0,      acmd_tunnels },
   { "quit",    "quit",                 0,      0,      acmd_quit },
@@ -955,12 +1101,14 @@ static void a_unlock(admin *a)
           a->seq); )
 
   selbuf_destroy(&a->b);
           a->seq); )
 
   selbuf_destroy(&a->b);
-  if (a->pname) {
-    xfree(a->pname);
+  if (a->peer.name) {
+    xfree(a->peer.name);
     xfree(a->paddr);
     bres_abort(&a->r);
     sel_rmtimer(&a->t);
   }
     xfree(a->paddr);
     bres_abort(&a->r);
     sel_rmtimer(&a->t);
   }
+  if (a->ping.p)
+    p_pingdone(&a->ping, PING_NONOTIFY);
   if (a->b.reader.fd != a->w.fd)
     close(a->b.reader.fd);
   close(a->w.fd);
   if (a->b.reader.fd != a->w.fd)
     close(a->b.reader.fd);
   close(a->w.fd);
@@ -1081,7 +1229,8 @@ void a_create(int fd_in, int fd_out, unsigned f)
   T( static unsigned seq = 0;
      a->seq = seq++; )
   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
   T( static unsigned seq = 0;
      a->seq = seq++; )
   T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
-  a->pname = 0;
+  a->peer.name = 0;
+  a->ping.p = 0;
   a->f = f;
   if (fd_in == STDIN_FILENO)
     a_stdin = a;
   a->f = f;
   if (fd_in == STDIN_FILENO)
     a_stdin = a;
index 79874e914e2267c745e5e1f81c741c4971e7a029..327d1b562683897a4d12ecca0b9b4d05dec8a849 100644 (file)
@@ -109,7 +109,22 @@ is the network address (see above for the format) at which the peer can
 be contacted.  The following options are recognised.
 .RS
 .TP
 be contacted.  The following options are recognised.
 .RS
 .TP
-.BI "-tunnel " tunnel
+.BI "\-keepalive " time
+Send a no-op packet if we've not sent a packet to the peer in the last
+.I time
+interval.  This is useful for persuading port-translating firewalls to
+believe that the `connection' is still active.  The
+.I time
+is expressed as a nonnegative integer followed optionally by
+.BR d ,
+.BR h ,
+.BR m ,
+or
+.BR s
+for days, hours, minutes, or seconds respectively; if no suffix is
+given, seconds are assumed.
+.TP
+.BI "\-tunnel " tunnel
 Use the named tunnel driver, rather than the default.
 .RE
 .TP
 Use the named tunnel driver, rather than the default.
 .RE
 .TP
@@ -123,6 +138,14 @@ line reporting the IP address and port number stored for
 Causes the server to disassociate itself from its terminal and become a
 background task.  This only works once.  A warning is issued.
 .TP
 Causes the server to disassociate itself from its terminal and become a
 background task.  This only works once.  A warning is issued.
 .TP
+.BI "EPING \fR[" options "\fR] " peer
+Sends an encrypted ping to the peer, and expects an encrypted response.
+This checks that the peer is running (and not being impersonated), and
+that it can encrypt and decrypt packets correctly.  Options and
+responses are the same as for the
+.B PING
+command.
+.TP
 .B "HELP"
 Causes the server to emit an
 .B INFO
 .B "HELP"
 Causes the server to emit an
 .B INFO
@@ -157,6 +180,44 @@ Issues a
 .B USER
 notification to all interested administration clients.
 .TP
 .B USER
 notification to all interested administration clients.
 .TP
+.BI "PING \fR[" options "\fR] " peer
+Send a transport-level ping to the peer.  The ping and its response are
+not encrypted or authenticated.  This command, possibly in conjunction
+with tracing, is useful for ensuring that UDP packets are actually
+flowing in both directions.  See also the
+.B EPING
+command.
+.IP
+An
+.B INFO
+line is printed describing the outcome:
+.RS
+.TP
+.BI "ping-ok " millis
+A response was received 
+.I millis
+after the ping was sent.
+.TP
+.BI "ping-timeout"
+No response was received within the time allowed.
+.TP
+.BI "ping-peer-died"
+The peer was killed (probably by another admin connection) before a
+response was received.
+.RE
+.IP
+Options recognized for this command are:
+.RS
+.TP
+.BI "\-timeout " time
+Wait for
+.I time
+seconds before giving up on a response.  The default is 5 seconds.  (The
+time format is the same as for the
+.B "ADD \-keepalive"
+option.)
+.RE
+.TP
 .B "PORT"
 Emits an
 .B INFO
 .B "PORT"
 Emits an
 .B INFO
@@ -316,6 +377,18 @@ server is already running as a daemon.
 (For any command.)  The command couldn't be understood: e.g., the number
 of arguments was wrong.
 .TP
 (For any command.)  The command couldn't be understood: e.g., the number
 of arguments was wrong.
 .TP
+.BI "bad-time-spec " word
+The
+.I word
+is not a valid time interval specification.  Acceptable time
+specifications are nonnegative integers followed optionally by 
+.BR d ,
+.BR h ,
+.BR m ,
+or
+.BR s ,
+for days, hours, minutes, or seconds, respectively.
+.TP
 .BI "bad-trace-option " char
 (For
 .BR TRACE .)
 .BI "bad-trace-option " char
 (For
 .BR TRACE .)
@@ -351,6 +424,10 @@ why.
 There is already a peer named
 .IR peer .
 .TP
 There is already a peer named
 .IR peer .
 .TP
+.B "ping-send-failed"
+The attempt to send a ping packet failed, probably due to lack of
+encryption keys.
+.TP
 .BI "resolve-error " hostname
 (For
 .BR ADD .)
 .BI "resolve-error " hostname
 (For
 .BR ADD .)
@@ -571,13 +648,6 @@ a peer, or
 .RB ` \- '
 if none is relevant.
 .TP
 .RB ` \- '
 if none is relevant.
 .TP
-.BI "PEER \- unexpected-source " address\fR...
-A packet arrived from
-.I address
-(a network address \(en see above), but no peer is known at that
-address.  This may indicate a misconfiguration, or simply be a result of
-one end of a connection being set up before the other.
-.TP
 .BI "PEER " peer " bad-packet no-type"
 An empty packet arrived.  This is very strange.
 .TP
 .BI "PEER " peer " bad-packet no-type"
 An empty packet arrived.  This is very strange.
 .TP
@@ -593,6 +663,15 @@ The message type
 (in hex) isn't understood.  Probably a strange random packet from
 somewhere; could be an unlikely bug.
 .TP
 (in hex) isn't understood.  Probably a strange random packet from
 somewhere; could be an unlikely bug.
 .TP
+.BI "PEER " peer " corrupt-encrypted-ping"
+The peer sent a ping response which matches an outstanding ping, but its
+payload is wrong.  There's definitely a bug somewhere.
+.TP
+.BI "PEER " peer " corrupt-transport-ping"
+The peer (apparently) sent a ping response which matches an outstanding
+ping, but its payload is wrong.  Either there's a bug, or the bad guys
+are playing tricks on you.
+.TP
 .BI "PEER " peer " decrypt-failed"
 An encrypted IP packet failed to decrypt.  It may have been mangled in
 transit, or may be a very old packet from an expired previous session
 .BI "PEER " peer " decrypt-failed"
 An encrypted IP packet failed to decrypt.  It may have been mangled in
 transit, or may be a very old packet from an expired previous session
@@ -600,6 +679,14 @@ key.  There is usually a considerable overlap in the validity periods of
 successive session keys, so this shouldn't occur unless the key exchange
 takes ages or fails.
 .TP
 successive session keys, so this shouldn't occur unless the key exchange
 takes ages or fails.
 .TP
+.BI "PEER " peer " malformed-encrypted-ping"
+The peer sent a ping response which is hopelessly invalid.  There's
+definitely a bug somewhere.
+.TP
+.BI "PEER " peer " malformed-transport-ping"
+The peer (apparently) sent a ping response which is hopelessly invalid.
+Either there's a bug, or the bad guys are playing tricks on you.
+.TP
 .BI "PEER " peer " packet-build-failed"
 There wasn't enough space in our buffer to put the packet we wanted to
 send.  Shouldn't happen.
 .BI "PEER " peer " packet-build-failed"
 There wasn't enough space in our buffer to put the packet we wanted to
 send.  Shouldn't happen.
@@ -610,6 +697,24 @@ An error occurred trying to read an incoming packet.
 .BI "PEER " peer " socket-write-error \-\- " message
 An error occurred attempting to send a network packet.  We lost that
 one.
 .BI "PEER " peer " socket-write-error \-\- " message
 An error occurred attempting to send a network packet.  We lost that
 one.
+.TP
+.BI "PEER " peer " unexpected-encrypted-ping 0x" id
+The peer sent an encrypted ping response whose id doesn't match any
+outstanding ping.  Maybe it was delayed for longer than the server was
+willing to wait, or maybe the peer has gone mad.
+.TP
+.BI "PEER \- unexpected-source " address\fR...
+A packet arrived from
+.I address
+(a network address \(en see above), but no peer is known at that
+address.  This may indicate a misconfiguration, or simply be a result of
+one end of a connection being set up before the other.
+.TP
+.BI "PEER " peer " unexpected-transport-ping 0x" id
+The peer (apparently) sent a transport ping response whose id doesn't
+match any outstanding ping.  Maybe it was delayed for longer than the
+server was willing to wait, or maybe the peer has gone mad; or maybe
+there are bad people trying to confuse you.
 .SS "SERVER warnings"
 These indicate problems concerning the server process as a whole.
 .TP
 .SS "SERVER warnings"
 These indicate problems concerning the server process as a whole.
 .TP
index aaad0ec9b486fabfbd0e71d8b5bbac4099a7c12a..44c74aa6e539b8fe62966210dbdb6317b9f4e6fc 100644 (file)
@@ -64,6 +64,8 @@ static int hf_tripe_ct_seq = -1;
 static int hf_tripe_ct_iv = -1;
 static int hf_tripe_ct_ct = -1;
 static int hf_tripe_ct_tag = -1;
 static int hf_tripe_ct_iv = -1;
 static int hf_tripe_ct_ct = -1;
 static int hf_tripe_ct_tag = -1;
+static int hf_tripe_misc_type = -1;
+static int hf_tripe_misc_payload = -1;
 static int hf_tripe_kx_type = -1;
 static hfge hf_tripe_kx_mychal = { -1, -1, -1, -1, -1, -1, -1, -1 };
 static int hf_tripe_kx_mycookie = -1;
 static int hf_tripe_kx_type = -1;
 static hfge hf_tripe_kx_mychal = { -1, -1, -1, -1, -1, -1, -1, -1 };
 static int hf_tripe_kx_mycookie = -1;
@@ -178,6 +180,32 @@ static void dissect_tripe(tvbuff_t *b, packet_info *p, proto_tree *t)
            break;
        }
        break;
            break;
        }
        break;
+      case MSG_MISC:
+       switch (ty & MSG_TYPEMASK) {
+         case MISC_NOP:
+           col_set_str(p->cinfo, COL_INFO, "Miscellaneous, no-operation");
+           break;
+         case MISC_PING:
+           col_set_str(p->cinfo, COL_INFO, "Miscellaneous, transport ping");
+           break;
+         case MISC_PONG:
+           col_set_str(p->cinfo, COL_INFO,
+                       "Miscellaneous, transport ping reply");
+           break;
+         case MISC_EPING:
+           col_set_str(p->cinfo, COL_INFO, "Miscellaneous, encrypted ping");
+           break;
+         case MISC_EPONG:
+           col_set_str(p->cinfo, COL_INFO,
+                       "Miscellaneous, encrypted ping reply");
+           break;
+         default:
+           col_add_fstr(p->cinfo, COL_INFO,
+                        "Miscellaneous, unknown type code %u",
+                        ty & MSG_TYPEMASK);
+           break;
+       }
+       break;
       default:
        col_add_fstr(p->cinfo, COL_INFO,
                     "Unknown category code %u, unknown type code %u",
       default:
        col_add_fstr(p->cinfo, COL_INFO,
                     "Unknown category code %u, unknown type code %u",
@@ -235,6 +263,22 @@ static void dissect_tripe(tvbuff_t *b, packet_info *p, proto_tree *t)
            goto huh;
        }
        break;
            goto huh;
        }
        break;
+      case MSG_MISC:
+       proto_tree_add_item(tt, hf_tripe_misc_type, b, 0, 1, FALSE);
+       switch (ty & MSG_TYPEMASK) {
+         case MISC_NOP:
+         case MISC_PING:
+         case MISC_PONG:
+           proto_tree_add_item(tt, hf_tripe_misc_payload,
+                               b, off, -1, FALSE);
+           goto done;
+         case MISC_EPING:
+         case MISC_EPONG:
+           goto ct;
+         default:
+           goto huh;
+       }
+       break;
       default:
        goto huh;
     }
       default:
        goto huh;
     }
@@ -316,6 +360,16 @@ void proto_register_tripe(void)
       FT_BYTES, BASE_NONE, 0, 0,
       "This is the message authentication code tag for the ciphertext."
     } },
       FT_BYTES, BASE_NONE, 0, 0,
       "This is the message authentication code tag for the ciphertext."
     } },
+    { &hf_tripe_misc_type, {
+      "Miscellaneous message type", "tripe.misc.type",
+      FT_UINT8, BASE_HEX, 0, MSG_TYPEMASK,
+      "This is the TrIPE miscellaneous message type subcode."
+    } },
+    { &hf_tripe_misc_payload, {
+      "Miscellaneous message type", "tripe.misc.payload",
+      FT_BYTES, BASE_NONE, 0, 0,
+      "This is the miscellaneous message payload."
+    } },
     { &hf_tripe_kx_type, {
       "Key-exchange message type", "tripe.kx.type",
       FT_UINT8, BASE_HEX, vs_kxtype, MSG_TYPEMASK,
     { &hf_tripe_kx_type, {
       "Key-exchange message type", "tripe.kx.type",
       FT_UINT8, BASE_HEX, vs_kxtype, MSG_TYPEMASK,
diff --git a/peer.c b/peer.c
index 402d08b556e120d7668d159184798236d747aa9b..15a12b7ecd903cac1f2a98cadd6e5aaa95fdd06c 100644 (file)
--- a/peer.c
+++ b/peer.c
@@ -53,6 +53,73 @@ const tunnel_ops *tunnels[] = {
 
 /*----- Main code ---------------------------------------------------------*/
 
 
 /*----- Main code ---------------------------------------------------------*/
 
+/* --- @p_pingtype@ --- *
+ *
+ * Arguments:  @unsigned msg@ = message type
+ *
+ * Returns:    String to describe the message.
+ */
+
+static const char *p_pingtype(unsigned msg)
+{
+  switch (msg & MSG_TYPEMASK) {
+    case MISC_PING:
+    case MISC_PONG:
+      return "transport-ping";
+    case MISC_EPING:
+    case MISC_EPONG:
+      return "encrypted-ping";
+    default:
+      abort();
+  }
+}
+
+/* --- @p_ponged@ --- *
+ *
+ * Arguments:  @peer *p@ = peer packet arrived from
+ *             @unsigned msg@ = message type
+ *             @buf *b@ = buffer containing payload
+ *
+ * Returns:    ---
+ *
+ * Use:                Processes a ping response.
+ */
+
+static void p_ponged(peer *p, unsigned msg, buf *b)
+{
+  uint32 id;
+  const octet *magic;
+  ping *pg;
+  
+  IF_TRACING(T_PEER, {
+    trace(T_PEER, "peer: received %s reply from %s",
+         p_pingtype(msg), p->spec.name);
+    trace_block(T_PACKET, "peer: ping contents", BBASE(b), BSZ(b));
+  })
+
+  if (buf_getu32(b, &id) ||
+      (magic = buf_get(b, sizeof(pg->magic))) == 0 ||
+      BLEFT(b)) {
+    a_warn("PEER %s malformed-%s", p->spec.name, p_pingtype(msg));
+    return;
+  }
+
+  for (pg = p->pings; pg; pg = pg->next) {
+    if (pg->id == id)
+      goto found;
+  }
+  a_warn("PEER %s unexpected-%s 0x%08lx",
+        p->spec.name, p_pingtype(msg), (unsigned long)id);
+  return;
+
+found:
+  if (memcmp(magic, pg->magic, sizeof(pg->magic)) != 0) {
+    a_warn("PEER %s corrupt-%s", p->spec.name, p_pingtype(msg));
+    return;
+  }
+  p_pingdone(pg, PING_OK);
+}
+
 /* --- @p_read@ --- *
  *
  * Arguments:  @int fd@ = file descriptor to read from
 /* --- @p_read@ --- *
  *
  * Arguments:  @int fd@ = file descriptor to read from
@@ -87,8 +154,8 @@ static void p_read(int fd, unsigned mode, void *v)
 
   assert(a.sa.sa_family == AF_INET);
   for (p = peers; p; p = p->next) {
 
   assert(a.sa.sa_family == AF_INET);
   for (p = peers; p; p = p->next) {
-    if (p->peer.sin.sin_addr.s_addr == a.sin.sin_addr.s_addr &&
-       p->peer.sin.sin_port == a.sin.sin_port)
+    if (p->spec.sa.sin.sin_addr.s_addr == a.sin.sin_addr.s_addr &&
+       p->spec.sa.sin.sin_port == a.sin.sin_port)
       goto found;
   }
   a_warn("PEER - unexpected-source INET %s %u",
       goto found;
   }
   a_warn("PEER - unexpected-source INET %s %u",
@@ -97,7 +164,7 @@ static void p_read(int fd, unsigned mode, void *v)
 
 found:
   IF_TRACING(T_PEER, {
 
 found:
   IF_TRACING(T_PEER, {
-    trace(T_PEER, "peer: packet received from `%s'", p->name);
+    trace(T_PEER, "peer: packet received from `%s'", p->spec.name);
     trace_block(T_PACKET, "peer: packet contents", buf_i, n);
   })
 
     trace_block(T_PACKET, "peer: packet contents", buf_i, n);
   })
 
@@ -108,20 +175,20 @@ found:
   p->st.sz_in += n;
   buf_init(&b, buf_i, n);
   if ((ch = buf_getbyte(&b)) < 0) {
   p->st.sz_in += n;
   buf_init(&b, buf_i, n);
   if ((ch = buf_getbyte(&b)) < 0) {
-    a_warn("PEER %s bad-packet no-type", p->name);
+    a_warn("PEER %s bad-packet no-type", p->spec.name);
     return;
   }
   switch (ch & MSG_CATMASK) {
     case MSG_PACKET:
       if (ch & MSG_TYPEMASK) {
     return;
   }
   switch (ch & MSG_CATMASK) {
     case MSG_PACKET:
       if (ch & MSG_TYPEMASK) {
-       a_warn("PEER %s bad-packet unknown-type 0x%02x", p->name, ch);
+       a_warn("PEER %s bad-packet unknown-type 0x%02x", p->spec.name, ch);
        p->st.n_reject++;
        return;
       }
       buf_init(&bb, buf_o, sizeof(buf_o));
       if (ksl_decrypt(&p->ks, MSG_PACKET, &b, &bb)) {
        p->st.n_reject++;
        p->st.n_reject++;
        return;
       }
       buf_init(&bb, buf_o, sizeof(buf_o));
       if (ksl_decrypt(&p->ks, MSG_PACKET, &b, &bb)) {
        p->st.n_reject++;
-       a_warn("PEER %s decrypt-failed", p->name);
+       a_warn("PEER %s decrypt-failed", p->spec.name);
        return;
       }
       if (BOK(&bb)) {
        return;
       }
       if (BOK(&bb)) {
@@ -130,15 +197,56 @@ found:
        p->t->ops->inject(p->t, &bb);
       } else {
        p->st.n_reject++;
        p->t->ops->inject(p->t, &bb);
       } else {
        p->st.n_reject++;
-       a_warn("PEER %s packet-build-failed", p->name);
+       a_warn("PEER %s packet-build-failed", p->spec.name);
       }
       break;
     case MSG_KEYEXCH:
       kx_message(&p->kx, ch & MSG_TYPEMASK, &b);
       break;
       }
       break;
     case MSG_KEYEXCH:
       kx_message(&p->kx, ch & MSG_TYPEMASK, &b);
       break;
+    case MSG_MISC:
+      switch (ch & MSG_TYPEMASK) {
+       case MISC_NOP:
+         T( trace(T_PEER, "peer: received NOP packet"); )
+         break;
+       case MISC_PING:
+         buf_put(p_txstart(p, MSG_MISC | MISC_PONG), BCUR(&b), BLEFT(&b));
+         p_txend(p);
+         break;        
+       case MISC_PONG:
+         p_ponged(p, MISC_PONG, &b);
+         break;
+       case MISC_EPING:
+         buf_init(&bb, buf_t, sizeof(buf_t));
+         if (ksl_decrypt(&p->ks, ch, &b, &bb)) {
+           p->st.n_reject++;
+           a_warn("PEER %s decrypt-failed", p->spec.name);
+           return;
+         }
+         if (BOK(&bb)) {
+           buf_flip(&bb);
+           if (ksl_encrypt(&p->ks, MSG_MISC | MISC_EPONG, &bb,
+                           p_txstart(p, MSG_MISC | MISC_EPONG)))
+             kx_start(&p->kx);
+           p_txend(p);
+         }
+         break;
+       case MISC_EPONG:
+         buf_init(&bb, buf_t, sizeof(buf_t));
+         if (ksl_decrypt(&p->ks, ch, &b, &bb)) {
+           p->st.n_reject++;
+           a_warn("PEER %s decrypt-failed", p->spec.name);
+           return;
+         }
+         if (BOK(&bb)) {
+           buf_flip(&bb);
+           p_ponged(p, MISC_EPONG, &bb);
+         }
+         break;
+      }
+      break;
     default:
       p->st.n_reject++;
     default:
       p->st.n_reject++;
-      a_warn("PEER %s bad-packet unknown-category 0x%02x", p->name, ch);
+      a_warn("PEER %s bad-packet unknown-category 0x%02x", p->spec.name, ch);
       break;
   }
 }
       break;
   }
 }
@@ -170,23 +278,157 @@ buf *p_txstart(peer *p, unsigned msg)
  * Use:                Sends a packet to the peer.
  */
 
  * Use:                Sends a packet to the peer.
  */
 
-void p_txend(peer *p)
+static void p_setkatimer(peer *);
+
+static int p_dotxend(peer *p)
 {
   if (!BOK(&p->b)) {
 {
   if (!BOK(&p->b)) {
-    a_warn("PEER %s packet-build-failed", p->name);
-    return;
+    a_warn("PEER %s packet-build-failed", p->spec.name);
+    return (0);
   }
   IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet",
                                 BBASE(&p->b), BLEN(&p->b)); )
   if (sendto(sock.fd, BBASE(&p->b), BLEN(&p->b),
   }
   IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet",
                                 BBASE(&p->b), BLEN(&p->b)); )
   if (sendto(sock.fd, BBASE(&p->b), BLEN(&p->b),
-            0, &p->peer.sa, p->sasz) < 0)
-    a_warn("PEER %s socket-write-error -- %s", p->name, strerror(errno));
-  else {
+            0, &p->spec.sa.sa, p->spec.sasz) < 0) {
+    a_warn("PEER %s socket-write-error -- %s",
+          p->spec.name, strerror(errno));
+    return (0);
+  } else {
     p->st.n_out++;
     p->st.sz_out += BLEN(&p->b);
     p->st.n_out++;
     p->st.sz_out += BLEN(&p->b);
+    return (1);
   }
 }
 
   }
 }
 
+void p_txend(peer *p)
+{
+  if (p_dotxend(p) && p->spec.t_ka) {
+    sel_rmtimer(&p->tka);
+    p_setkatimer(p);
+  }
+}
+
+/* --- @p_pingwrite@ --- *
+ *
+ * Arguments:  @ping *p@ = ping structure
+ *             @buf *b@ = buffer to write in
+ *
+ * Returns:    ---
+ *
+ * Use:                Fills in a ping structure and writes the packet payload.
+ */
+
+static void p_pingwrite(ping *p, buf *b)
+{
+  static uint32 seq = 0;
+
+  p->id = U32(seq++);
+  GR_FILL(&rand_global, p->magic, sizeof(p->magic));
+  buf_putu32(b, p->id);
+  buf_put(b, p->magic, sizeof(p->magic));
+}
+
+/* --- @p_pingdone@ --- *
+ *
+ * Arguments:  @ping *p@ = ping structure
+ *             @int rc@ = return code to pass on
+ *
+ * Returns:    ---
+ *
+ * Use:                Disposes of a ping structure, maybe sending a notification.
+ */
+
+void p_pingdone(ping *p, int rc)
+{
+  if (!p->p) return;
+  if (p->prev) p->prev->next = p->next;
+  else p->p->pings = p->next;
+  if (p->next) p->next->prev = p->prev;
+  if (rc != PING_TIMEOUT) sel_rmtimer(&p->t);
+  p->p = 0;
+  if (rc >= 0) p->func(rc, p->arg);
+}
+
+/* --- @p_pingtimeout@ --- *
+ *
+ * Arguments:  @struct timeval *now@ = the time now
+ *             @void *pv@ = pointer to ping block
+ *
+ * Returns:    ---
+ *
+ * Use:                Called when a ping times out.
+ */
+
+static void p_pingtimeout(struct timeval *now, void *pv)
+{
+  ping *p = pv;
+
+  T( trace(T_PEER, "peer: ping 0x%08lx timed out", (unsigned long)p->id); )
+  p_pingdone(p, PING_TIMEOUT);
+}
+
+/* --- @p_pingsend@ --- *
+ *
+ * Arguments:  @peer *p@ = destination peer
+ *             @ping *pg@ = structure to fill in
+ *             @unsigned type@ = message type
+ *             @unsigned long timeout@ = how long to wait before giving up
+ *             @void (*func)(int, void *)@ = callback function
+ *             @void *arg@ = argument for callback
+ *
+ * Returns:    Zero if successful, nonzero if it failed.
+ *
+ * Use:                Sends a ping to a peer.  Call @func@ with a nonzero argument
+ *             if we get an answer within the timeout, or zero if no answer.
+ */
+
+int p_pingsend(peer *p, ping *pg, unsigned type,
+              unsigned long timeout,
+              void (*func)(int, void *), void *arg)
+{
+  buf *b, bb;
+  struct timeval tv;
+
+  assert(!pg->p);
+
+  switch (type) {
+    case MISC_PING:
+      pg->msg = MISC_PONG;
+      b = p_txstart(p, MSG_MISC | MISC_PING);
+      p_pingwrite(pg, b);
+      p_txend(p);
+      break;
+    case MISC_EPING:
+      pg->msg = MISC_EPONG;
+      b = p_txstart(p, MSG_MISC | MISC_EPING);
+      buf_init(&bb, buf_t, sizeof(buf_t));
+      p_pingwrite(pg, &bb);
+      buf_flip(&bb);
+      if (ksl_encrypt(&p->ks, MSG_MISC | MISC_EPING, &bb, b))
+       kx_start(&p->kx);
+      if (!BOK(b))
+       return (-1);
+      p_txend(p);
+      break;
+    default:
+      abort();
+      break;
+  }
+
+  pg->next = p->pings;
+  pg->prev = 0;
+  pg->p = p;
+  pg->func = func;
+  pg->arg = arg;
+  p->pings = pg;
+  gettimeofday(&tv, 0);
+  tv.tv_sec += timeout;
+  sel_addtimer(&sel, &pg->t, &tv, p_pingtimeout, pg);
+  T( trace(T_PEER, "peer: send %s 0x%08lx to %s",
+          p_pingtype(type), (unsigned long)pg->id, p->spec.name); )
+  return (0);
+}
+
 /* --- @p_tun@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
 /* --- @p_tun@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
@@ -259,7 +501,7 @@ const char *p_ifname(peer *p) { return (p->t->ops->ifname(p->t)); }
  * Returns:    A pointer to the peer's address.
  */
 
  * Returns:    A pointer to the peer's address.
  */
 
-const addr *p_addr(peer *p) { return (&p->peer); }
+const addr *p_addr(peer *p) { return (&p->spec.sa); }
 
 /* --- @p_init@ --- *
  *
 
 /* --- @p_init@ --- *
  *
@@ -322,12 +564,47 @@ unsigned p_port(void)
   return (ntohs(a.sin.sin_port));
 }
 
   return (ntohs(a.sin.sin_port));
 }
 
+/* --- @p_keepalive@ --- *
+ *
+ * Arguments:  @struct timeval *now@ = the current time
+ *             @void *pv@ = peer to wake up
+ *
+ * Returns:    ---
+ *
+ * Use:                Sends a keepalive ping message to its peer.
+ */
+
+static void p_keepalive(struct timeval *now, void *pv)
+{
+  peer *p = pv;
+  p_txstart(p, MSG_MISC | MISC_NOP); p_dotxend(p);
+  T( trace(T_PEER, "peer: sent keepalive to %s", p->spec.name); )
+  p_setkatimer(p);
+}
+
+/* --- @p_setkatimer@ --- *
+ *
+ * Arguments:  @peer *p@ = peer to set
+ *
+ * Returns:    ---
+ *
+ * Use:                Resets the keepalive timer thing.
+ */
+
+static void p_setkatimer(peer *p)
+{
+  struct timeval tv;
+
+  if (!p->spec.t_ka)
+    return;
+  gettimeofday(&tv, 0);
+  tv.tv_sec += p->spec.t_ka;
+  sel_addtimer(&sel, &p->tka, &tv, p_keepalive, p);
+}
+
 /* --- @p_create@ --- *
  *
 /* --- @p_create@ --- *
  *
- * Arguments:  @const char *name@ = name for this peer
- *             @const tunnel_ops *tops@ = tunnel to use
- *             @struct sockaddr *sa@ = socket address of peer
- *             @size_t sz@ = size of socket address
+ * Arguments:  @peerspec *spec@ = information about this peer
  *
  * Returns:    Pointer to the peer block, or null if it failed.
  *
  *
  * Returns:    Pointer to the peer block, or null if it failed.
  *
@@ -335,46 +612,47 @@ unsigned p_port(void)
  *             by this point.
  */
 
  *             by this point.
  */
 
-peer *p_create(const char *name, const tunnel_ops *tops,
-              struct sockaddr *sa, size_t sz)
+peer *p_create(peerspec *spec)
 {
   peer *p = CREATE(peer);
 
 {
   peer *p = CREATE(peer);
 
-  T( trace(T_PEER, "peer: creating new peer `%s'", name); )
-  p->name = xstrdup(name);
+  T( trace(T_PEER, "peer: creating new peer `%s'", spec->name); )
+  p->spec = *spec;
+  p->spec.name = xstrdup(spec->name);
   p->ks = 0;
   p->prev = 0;
   p->ks = 0;
   p->prev = 0;
-  memcpy(&p->peer.sa, sa, sz);
-  p->sasz = sz;
   memset(&p->st, 0, sizeof(stats));
   p->st.t_start = time(0);
   memset(&p->st, 0, sizeof(stats));
   p->st.t_start = time(0);
-  if (kx_init(&p->kx, p, &p->ks))
+  if ((p->t = spec->tops->create(p)) == 0)
     goto tidy_0;
     goto tidy_0;
-  if ((p->t = tops->create(p)) == 0)
+  p_setkatimer(p);
+  if (kx_init(&p->kx, p, &p->ks))
     goto tidy_1;
   p->next = peers;
   if (peers)
     peers->prev = p;
   peers = p;
     goto tidy_1;
   p->next = peers;
   if (peers)
     peers->prev = p;
   peers = p;
-  switch (p->peer.sa.sa_family) {
+  switch (p->spec.sa.sa.sa_family) {
     case AF_INET:
       a_notify("ADD %s %s INET %s %u",
     case AF_INET:
       a_notify("ADD %s %s INET %s %u",
-              name,
+              spec->name,
               p->t->ops->ifname(p->t),
               p->t->ops->ifname(p->t),
-              inet_ntoa(p->peer.sin.sin_addr),
-              (unsigned)ntohs(p->peer.sin.sin_port));
+              inet_ntoa(p->spec.sa.sin.sin_addr),
+              (unsigned)ntohs(p->spec.sa.sin.sin_port));
       break;
     default:
       break;
     default:
-      a_notify("ADD %s %s UNKNOWN", name, p->t->ops->ifname(p->t));
+      a_notify("ADD %s %s UNKNOWN", spec->name, p->t->ops->ifname(p->t));
       break;
   }
       break;
   }
-  a_notify("KXSTART %s", name);                /* Couldn't tell anyone before */
+  a_notify("KXSTART %s", spec->name);  /* Couldn't tell anyone before */
   return (p);
 
 tidy_1:
   return (p);
 
 tidy_1:
-  kx_free(&p->kx);
+  if (spec->t_ka)
+    sel_rmtimer(&p->tka);
+  p->t->ops->destroy(p->t);
 tidy_0:
 tidy_0:
-  xfree(p->name);
+  xfree(p->spec.name);
   DESTROY(p);
   return (0);
 }
   DESTROY(p);
   return (0);
 }
@@ -386,7 +664,7 @@ tidy_0:
  * Returns:    A pointer to the peer's name.
  */
 
  * Returns:    A pointer to the peer's name.
  */
 
-const char *p_name(peer *p) { return (p->name); }
+const char *p_name(peer *p) { return (p->spec.name); }
 
 /* --- @p_find@ --- *
  *
 
 /* --- @p_find@ --- *
  *
@@ -401,7 +679,7 @@ peer *p_find(const char *name)
 {
   peer *p;
   for (p = peers; p; p = p->next) {
 {
   peer *p;
   for (p = peers; p; p = p->next) {
-    if (strcmp(name, p->name) == 0)
+    if (strcmp(name, p->spec.name) == 0)
       return (p);
   }
   return (0);  
       return (p);
   }
   return (0);  
@@ -418,12 +696,20 @@ peer *p_find(const char *name)
 
 void p_destroy(peer *p)
 {
 
 void p_destroy(peer *p)
 {
-  T( trace(T_PEER, "peer: destroying peer `%s'", p->name); )
-  a_notify("KILL %s", p->name);
+  ping *pg, *ppg;
+
+  T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); )
+  a_notify("KILL %s", p->spec.name);
   ksl_free(&p->ks);
   kx_free(&p->kx);
   p->t->ops->destroy(p->t);
   ksl_free(&p->ks);
   kx_free(&p->kx);
   p->t->ops->destroy(p->t);
-  xfree(p->name);
+  if (p->spec.t_ka)
+    sel_rmtimer(&p->tka);
+  xfree(p->spec.name);
+  for (pg = p->pings; pg; pg = ppg) {
+    ppg = pg->next;
+    p_pingdone(pg, PING_PEERDIED);
+  }
   if (p->next)
     p->next->prev = p->prev;
   if (p->prev)
   if (p->next)
     p->next->prev = p->prev;
   if (p->prev)
index 1961785aa550ba57737eba969e376a6bf93a7cb0..ebf7a71d9fe8d60e0d4343be0e398a76bbd73e3f 100644 (file)
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
 /* -*-c-*-
  *
- * $Id: tripe-protocol.h,v 1.2 2004/04/08 01:36:17 mdw Exp $
+ * $Id$
  *
  * Protocol definition for TrIPE
  *
  *
  * Protocol definition for TrIPE
  *
 #define KX_SWITCHOK 5u
 #define KX_NMSG 6u
 
 #define KX_SWITCHOK 5u
 #define KX_NMSG 6u
 
+/* --- Miscellaneous packets --- */
+
+#define MSG_MISC 0x20
+
+#define MISC_NOP 0u                    /* Do nothing; ignore me */
+#define MISC_PING 1u                   /* Transport-level ping */
+#define MISC_PONG 2u                   /* Transport-level ping response */
+#define MISC_EPING 3u                  /* Encrypted ping */
+#define MISC_EPONG 4u                  /* Encrypted ping response */
+
 /* --- Symmetric encryption and keysets --- *
  *
  * Packets consist of an 80-bit MAC, a 32-bit sequence number, and the
  * encrypted payload.
  *
  * The plaintext is encrypted using Blowfish in CBC mode with ciphertext
 /* --- Symmetric encryption and keysets --- *
  *
  * Packets consist of an 80-bit MAC, a 32-bit sequence number, and the
  * encrypted payload.
  *
  * The plaintext is encrypted using Blowfish in CBC mode with ciphertext
- * stealing (as described in [Schneier].  The initialization vector is
+ * stealing (as described in [Schneier]).  The initialization vector is
  * selected randomly, and prepended to the actual ciphertext.
  *
  * The MAC is computed using the HMAC construction with RIPEMD160 over the
  * selected randomly, and prepended to the actual ciphertext.
  *
  * The MAC is computed using the HMAC construction with RIPEMD160 over the
diff --git a/tripe.h b/tripe.h
index 9cf7abf3103705250669f86f9b68d1346acb5b7e..002b3735b61e41a949a56b1820f8b5d7e534ee1c 100644 (file)
--- a/tripe.h
+++ b/tripe.h
@@ -80,6 +80,7 @@
 #include <mLib/str.h>
 #include <mLib/sub.h>
 #include <mLib/trace.h>
 #include <mLib/str.h>
 #include <mLib/sub.h>
 #include <mLib/trace.h>
+#include <mLib/tv.h>
 
 #include <catacomb/buf.h>
 
 
 #include <catacomb/buf.h>
 
@@ -292,18 +293,45 @@ typedef struct stats {
  * The main structure which glues everything else together.
  */
 
  * The main structure which glues everything else together.
  */
 
+typedef struct peerspec {
+  char *name;                          /* Peer's name */
+  const tunnel_ops *tops;              /* Tunnel operations */
+  unsigned long t_ka;                  /* Keep alive interval */
+  addr sa;                             /* Socket address to speak to */
+  size_t sasz;                         /* Socket address size */
+} peerspec;
+
 typedef struct peer {
   struct peer *next, *prev;            /* Links to next and previous */
 typedef struct peer {
   struct peer *next, *prev;            /* Links to next and previous */
-  char *name;                          /* Name of this peer */
+  struct ping *pings;                  /* Pings we're waiting for */
+  peerspec spec;                       /* Specifications for this peer */
   tunnel *t;                           /* Tunnel for local packets */
   keyset *ks;                          /* List head for keysets */
   buf b;                               /* Buffer for sending packets */
   tunnel *t;                           /* Tunnel for local packets */
   keyset *ks;                          /* List head for keysets */
   buf b;                               /* Buffer for sending packets */
-  addr peer;                           /* Peer socket address */
-  size_t sasz;                         /* Socket address size */
   stats st;                            /* Statistics */
   keyexch kx;                          /* Key exchange protocol block */
   stats st;                            /* Statistics */
   keyexch kx;                          /* Key exchange protocol block */
+  sel_timer tka;                       /* Timer for keepalives */
 } peer;
 
 } peer;
 
+typedef struct ping {
+  struct ping *next, *prev;            /* Links to next and previous */
+  peer *p;                             /* Peer so we can free it */
+  unsigned msg;                                /* Kind of response expected */
+  uint32 id;                           /* Id so we can recognize response */
+  octet magic[32];                     /* Some random data */
+  sel_timer t;                         /* Timeout for ping */
+  void (*func)(int /*rc*/, void */*arg*/); /* Function to call when done */
+  void *arg;                           /* Argument for callback */
+} ping;
+
+enum {
+  PING_NONOTIFY = -1,
+  PING_OK = 0,
+  PING_TIMEOUT,
+  PING_PEERDIED,
+  PING_MAX
+};
+
 /* --- Admin structure --- */
 
 #define OBUFSZ 16384u
 /* --- Admin structure --- */
 
 #define OBUFSZ 16384u
@@ -320,16 +348,15 @@ typedef struct admin {
 #ifndef NTRACE
   unsigned seq;                                /* Sequence number for tracing */
 #endif
 #ifndef NTRACE
   unsigned seq;                                /* Sequence number for tracing */
 #endif
-  char *pname;                         /* Peer name to create */
-  char *paddr;                         /* Address string to resolve */
   obuf *o_head, *o_tail;               /* Output buffer list */
   selbuf b;                            /* Line buffer for commands */
   sel_file w;                          /* Selector for write buffering */
   obuf *o_head, *o_tail;               /* Output buffer list */
   selbuf b;                            /* Line buffer for commands */
   sel_file w;                          /* Selector for write buffering */
+  peerspec peer;                       /* Peer pending creation */
+  char *paddr;                         /* Hostname to be resolved */
   bres_client r;                       /* Background resolver task */
   sel_timer t;                         /* Timer for resolver */
   bres_client r;                       /* Background resolver task */
   sel_timer t;                         /* Timer for resolver */
-  const tunnel_ops *tops;              /* Tunnel to use */
-  addr peer;                           /* Address to set */
-  size_t sasz;                         /* Size of the address */
+  ping ping;                           /* Ping pending response */
+  struct timeval pingtime;             /* Time last ping was sent */
 } admin;
 
 #define AF_DEAD 1u                     /* Destroy this admin block */
 } admin;
 
 #define AF_DEAD 1u                     /* Destroy this admin block */
@@ -736,6 +763,37 @@ extern buf *p_txstart(peer */*p*/, unsigned /*msg*/);
 
 extern void p_txend(peer */*p*/);
 
 
 extern void p_txend(peer */*p*/);
 
+/* --- @p_pingsend@ --- *
+ *
+ * Arguments:  @peer *p@ = destination peer
+ *             @ping *pg@ = structure to fill in
+ *             @unsigned type@ = message type
+ *             @unsigned long timeout@ = how long to wait before giving up
+ *             @void (*func)(int, void *)@ = callback function
+ *             @void *arg@ = argument for callback
+ *
+ * Returns:    Zero if successful, nonzero if it failed.
+ *
+ * Use:                Sends a ping to a peer.  Call @func@ with a nonzero argument
+ *             if we get an answer within the timeout, or zero if no answer.
+ */
+
+extern int p_pingsend(peer */*p*/, ping */*pg*/, unsigned /*type*/,
+                     unsigned long /*timeout*/,
+                     void (*/*func*/)(int, void *), void */*arg*/);
+
+/* --- @p_pingdone@ --- *
+ *
+ * Arguments:  @ping *p@ = ping structure
+ *             @int rc@ = return code to pass on
+ *
+ * Returns:    ---
+ *
+ * Use:                Disposes of a ping structure, maybe sending a notification.
+ */
+
+extern void p_pingdone(ping */*p*/, int /*rc*/);
+
 /* --- @p_tun@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
 /* --- @p_tun@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
@@ -809,10 +867,7 @@ unsigned p_port(void);
 
 /* --- @p_create@ --- *
  *
 
 /* --- @p_create@ --- *
  *
- * Arguments:  @const char *name@ = name for this peer
- *             @const tunnel_ops *tops@ = tunnel to use
- *             @struct sockaddr *sa@ = socket address of peer
- *             @size_t sz@ = size of socket address
+ * Arguments:  @peerspec *spec@ = information about this peer
  *
  * Returns:    Pointer to the peer block, or null if it failed.
  *
  *
  * Returns:    Pointer to the peer block, or null if it failed.
  *
@@ -820,8 +875,7 @@ unsigned p_port(void);
  *             by this point.
  */
 
  *             by this point.
  */
 
-extern peer *p_create(const char */*name*/, const tunnel_ops */*tops*/,
-                     struct sockaddr */*sa*/, size_t /*sz*/);
+extern peer *p_create(peerspec */*spec*/);
 
 /* --- @p_name@ --- *
  *
 
 /* --- @p_name@ --- *
  *