#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 --- *
*
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 = \
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
;; 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.
;;;
;;; 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.
;; 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.
;;;
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).
*['ADD'] +
_kwopts(kw, ['tunnel', 'keepalive',
'key', 'priv', 'cork',
- 'mobile', 'knock']) +
+ 'mobile', 'knock',
+ 'ephemeral']) +
[peer] +
list(addr)))
def addr(me, peer):
{
close(sock.fd);
unlink(sockname);
- FOREACH_PEER(p, { p_destroy(p); });
+ FOREACH_PEER(p, { p_destroy(p, 1); });
ps_quit();
exit(0);
}
})
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);
OPTARG("-knock", arg, {
if (add->peer.knock) xfree(add->peer.knock);
add->peer.knock = xstrdup(arg);
+ add->peer.f |= PSF_EPHEM;
})
});
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);
}
peer *p;
if ((p = a_findpeer(a, av[0])) != 0) {
- p_destroy(p);
+ p_destroy(p, 1);
a_ok(a);
}
}
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:
/* --- @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);
AT_CLEANUP
###--------------------------------------------------------------------------
-### Knock.
+### Knock and bye.
AT_SETUP([server knock])
AT_KEYWORDS([knock])
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])
AT_CHECK([TRIPECTL -dalice FORCEKX bob])
AT_CHECK([TRIPECTL -dbob FORCEKX alice])
])
+
AT_CHECK([TRIPECTL -dbob KILL alice])
AT_CHECK([TRIPECTL -dalice LIST],, [])
])
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
.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.
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
.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
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 {
/* --- @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@ --- *
*
.RB [ \-priv
.IR tag ]
.RB [ \-mobile ]
+.RB [ \-ephemeral ]
.RB [ \-tunnel
.IR driver ]
.I address
.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
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)
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)
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.
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():
"""
addr = cr.parent.switch()
if addr is None:
raise T.TripeJobError('connect-timeout')
- addpeer(peer, addr)
+ addpeer(peer, addr, True)
finally:
del chalmap[chal]
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)
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 } },
}
}
}