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:
#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_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 --- *
*
/* --- Symmetric encryption and keysets --- *
*
me.c_mobile = G.CheckButton('Mobile')
table.pack(me.c_mobile, newlinep = True, width = 4, xopt = G.FILL)
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 = \
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,
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
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)
;; 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.
;;;
;;;--------------------------------------------------------------------------
;;; Temporary association defaults.
;;;
;;; The parameters here affect peers to whom dynamic connections are made.
;;; The user and connect parameters probably need customizing.
;;; The parameters here affect peers to whom dynamic connections are made.
;;; The user and connect parameters probably need customizing.
@inherit = @ACTIVE, @WATCH
@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
;; 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]
;; knock: peer-name string to send to the peer.
knock = $(myhost)
[@DYNAMIC]
-@inherit = @ACTIVE, @WATCH
;; cork: whether to wait for a key-exchange packet from the peer before
;; sending one of our own.
;; 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
;; 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.
;;;
;;;--------------------------------------------------------------------------
;;; Passive-peers defaults.
;;;
Shell command for closing down connection to this peer. Used by
.BR connect (8).
.TP
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).
.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',
*['ADD'] +
_kwopts(kw, ['tunnel', 'keepalive',
'key', 'priv', 'cork',
+ 'mobile', 'knock',
+ 'ephemeral']) +
[peer] +
list(addr)))
def addr(me, peer):
[peer] +
list(addr)))
def addr(me, peer):
{
close(sock.fd);
unlink(sockname);
{
close(sock.fd);
unlink(sockname);
- FOREACH_PEER(p, { p_destroy(p); });
+ FOREACH_PEER(p, { p_destroy(p, 1); });
})
OPTTIME("-keepalive", t, { add->peer.t_ka = t; })
OPT("-cork", { add->peer.f |= KXF_CORK; })
})
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);
})
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("-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);
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),
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),
peer *p;
if ((p = a_findpeer(a, av[0])) != 0) {
peer *p;
if ((p = a_findpeer(a, av[0])) != 0) {
p_ponged(p, MISC_EPONG, &bb);
}
break;
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;
/* --- @p_destroy@ --- *
*
* Arguments: @peer *p@ = pointer to a peer
/* --- @p_destroy@ --- *
*
* Arguments: @peer *p@ = pointer to a peer
+ * @int bye@ = say goodbye to the peer?
*
* Returns: ---
*
* Use: Destroys a peer.
*/
*
* Returns: ---
*
* Use: Destroys a peer.
*/
+void p_destroy(peer *p, int bye)
T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); )
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);
a_notify("KILL", "%s", p->spec.name, A_END);
ksl_free(&p->ks);
kx_free(&p->kx);
AT_CLEANUP
###--------------------------------------------------------------------------
AT_CLEANUP
###--------------------------------------------------------------------------
AT_SETUP([server knock])
AT_KEYWORDS([knock])
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([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])
])
COMMS_EPING([alice], [alice], [bob], [bob])
AT_CHECK([TRIPECTL -dalice FORCEKX bob])
AT_CHECK([TRIPECTL -dbob FORCEKX alice])
])
AT_CHECK([TRIPECTL -dalice FORCEKX bob])
AT_CHECK([TRIPECTL -dbob FORCEKX alice])
])
AT_CHECK([TRIPECTL -dbob KILL alice])
AT_CHECK([TRIPECTL -dalice LIST],, [])
])
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
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
.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
.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. This option implies
+.BR \-ephemeral .
.TP
.B "\-mobile"
The peer is a mobile device, and is likely to change address rapidly.
.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
and if one succeeds, the server will update its idea of the peer's
address and emit an
.B NEWADDR
+notification. This option implies
+.BR \-ephemeral .
.TP
.BI "\-priv " tag
Use the private key
.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.
.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
.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 */
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 {
} peerspec;
typedef struct peer_byname {
/* --- @p_destroy@ --- *
*
* Arguments: @peer *p@ = pointer to a peer
/* --- @p_destroy@ --- *
*
* Arguments: @peer *p@ = pointer to a peer
+ * @int bye@ = say goodbye to the peer?
*
* Returns: ---
*
* Use: Destroys a peer.
*/
*
* Returns: ---
*
* Use: Destroys a peer.
*/
-extern void p_destroy(peer */*p*/);
+extern void p_destroy(peer */*p*/, int /*bye*/);
/* --- @FOREACH_PEER@ --- *
*
/* --- @FOREACH_PEER@ --- *
*
.RB [ \-priv
.IR tag ]
.RB [ \-mobile ]
.RB [ \-priv
.IR tag ]
.RB [ \-mobile ]
.RB [ \-tunnel
.IR driver ]
.I address
.RB [ \-tunnel
.IR driver ]
.I address
+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
The
.I address
is the value assigned to the
T.Coroutine(run_ifupdown, name = 'ifdown %s' % peer.name) \
.switch('ifdown', 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.
"""
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)
"""
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),
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)
*addr)
except T.TripeError, exc:
raise T.TripeJobError(*exc.args)
S.warn(['connect', 'knock-tag-mismatch',
'peer', pname, 'public-key-tag', ktag])
return
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.
###--------------------------------------------------------------------------
### Command implementation.
addr = peer.get('peer')
if addr == 'PASSIVE':
raise T.TripeJobError('passive-peer', 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():
"""
def cmd_listactive():
"""
addr = cr.parent.switch()
if addr is None:
raise T.TripeJobError('connect-timeout')
addr = cr.parent.switch()
if addr is None:
raise T.TripeJobError('connect-timeout')
+ addpeer(peer, addr, True)
finally:
del chalmap[chal]
finally:
del chalmap[chal]
for name in M.split(autos)[0]:
try:
peer = Peer(name, cdb)
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)
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 } },
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 } },