#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*/);
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@ --- *
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 {
- 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);
}
- xfree(a->pname);
+ xfree(a->peer.name);
xfree(a->paddr);
- a->pname = 0;
+ a->peer.name = 0;
selbuf_enable(&a->b);
a_unlock(a);
}
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);
- a->pname = 0;
+ a->peer.name = 0;
selbuf_enable(&a->b);
a_unlock(a);
}
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 --- */
return;
}
+ /* --- Set stuff up --- */
+
+ a->peer.name = av[0];
+ a->peer.t_ka = 0;
+ a->peer.tops = tun_default;
+
/* --- Parse options --- */
i = 1;
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)) {
- tops = tunnels[j];
+ a->peer.tops = tunnels[j];
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;
+ i++;
}
/* --- Fill in the easy bits of address --- */
- BURN(a->peer);
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;
}
- 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");
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 (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->peer.name = 0;
return;
}
* 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->tops = tops;
selbuf_disable(&a->b);
gettimeofday(&tv, 0);
tv.tv_sec += T_RESOLVE;
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;
}
+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 --- */
}
}
+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");
{ "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 },
- { "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 },
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);
}
+ 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);
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;
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
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 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
(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 .)
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 .)
.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
(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
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 " 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
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;
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",
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;
}
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,
/*----- 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
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",
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);
})
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) {
- 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++;
- a_warn("PEER %s decrypt-failed", p->name);
+ a_warn("PEER %s decrypt-failed", p->spec.name);
return;
}
if (BOK(&bb)) {
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;
+ 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++;
- 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;
}
}
* 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)) {
- 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),
- 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);
+ 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
* 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@ --- *
*
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@ --- *
*
- * 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.
*
* 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);
- 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;
- memcpy(&p->peer.sa, sa, sz);
- p->sasz = sz;
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;
- 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;
- switch (p->peer.sa.sa_family) {
+ switch (p->spec.sa.sa.sa_family) {
case AF_INET:
a_notify("ADD %s %s INET %s %u",
- name,
+ spec->name,
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:
- 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;
}
- 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:
- kx_free(&p->kx);
+ if (spec->t_ka)
+ sel_rmtimer(&p->tka);
+ p->t->ops->destroy(p->t);
tidy_0:
- xfree(p->name);
+ xfree(p->spec.name);
DESTROY(p);
return (0);
}
* 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@ --- *
*
{
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);
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);
- 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)
/* -*-c-*-
*
- * $Id: tripe-protocol.h,v 1.2 2004/04/08 01:36:17 mdw Exp $
+ * $Id$
*
* Protocol definition for TrIPE
*
#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
- * 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
#include <mLib/str.h>
#include <mLib/sub.h>
#include <mLib/trace.h>
+#include <mLib/tv.h>
#include <catacomb/buf.h>
* 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 */
- 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 */
- addr peer; /* Peer socket address */
- size_t sasz; /* Socket address size */
stats st; /* Statistics */
keyexch kx; /* Key exchange protocol block */
+ sel_timer tka; /* Timer for keepalives */
} 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
#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 */
+ peerspec peer; /* Peer pending creation */
+ char *paddr; /* Hostname to be resolved */
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 */
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_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.
*
* 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@ --- *
*