chiark / gitweb /
Add notion of `ephemeral' associations and a goodbye protocol.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 5 Sep 2017 21:26:51 +0000 (22:26 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 25 Jan 2019 12:07:16 +0000 (12:07 +0000)
When TrIPE kills an ephemeral peer, it sends a `bye' message to the
peer.  When TrIPE receives `bye' from an ephemeral peer, it kills the
peer (without sending `bye' back).

Augment the `connect' service to set appropriate flags when adding
peers, and the Wireshark dissector to understand the new message.

13 files changed:
common/protocol.h
mon/tripemon.in
peerdb/peers.in
peerdb/peers.in.5.in
py/tripe.py.in
server/admin.c
server/peer.c
server/tests.at
server/tripe-admin.5.in
server/tripe.h
svc/connect.8.in
svc/connect.in
wireshark/tripe.lua

index 2416afcff2cd84135dfa4f2f48d34019f0851ebc..e40cf177c142c5fcf22430f0396a0ed3ab1c3a89 100644 (file)
@@ -79,6 +79,7 @@
 #define MISC_EPING 3u                  /* Encrypted ping */
 #define MISC_EPONG 4u                  /* Encrypted ping response */
 #define MISC_GREET 5u                  /* A greeting from a NATed peer */
+#define MISC_BYE 6u                    /* Departure notification */
 
 /* --- Symmetric encryption and keysets --- *
  *
index 0d15474ca335d82b8e3001eb586ab09d79dfc606..8666575a1fba5a6c5b182688341cd0638458b1b5 100644 (file)
@@ -1093,6 +1093,9 @@ class AddPeerDialog (MyDialog):
     me.c_mobile = G.CheckButton('Mobile')
     table.pack(me.c_mobile, newlinep = True, width = 4, xopt = G.FILL)
 
+    me.c_ephem = G.CheckButton('Ephemeral')
+    table.pack(me.c_ephem, newlinep = True, width = 4, xopt = G.FILL)
+
     me.c_peerkey, me.e_peerkey = \
       optional_entry('Peer key tag', r'^[^.:\s]+$', 16)
     me.c_privkey, me.e_privkey = \
@@ -1115,6 +1118,7 @@ class AddPeerDialog (MyDialog):
                   tunnel = t and me.tuns[t] or None,
                   cork = me.c_cork.get_active() or None,
                   mobile = me.c_mobile.get_active() or None,
+                  ephemeral = me.c_ephem.get_active() or None,
                   key = (me.c_peerkey.get_active() and
                          me.e_peerkey.get_text() or None),
                   priv = (me.c_privkey.get_active() and
index 3fb23803c8fc0e79b24d39a8d3e9193f04741bdf..75c039d9f5da51c720b0cd7f7af5cc7d27b1552f 100644 (file)
@@ -57,6 +57,10 @@ host = override-me
 ;; the remote peer.
 peer = INET $[$(host)] $(port)
 
+;; ephemeral: whether to send the peer a disconnection notification, or
+;; react to one from the peer.
+ephemeral = nil
+
 ;;;--------------------------------------------------------------------------
 ;;; Temporary association defaults.
 ;;;
@@ -81,21 +85,28 @@ retries = 5
 ;;; The parameters here affect peers to whom dynamic connections are made.
 ;;; The user and connect parameters probably need customizing.
 
-[@KNOCK]
+[@EPHEMERAL]
 @inherit = @ACTIVE, @WATCH
 
-;; keepalive: how often to send NOP packets to keep the connection alive, at
-;; least in the minds of intermediate stateful firewalls and NAT routers.
-keepalive = 2m
+;; ephemeral: whether to send the peer a disconnection notification, or
+;; react to one from the peer.
+ephemeral = t
 
 ;; every: interval for checking that this connection is alive.
 every = 30s
 
+[@KNOCK]
+@inherit = @EPHEMERAL
+
+;; keepalive: how often to send NOP packets to keep the connection alive, at
+;; least in the minds of intermediate stateful firewalls and NAT routers.
+keepalive = 2m
+
 ;; knock: peer-name string to send to the peer.
 knock = $(myhost)
 
 [@DYNAMIC]
-@inherit = @ACTIVE, @WATCH
+@inherit = @EPHEMERAL
 
 ;; cork: whether to wait for a key-exchange packet from the peer before
 ;; sending one of our own.
@@ -115,9 +126,6 @@ disconnect = ssh -q $(ssh-user)@$[$(host)] goodbye
 ;; least in the minds of intermediate stateful firewalls and NAT routers.
 keepalive = 2m
 
-;; every: interval for checking that this connection is alive.
-every = 30s
-
 ;;;--------------------------------------------------------------------------
 ;;; Passive-peers defaults.
 ;;;
index 89eb77769cb325a223b67e9c6227dbef3f228a75..ac27b49cabd5c819363e958d35ff2a037b7f28ce 100644 (file)
@@ -175,6 +175,12 @@ Don't initiate immediate key exchange.  Used by
 Shell command for closing down connection to this peer.  Used by
 .BR connect (8).
 .TP
+.B ephemeral
+Mark the peer as ephemeral: see
+.BR tripe-admin (5)
+for what this means.  Used by
+.BR connect (8).
+.TP
 .B every
 Interval for checking that the peer is still alive and well.  Used by
 .BR connect (8).
index 63ae0afc4c72ed0f4be4f828c4fe81bcaa4e4de4..0126dc586991c1357f71e0679b85c3c34cba0b72 100644 (file)
@@ -838,7 +838,8 @@ class TripeCommandDispatcher (TripeConnection):
                               *['ADD'] +
                               _kwopts(kw, ['tunnel', 'keepalive',
                                            'key', 'priv', 'cork',
-                                           'mobile', 'knock']) +
+                                           'mobile', 'knock',
+                                           'ephemeral']) +
                               [peer] +
                               list(addr)))
   def addr(me, peer):
index 886c672c9c30071b46cce2bb48be8a3f5573ce3b..54883afa448cf15926c78c5e5d0c8748ea0276d1 100644 (file)
@@ -551,7 +551,7 @@ void a_quit(void)
 {
   close(sock.fd);
   unlink(sockname);
-  FOREACH_PEER(p, { p_destroy(p); });
+  FOREACH_PEER(p, { p_destroy(p, 1); });
   ps_quit();
   exit(0);
 }
@@ -1285,11 +1285,12 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
     })
     OPTTIME("-keepalive", t, { add->peer.t_ka = t; })
     OPT("-cork", { add->peer.f |= KXF_CORK; })
+    OPT("-ephemeral", { add->peer.f |= PSF_EPHEM; })
     OPTARG("-key", arg, {
       if (add->peer.tag) xfree(add->peer.tag);
       add->peer.tag = xstrdup(arg);
     })
-    OPT("-mobile", { add->peer.f |= PSF_MOBILE; })
+    OPT("-mobile", { add->peer.f |= PSF_MOBILE | PSF_EPHEM; })
     OPTARG("-priv", arg, {
       if (add->peer.privtag) xfree(add->peer.privtag);
       add->peer.privtag = xstrdup(arg);
@@ -1297,6 +1298,7 @@ static void acmd_add(admin *a, unsigned ac, char *av[])
     OPTARG("-knock", arg, {
       if (add->peer.knock) xfree(add->peer.knock);
       add->peer.knock = xstrdup(arg);
+      add->peer.f |= PSF_EPHEM;
     })
   });
 
@@ -1860,6 +1862,7 @@ static void acmd_peerinfo(admin *a, unsigned ac, char *av[])
     a_info(a, "keepalive=%lu", ps->t_ka, A_END);
     a_info(a, "corked=%s", BOOL(p->kx.f&KXF_CORK),
           "mobile=%s", BOOL(ps->f&PSF_MOBILE),
+          "ephemeral=%s", BOOL(ps->f&PSF_EPHEM),
           A_END);
     a_ok(a);
   }
@@ -1915,7 +1918,7 @@ static void acmd_kill(admin *a, unsigned ac, char *av[])
   peer *p;
 
   if ((p = a_findpeer(a, av[0])) != 0) {
-    p_destroy(p);
+    p_destroy(p, 1);
     a_ok(a);
   }
 }
index f600f99e8531794845427c65328a2b49fd0e99bd..606b2038d0c76f93a64af77d539566786de0fe7f 100644 (file)
@@ -454,6 +454,16 @@ static void p_read(int fd, unsigned mode, void *v)
            p_ponged(p, MISC_EPONG, &bb);
          }
          break;
+       case MISC_BYE:
+         buf_init(&bb, buf_t, sizeof(buf_t));
+         if (p_decrypt(&p, &a, n, ch, &b, &bb)) return;
+         if (!(p->spec.f&PSF_EPHEM)) return;
+         if (BOK(&bb)) {
+           buf_flip(&bb);
+           if (BSZ(&bb)) return;
+           p_destroy(p, 0);
+         }
+         break;
       }
       break;
     default:
@@ -1044,17 +1054,28 @@ peer *p_find(const char *name)
 /* --- @p_destroy@ --- *
  *
  * Arguments:  @peer *p@ = pointer to a peer
+ *             @int bye@ = say goodbye to the peer?
  *
  * Returns:    ---
  *
  * Use:                Destroys a peer.
  */
 
-void p_destroy(peer *p)
+void p_destroy(peer *p, int bye)
 {
   ping *pg, *ppg;
+  buf *b, bb;
 
   T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); )
+
+  if (bye && (p->spec.f&PSF_EPHEM)) {
+    b = p_txstart(p, MSG_MISC | MISC_BYE);
+    buf_init(&bb, buf_t, sizeof(buf_t));
+    assert(BOK(&bb)); buf_flip(&bb);
+    p_encrypt(p, MSG_MISC | MISC_BYE, &bb, b);
+    p_txend(p);
+  }
+
   a_notify("KILL", "%s", p->spec.name, A_END);
   ksl_free(&p->ks);
   kx_free(&p->kx);
index 8bceb147703d697dfe8ce0f269d6234955e0a1e1..08bfb135cd3219fb5bf2faebd2b9ce04c735aadd 100644 (file)
@@ -752,7 +752,7 @@ WITH_TRIPE(, [
 AT_CLEANUP
 
 ###--------------------------------------------------------------------------
-### Knock.
+### Knock and bye.
 
 AT_SETUP([server knock])
 AT_KEYWORDS([knock])
@@ -786,7 +786,7 @@ WITH_2TRIPES([alice], [bob], [-nslip], [-talice], [-tbob], [
     AT_CHECK([cat knock-addr],, [INET 127.0.0.1 5311[]nl])
 
     AWAIT_KXDONE([alice], [alice], [bob], [bob], [
-      AT_CHECK([TRIPECTL -dalice ADD bob INET 127.0.0.1 5311])
+      AT_CHECK([TRIPECTL -dalice ADD -ephemeral bob INET 127.0.0.1 5311])
     ])
 
     COMMS_EPING([alice], [alice], [bob], [bob])
@@ -798,6 +798,7 @@ WITH_2TRIPES([alice], [bob], [-nslip], [-talice], [-tbob], [
       AT_CHECK([TRIPECTL -dalice FORCEKX bob])
       AT_CHECK([TRIPECTL -dbob FORCEKX alice])
     ])
+
     AT_CHECK([TRIPECTL -dbob KILL alice])
     AT_CHECK([TRIPECTL -dalice LIST],, [])
   ])
index a340f57f75997064f3ccdcc02c303e9efc24525a..c81dc111b84e1d4677e8633da27e7f26596a89aa 100644 (file)
@@ -332,6 +332,21 @@ Run the command in the background, using the given
 Don't send an immediate challenge to the peer; instead, wait until it
 sends us something before responding.
 .TP
+.B "\-ephemeral"
+The association with the peer is not intended to persist indefinitely.
+If a peer marked as ephemeral is killed, or the
+.BR tripe (8)
+daemon is shut down, send a
+.B bye
+packet to the peer so that it forgets about us; if a peer marked as
+ephemeral sends us a
+.B bye
+packet then it is killed (but in this case no further
+.B bye
+packet is sent).  Peers not marked as ephemeral exhibit neither of these
+behaviours; each peer must have the other marked as ephemeral for the
+association to be fully torn down if either end kills the other.
+.TP
 .BI "\-keepalive " time
 Send a no-op packet if we've not sent a packet to the peer in the last
 .I time
@@ -370,7 +385,8 @@ emits a
 .B KNOCK
 notification stating the peer's (claimed) name and address.  The server
 will already have verified that the sender is using the peer's private
-key by this point.
+key by this point.  This option implies
+.BR \-ephemeral .
 .TP
 .B "\-mobile"
 The peer is a mobile device, and is likely to change address rapidly.
@@ -380,7 +396,8 @@ peers, however, it will attempt to decrypt the packet using their keys,
 and if one succeeds, the server will update its idea of the peer's
 address and emit an
 .B NEWADDR
-notification.
+notification.  This option implies
+.BR \-ephemeral .
 .TP
 .BI "\-priv " tag
 Use the private key
@@ -639,6 +656,14 @@ or
 .B nil
 depending on whether or not (respectively) the peer is expected to
 change its address unpredictably.
+.TP
+.B ephemeral
+Either
+.B t
+or
+.B nil
+depending on whether the association with the peer is expected to be
+temporary or persistent (respectively).
 .RE
 .SP
 .BI "PING \fR[" options "\fR] " peer
index 2024627a10abde931bc1dabc15137fb89875ae35..a2907be1a6bff9a030d9dd4879c8eed1e96e14e6 100644 (file)
@@ -617,6 +617,7 @@ typedef struct peerspec {
   unsigned f;                          /* Flags for the peer */
 #define PSF_KXMASK 255u                        /*   Key-exchange flags to set */
 #define PSF_MOBILE 256u                        /*   Address may change rapidly */
+#define PSF_EPHEM 512u                 /*   Association is ephemeral */
 } peerspec;
 
 typedef struct peer_byname {
@@ -1693,13 +1694,14 @@ extern peer *p_find(const char */*name*/);
 /* --- @p_destroy@ --- *
  *
  * Arguments:  @peer *p@ = pointer to a peer
+ *             @int bye@ = say goodbye to the peer?
  *
  * Returns:    ---
  *
  * Use:                Destroys a peer.
  */
 
-extern void p_destroy(peer */*p*/);
+extern void p_destroy(peer */*p*/, int /*bye*/);
 
 /* --- @FOREACH_PEER@ --- *
  *
index 5a11164763eda16df987c994775121f24e0ea2d8..1e4e2d25e7d3315a9cdc8512dadcf1b8b82a7f5f 100644 (file)
@@ -467,6 +467,7 @@ The service will submit the command
 .RB [ \-priv
 .IR tag ]
 .RB [ \-mobile ]
+.RB [ \-ephemeral ]
 .RB [ \-tunnel
 .IR driver ]
 .I address
@@ -533,6 +534,19 @@ to the
 .B tunnel
 key.
 .hP \*o
+The option
+.B \-ephemeral
+is provided if the peer's database record assigns the
+.B ephemeral
+key one of the values
+.BR t ,
+.BR true ,
+.BR y ,
+.BR yes,
+or
+.BR on ;
+or if it is absent.
+.hP \*o
 The
 .I address
 is the value assigned to the
index fe8d73b98e592862cabe2f8ee31b4722d871a126..99dbd8af1ca8340e943999b7bdafa9f5ac5a3685 100644 (file)
@@ -717,11 +717,12 @@ def disownpeer(peer):
     T.Coroutine(run_ifupdown, name = 'ifdown %s' % peer.name) \
         .switch('ifdown', peer)
 
-def addpeer(peer, addr):
+def addpeer(peer, addr, ephemp):
   """
   Process a connect request from a new peer PEER on address ADDR.
 
-  Any existing peer with this name is disconnected from the server.
+  Any existing peer with this name is disconnected from the server.  EPHEMP
+  is the default ephemeral-ness state for the new peer.
   """
   if peer.name in S.list():
     S.kill(peer.name)
@@ -734,6 +735,8 @@ def addpeer(peer, addr):
           mobile = peer.get('mobile', filter = boolean, default = False),
           knock = peer.get('knock', default = None),
           cork = peer.get('cork', filter = boolean, default = False),
+          ephemeral = peer.get('ephemeral', filter = boolean,
+                               default = ephemp),
           *addr)
   except T.TripeError, exc:
     raise T.TripeJobError(*exc.args)
@@ -778,7 +781,7 @@ def notify(_, code, *rest):
       S.warn(['connect', 'knock-tag-mismatch',
               'peer', pname, 'public-key-tag', ktag])
       return
-    T.spawn(addpeer, p, rest[1:])
+    T.spawn(addpeer, p, rest[1:], True)
 
 ###--------------------------------------------------------------------------
 ### Command implementation.
@@ -813,7 +816,7 @@ def cmd_active(name):
   addr = peer.get('peer')
   if addr == 'PASSIVE':
     raise T.TripeJobError('passive-peer', name)
-  addpeer(peer, M.split(addr, quotep = True)[0])
+  addpeer(peer, M.split(addr, quotep = True)[0], True)
 
 def cmd_listactive():
   """
@@ -875,7 +878,7 @@ def cmd_passive(*args):
     addr = cr.parent.switch()
     if addr is None:
       raise T.TripeJobError('connect-timeout')
-    addpeer(peer, addr)
+    addpeer(peer, addr, True)
   finally:
     del chalmap[chal]
 
@@ -911,7 +914,7 @@ def setup():
     for name in M.split(autos)[0]:
       try:
         peer = Peer(name, cdb)
-        addpeer(peer, M.split(peer.get('peer'), quotep = True)[0])
+        addpeer(peer, M.split(peer.get('peer'), quotep = True)[0], False)
       except T.TripeJobError, err:
         S.warn('connect', 'auto-add-failed', name, *err.args)
 
index aab8e7983832b8dfc56dc2397228d0072f3e9e48..5c6d7ba94ee54ce02a073b7fca49a68f41541359 100644 (file)
@@ -408,6 +408,8 @@ local PKTINFO = {
              dissect = { dissect_misc_ciphertext } },
       [5] = { label = "MISC_GREET", info = "greeting",
              dissect = { dissect_misc_payload } },
+      [6] = { label = "MISC_BYE", info = "disconnect notification",
+             dissect = { dissect_misc_ciphertext } },
     }
   }
 }