From 060ca767db058036f7ade27219de9ebaf1d4737b Mon Sep 17 00:00:00 2001 Message-Id: <060ca767db058036f7ade27219de9ebaf1d4737b.1714625821.git.mdw@distorted.org.uk> From: Mark Wooding Date: Tue, 4 Oct 2005 18:25:28 +0000 Subject: [PATCH] More support scripts and other cool stuff. Organization: Straylight/Edgeware From: mdw --- Makefile.am | 7 +- admin.c | 166 +++--- configure.in | 28 +- debian/control | 19 + debian/rules | 30 +- doc/Makefile.am | 10 +- doc/tripe-admin.5 | 135 ++++- doc/tripe-keys.8 | 212 +++++++ doc/tripe-keys.conf.5 | 267 +++++++++ doc/tripemon.1 | 88 +++ peer.c | 27 +- tripe-keys.in | 347 +++++++++++ tripe-keys.master | 46 ++ tripe.h | 15 +- tripemon.in | 1296 +++++++++++++++++++++++++++++++++++++++++ tun-bsd.c | 10 +- tun-linux.c | 9 +- tun-slip.c | 24 +- tun-unet.c | 10 +- 19 files changed, 2612 insertions(+), 134 deletions(-) create mode 100644 doc/tripe-keys.8 create mode 100644 doc/tripe-keys.conf.5 create mode 100644 doc/tripemon.1 create mode 100644 tripe-keys.in create mode 100644 tripe-keys.master create mode 100644 tripemon.in diff --git a/Makefile.am b/Makefile.am index f5f5a255..8fbec707 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,6 +37,10 @@ tun = @tun@ sbin_PROGRAMS = tripe bin_PROGRAMS = tripectl tripe-mitm pkstream noinst_SCRIPTS = tripe-init +bin_SCRIPTS = @pyscripts@ @pygtkscripts@ +PYTHONSCRIPTS = tripe-keys +PYGTKSCRIPTS = tripemon +EXTRA_SCRIPTS = ${PYTHONSCRIPTS} ${PYGTKSCRIPTS} tripe_SOURCES = \ tripe.c tripe.h tripe-protocol.h \ admin.c peer.c \ @@ -59,6 +63,7 @@ install-hook: EXTRA_DIST = tripe.conf \ debian/rules debian/control debian/changelog debian/copyright \ - debian/tripe.postinst debian/tripe.prerm debian/tripe.postrm + debian/tripe.postinst debian/tripe.prerm debian/tripe.postrm \ + tripe-keys.in ##----- That's all, folks --------------------------------------------------- diff --git a/admin.c b/admin.c index 92ea0a64..987de7ab 100644 --- a/admin.c +++ b/admin.c @@ -77,6 +77,8 @@ static void a_destroy(admin */*a*/); static void a_lock(admin */*a*/); static void a_unlock(admin */*a*/); +#define BOOL(x) ((x) ? "t" : "nil") + /*----- Output functions --------------------------------------------------*/ /* --- @trywrite@ --- * @@ -512,6 +514,9 @@ static long a_parsetime(const char *p) /*----- Backgrounded operations -------------------------------------------*/ +#define BGTAG(bg) \ + (((admin_bgop *)(bg))->tag ? ((admin_bgop *)(bg))->tag : "") + /* --- @a_bgrelease@ --- * * * Arguments: @admin_bgop *bg@ = backgrounded operation @@ -526,19 +531,15 @@ static void a_bgrelease(admin_bgop *bg) { admin *a = bg->a; - if (bg->tag) - xfree(bg->tag); - else - selbuf_enable(&a->b); - if (bg->next) - bg->next->prev = bg->prev; - if (bg->prev) - bg->prev->next = bg->next; - else - a->bg = bg->next; + T( trace(T_ADMIN, "admin: release bgop %s", BGTAG(bg)); ) + if (bg->tag) xfree(bg->tag); + else selbuf_enable(&a->b); + if (bg->next) bg->next->prev = bg->prev; + if (bg->prev) bg->prev->next = bg->next; + else a->bg = bg->next; xfree(bg); - if (a->f & AF_CLOSE) - a_destroy(a); + if (a->f & AF_CLOSE) a_destroy(a); + a_unlock(a); } /* --- @a_bgok@, @a_bginfo@, @a_bgfail@ --- * @@ -596,7 +597,10 @@ static void a_bgadd(admin *a, admin_bgop *bg, const char *tag, bg->cancel = cancel; bg->next = a->bg; bg->prev = 0; + if (a->bg) a->bg->prev = bg; a->bg = bg; + a_lock(a); + T( trace(T_ADMIN, "admin: add bgop %s", BGTAG(bg)); ) if (tag) a_write(a, "DETACH", tag, 0); } @@ -613,6 +617,7 @@ static void a_bgadd(admin *a, admin_bgop *bg, const char *tag, static void a_addfree(admin_addop *add) { + T( trace(T_ADMIN, "admin: free add op %s", BGTAG(add)); ) if (add->peer.name) xfree(add->peer.name); if (add->paddr) xfree(add->paddr); } @@ -630,6 +635,7 @@ static void a_addcancel(admin_bgop *bg) { admin_addop *add = (admin_addop *)bg; + T( trace(T_ADMIN, "admin: cancel add op %s", BGTAG(add)); ) sel_rmtimer(&add->t); bres_abort(&add->r); a_addfree(add); @@ -668,8 +674,7 @@ static void a_addresolve(struct hostent *h, void *v) { admin_addop *add = v; - a_lock(add->bg.a); - T( trace(T_ADMIN, "admin: %u resolved", add->bg.a->seq); ) + T( trace(T_ADMIN, "admin: add op %s resolved", BGTAG(add)); ) TIMER; if (!h) a_bgfail(&add->bg, "resolve-error %s", add->paddr); @@ -680,7 +685,6 @@ static void a_addresolve(struct hostent *h, void *v) sel_rmtimer(&add->t); a_addfree(add); a_bgrelease(&add->bg); - a_unlock(add->bg.a); } /* --- @a_addtimer@ --- * @@ -697,13 +701,11 @@ static void a_addtimer(struct timeval *tv, void *v) { admin_addop *add = v; - a_lock(add->bg.a); - T( trace(T_ADMIN, "admin: %u resolver timeout", add->bg.a->seq); ) + T( trace(T_ADMIN, "admin: add op %s timeout", BGTAG(add)); ) a_bgfail(&add->bg, "resolver-timeout %s\n", add->paddr); bres_abort(&add->r); a_addfree(add); a_bgrelease(&add->bg); - a_unlock(add->bg.a); } /* --- @acmd_add@ --- * @@ -810,10 +812,13 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) */ a_bgadd(a, &add->bg, tag, a_addcancel); + T( trace(T_ADMIN, "admin: %u, add op %s resolving hostname `%s'", + a->seq, BGTAG(add), add->paddr); ) /* --- If the name is numeric, do it the easy way --- */ if (inet_aton(av[i], &add->peer.sa.sin.sin_addr)) { + T( trace(T_ADMIN, "admin: add op %s done the easy way", BGTAG(add)); ) a_doadd(add); a_addfree(add); a_bgrelease(&add->bg); @@ -826,8 +831,6 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) tv.tv_sec += T_RESOLVE; sel_addtimer(&sel, &add->t, &tv, a_addtimer, add); bres_byname(&add->r, add->paddr, a_addresolve, add); - T( trace(T_ADMIN, "admin: %u resolving hostname `%s'", - a->seq, add->paddr); ) return; bad_syntax: @@ -854,6 +857,7 @@ fail: static void a_pingcancel(admin_bgop *bg) { admin_pingop *pg = (admin_pingop *)bg; + T( trace(T_ADMIN, "admin: cancel ping op %s", BGTAG(pg)); ) p_pingdone(&pg->ping, PING_NONOTIFY); } @@ -873,7 +877,6 @@ static void a_pong(int rc, void *v) struct timeval tv; double millis; - a_lock(pg->bg.a); switch (rc) { case PING_OK: gettimeofday(&tv, 0); @@ -893,8 +896,8 @@ static void a_pong(int rc, void *v) default: abort(); } + T( trace(T_ADMIN, "admin: ponged ping op %s", BGTAG(pg)); ) a_bgrelease(&pg->bg); - a_unlock(pg->bg.a); } /* --- @acmd_ping@, @acmd_eping@ --- * @@ -946,6 +949,8 @@ static void a_ping(admin *a, unsigned ac, char *av[], pg = xmalloc(sizeof(*pg)); gettimeofday(&pg->pingtime, 0); a_bgadd(a, &pg->bg, tag, a_pingcancel); + T( trace(T_ADMIN, "admin: ping op %s: %s to %s", + BGTAG(pg), cmd, p_name(p)); ) if (p_pingsend(p, &pg->ping, msg, t, a_pong, pg)) { a_bgfail(&pg->bg, "ping-send-failed"); a_bgrelease(&pg->bg); @@ -987,10 +992,9 @@ static int traceish(admin *a, unsigned ac, char *av[], if (!ac || strcmp(av[0], "?") == 0) { const trace_opt *t; - a_info(a, "Current %s status:", what); for (t = tt; t->ch; t++) { - a_info(a, "%c %c %s", - t->ch, (*ff & t->f) == t->f ? '*' : ' ', t->help); + a_info(a, "%c%c %s", + t->ch, (*ff & t->f) == t->f ? '+' : ' ', t->help); } } else { unsigned sense = 1; @@ -1136,32 +1140,57 @@ static void acmd_addr(admin *a, unsigned ac, char *av[]) } } +static void acmd_peerinfo(admin *a, unsigned ac, char *av[]) +{ + peer *p; + const peerspec *ps; + + if ((p = p_find(av[0])) == 0) { + a_fail(a, "unknown-peer %s", av[0]); + return; + } + + ps = p_spec(p); + a_info(a, "tunnel=%s", ps->tops->name); + a_info(a, "keepalive=%lu", ps->t_ka); + a_ok(a); +} + +static void acmd_servinfo(admin *a, unsigned ac, char *av[]) +{ + a_info(a, "implementation=edgeware-tripe"); + a_info(a, "version=%s", VERSION); + a_info(a, "daemon=%s", BOOL(flags & F_DAEMON)); + a_ok(a); +} + static void acmd_stats(admin *a, unsigned ac, char *av[]) { peer *p; stats *st; - if ((p = p_find(av[0])) == 0) + if ((p = p_find(av[0])) == 0) { a_fail(a, "unknown-peer %s", av[0]); - else { - st = p_stats(p); - a_info(a, "start-time=%s", timestr(st->t_start)); - a_info(a, "last-packet-time=%s", timestr(st->t_last)); - a_info(a, "last-keyexch-time=%s", timestr(st->t_kx)); - a_info(a, "packets-in=%lu bytes-in=%lu", st->n_in, st->sz_in); - a_info(a, "packets-out=%lu bytes-out=%lu", - st->n_out, st->sz_out); - a_info(a, "keyexch-packets-in=%lu keyexch-bytes-in=%lu", - st->n_kxin, st->sz_kxin); - a_info(a, "keyexch-packets-out=%lu keyexch-bytes-out=%lu", - st->n_kxout, st->sz_kxout); - a_info(a, "ip-packets-in=%lu ip-bytes-in=%lu", - st->n_ipin, st->sz_ipin); - a_info(a, "ip-packets-out=%lu ip-bytes-out=%lu", - st->n_ipout, st->sz_ipout); - a_info(a, "rejected-packets=%lu", st->n_reject); - a_ok(a); + return; } + + st = p_stats(p); + a_info(a, "start-time=%s", timestr(st->t_start)); + a_info(a, "last-packet-time=%s", timestr(st->t_last)); + a_info(a, "last-keyexch-time=%s", timestr(st->t_kx)); + a_info(a, "packets-in=%lu bytes-in=%lu", st->n_in, st->sz_in); + a_info(a, "packets-out=%lu bytes-out=%lu", + st->n_out, st->sz_out); + a_info(a, "keyexch-packets-in=%lu keyexch-bytes-in=%lu", + st->n_kxin, st->sz_kxin); + a_info(a, "keyexch-packets-out=%lu keyexch-bytes-out=%lu", + st->n_kxout, st->sz_kxout); + a_info(a, "ip-packets-in=%lu ip-bytes-in=%lu", + st->n_ipin, st->sz_ipin); + a_info(a, "ip-packets-out=%lu ip-bytes-out=%lu", + st->n_ipout, st->sz_ipout); + a_info(a, "rejected-packets=%lu", st->n_reject); + a_ok(a); } static void acmd_kill(admin *a, unsigned ac, char *av[]) @@ -1234,10 +1263,12 @@ static const acmd acmdtab[] = { { "kill", "kill PEER", 1, 1, acmd_kill }, { "list", "list", 0, 0, acmd_list }, { "notify", "notify MESSAGE ...", 1, 0xffff, acmd_notify }, + { "peerinfo", "peerinfo PEER", 1, 1, acmd_peerinfo }, { "ping", "ping [OPTIONS] PEER", 1, 0xffff, acmd_ping }, { "port", "port", 0, 0, acmd_port }, { "quit", "quit", 0, 0, acmd_quit }, { "reload", "reload", 0, 0, acmd_reload }, + { "servinfo", "servinfo", 0, 0, acmd_servinfo }, { "stats", "stats PEER", 1, 1, acmd_stats }, #ifndef NTRACE { "trace", "trace [OPTIONS]", 0, 1, acmd_trace }, @@ -1269,32 +1300,20 @@ static void acmd_help(admin *a, unsigned ac, char *av[]) * immediately. */ -static void a_lock(admin *a) { assert(!(a->f & AF_LOCK)); a->f |= AF_LOCK; } +static void a_lock(admin *a) { a->ref++; } -/* --- @a_unlock@ --- * +/* --- @a_dodestroy@ --- * * * Arguments: @admin *a@ = pointer to an admin block * * Returns: --- * - * Use: Unlocks an admin block, allowing its destruction. This is - * also the second half of @a_destroy@. + * Use: Actually does the legwork of destroying an admin block. */ -static void a_unlock(admin *a) +static void a_dodestroy(admin *a) { admin_bgop *bg, *bbg; - - assert(a->f & AF_LOCK); - - /* --- If we're not dead, that's fine --- */ - - if (!(a->f & AF_DEAD)) { - a->f &= ~AF_LOCK; - return; - } - - /* --- If we are, then destroy the rest of the block --- */ T( trace(T_ADMIN, "admin: completing destruction of connection %u", a->seq); ) @@ -1320,6 +1339,23 @@ static void a_unlock(admin *a) DESTROY(a); } +/* --- @a_unlock@ --- * + * + * Arguments: @admin *a@ = pointer to an admin block + * + * Returns: --- + * + * Use: Unlocks an admin block, allowing its destruction. This is + * also the second half of @a_destroy@. + */ + +static void a_unlock(admin *a) +{ + assert(a->ref); + if (!--a->ref && (a->f & AF_DEAD)) + a_dodestroy(a); +} + /* --- @a_destroy@ --- * * * Arguments: @admin *a@ = pointer to an admin block @@ -1361,12 +1397,10 @@ static void a_destroy(admin *a) /* --- If the block is locked, that's all we can manage --- */ - if (a->f & AF_LOCK) { - T( trace(T_ADMIN, "admin: deferring destruction..."); ) - return; - } - a->f |= AF_LOCK; - a_unlock(a); + if (!a->ref) + a_dodestroy(a); + T( else + trace(T_ADMIN, "admin: deferring destruction..."); ) } /* --- @a_line@ --- * diff --git a/configure.in b/configure.in index 70ceb799..690c1934 100644 --- a/configure.in +++ b/configure.in @@ -33,6 +33,30 @@ AC_CANONICAL_HOST AC_PROG_MAKE_SET AC_PROG_CC AM_PROG_LIBTOOL +python=no +mdw_PROG_PYTHON([2.3], + [python=yes + pyscripts='${PYTHONSCRIPTS}' + pymans='${PYTHONMANS}']) +AC_SUBST([pyscripts]) AC_SUBST([pymans]) + +if test $python = yes; then + mdw_CHECK_PYTHON([2.3]) + AC_CACHE_CHECK([for pygtk], [mdw_cv_pygtk], [ + mdw_cv_pygtk=no + python -c >&5 2>&5 ' +import pygtk +pygtk.require("2.0") +import gtk +' && mdw_cv_pygtk=yes + ]) + if test $mdw_cv_pygtk = yes; then + pygtkscripts='${PYGTKSCRIPTS}' + pygtkmans='${PYGTKMANS}' + fi +fi +AC_SUBST([pygtkscripts]) AC_SUBST([pygtkmans]) + AC_CHECK_HEADERS([stdarg.h]) mdw_GCC_FLAGS([-Wall]) mdw_OPT_TRACE @@ -226,6 +250,8 @@ mdw_DEFINE_PATHS([ AC_SUBST(initconfig) ]) AC_SUBST(DIRS) -AC_OUTPUT(Makefile doc/Makefile ethereal/Makefile tripe-init) +AC_OUTPUT( \ + Makefile doc/Makefile ethereal/Makefile \ + tripe-init tripe-keys tripemon) dnl ----- That's all, folks ------------------------------------------------- diff --git a/debian/control b/debian/control index 4386a9a3..ef4865cb 100644 --- a/debian/control +++ b/debian/control @@ -37,3 +37,22 @@ Description: Trivial IP Encryption: a simple virtual private network and authenticity of packets it sends and receives. . This package contains the protocol analysis plug-in for Ethereal. + +Package: tripemon +Architecture: all +Depends: python (>= 2.3), python-gtk2 (>= 2.6), tripe +Description: Trivial IP Encryption: a simple virtual private network + TrIPE is a simple VPN protocol. It uses cryptography to ensure secrecy + and authenticity of packets it sends and receives. + . + This package contains a graphical monitor program for managing and + keeping an eye on a TrIPE server. + +Package: tripe-keys +Architecture: all +Depends: python (>= 2.3), tripe, catacomb-bin, python-catacomb +Description: Trivial IP Encryption: a simple virtual private network + TrIPE is a simple VPN protocol. It uses cryptography to ensure secrecy + and authenticity of packets it sends and receives. + . + This package contains a tool for centrally managing TrIPE keys. diff --git a/debian/rules b/debian/rules index 6a52d030..7b94252d 100755 --- a/debian/rules +++ b/debian/rules @@ -30,6 +30,24 @@ install: build cp deb-build/tripe-init debian/tripe/etc/init.d/tripe chmod 755 debian/tripe/etc/init.d/tripe cp tripe.conf debian/tripe/etc/default/tripe + mkdir -p debian/tripe-keys/usr/bin + mv debian/tripe/usr/bin/tripe-keys debian/tripe-keys/usr/bin + mkdir -p \ + debian/tripe-keys/usr/share/man/man5 \ + debian/tripe-keys/usr/share/man/man8 + mv debian/tripe/usr/share/man/man5/tripe-keys.conf.5 \ + debian/tripe-keys/usr/share/man/man5 + mv debian/tripe/usr/share/man/man8/tripe-keys.8 \ + debian/tripe-keys/usr/share/man/man8 + mkdir -p debian/tripe-keys/usr/share/doc/tripe-keys/examples + cp tripe-keys.master \ + debian/tripe-keys/usr/share/doc/tripe-keys/examples + mkdir -p debian/tripemon/usr/bin + mv debian/tripe/usr/bin/tripemon debian/tripemon/usr/bin + mkdir -p \ + debian/tripemon/usr/share/man/man1 + mv debian/tripe/usr/share/man/man1/tripemon.1 \ + debian/tripemon/usr/share/man/man1 mkdir -p debian/pkstream/usr/bin mv debian/tripe/usr/bin/pkstream debian/pkstream/usr/bin mkdir -p debian/pkstream/usr/share/man/man1 @@ -40,7 +58,17 @@ install: build rm -f debian/tripe-ethereal/usr/lib/ethereal/plugins/*/*.a rmdir debian/tripe/usr/lib -binary-indep: +binary-indep: install + dh_testdir -i + dh_testroot -i + dh_compress -i + dh_installdocs -i + dh_installlogrotate -i + dh_gencontrol -i + dh_fixperms -i + dh_installdeb -i + dh_md5sums -i + dh_builddeb -i binary-arch: install dpkg --status ethereal | \ diff --git a/doc/Makefile.am b/doc/Makefile.am index b537576b..ee4efdde 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ ## -*-makefile-*- ## -## $Id: Makefile.am,v 1.5 2004/04/08 01:36:17 mdw Exp $ +## $Id$ ## ## Makefile for TrIPE documentation ## @@ -27,7 +27,11 @@ AUTOMAKE_OPTIONS = foreign -man_MANS = tripe.8 tripectl.1 tripe-admin.5 pkstream.1 tripe-mitm.8 -EXTRA_DIST = $(man_MANS) +man_MANS = \ + tripe.8 tripectl.1 tripe-admin.5 pkstream.1 tripe-mitm.8 \ + @pymans@ @pygtkmans@ +PYTHONMANS = tripe-keys.8 tripe-keys.conf.5 +PYGTKMANS = tripemon.1 +EXTRA_DIST = $(man_MANS) $(PYTHONMANS) $(PYGTKMANS) ##----- That's all, folks --------------------------------------------------- diff --git a/doc/tripe-admin.5 b/doc/tripe-admin.5 index b4d51b5c..b6bcdfc1 100644 --- a/doc/tripe-admin.5 +++ b/doc/tripe-admin.5 @@ -1,4 +1,10 @@ .\" -*-nroff-*- +.\" +.ie t \{\ +. if \n(.g \{\ +. fam P +. \} +.\} .TH tripe-admin 5 "18 February 2001" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption" .SH NAME tripe-admin \- administrator commands for TrIPE @@ -134,7 +140,11 @@ indicates that a background command succeeded or failed, respectively. .PP A background command will never issue an .B OK -response: it will always detach and then issue a +or +.B BGINFO +response: it will always detach and then issue any +.B BGINFO +lines followed by .B BGOK response. .SS "Network addresses" @@ -159,6 +169,43 @@ If, on input, no recognised address family token is found, the following words are assumed to represent an .B INET address. +.SS "Key-value output" +Some commands (e.g., +.B STATS +and +.BR SERVINFO ) +produce output in the form of +.IB key = value +pairs, one per word. Neither the +.I key +nor the +.I value +contain spaces. +.SS "Trace lists" +Commands which enable or disable kinds of output (e.g., +.B TRACE +and +.BR WATCH ) +work in similar ways. They take a single optional argument, which +consists of a string of letters selecting message types, optionally +interspersed with +.RB ` + ' +to enable, or +.RB ` \- ' +to disable, the subsequently listed types. +.PP +If the argument is omitted, the available message types are displayed, +one to an +.B INFO +line, in a fixed-column format. Column zero contains the key letter for +selecting that message type; column one contains either a space or a +.RB ` + ' +sign, if the message type is disabled or enabled respectively; and a +textual description of the message type begins at column 3 and continues +to the end of the line. +.PP +Lowercase key letters control individual message types. Uppercase key +letters control collections of message types. .SH "COMMAND REFERENCE" The commands provided are: .TP @@ -255,6 +302,19 @@ Issues a .B USER notification to all interested administration clients. .TP +.BI "PEERINFO " peer +Returns information about a peer, in key-value form. The following keys +are returned. +.RS +.TP +.B tunnel +The tunnel driver used for this peer. +.TP +.B keepalive +The keepalive interval, in seconds, or zero if no keepalives are to be +sent. +.RE +.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 @@ -313,6 +373,29 @@ for example after adding a new peer key. .B "QUIT" Instructs the server to exit immediately. A warning is sent. .TP +.B "SERVINFO" +Returns information about the server, in the form of key-value pairs. +The following keys are used. +.RS +.TP +.B implementation +A keyword naming the implementation of the +.BR tripe (8) +server. The current implementation is called +.BR edgeware-tripe . +.TP +.B version +The server's version number, as reported by +.BR VERSION . +.TP +.B daemon +Either +.B t +or +.BR nil , +if the server has or hasn't (respectively) become a daemon. +.RE +.TP .BI "STATS " peer Emits a number of .B INFO @@ -321,20 +404,9 @@ lines, each containing one or more statistics in the form The statistics-gathering is experimental and subject to change. .TP .BR "TRACE " [\fIoptions\fP] -A trace argument consists of a string of letters (listed below) -selecting trace outputs, optionally interspersed with -.RB ` + ' -to enable, or -.RB ` \- ' -to disable, the subsequently listed outputs; the initial behaviour is to -enable listed outputs. For example, the string -.B ra\-st+x -enables tracing of peer management, admin-connection handling and -key-exchange processing, and disables tracing of symmetric keyset -management and the system-specific tunnel driver. If no argument is -given, a table is returned showing the available tracing option letters -and their meanings. Programs should not attempt to parse this table: -its format is not guaranteed to remain the same. +Selects trace outputs: see +.B "Trace lists" +above. Message types provided are: .RS .PP Currently, the following tracing options are supported: @@ -396,17 +468,24 @@ or All of the above. .RE .TP +.B "TUNNELS" +For each available tunnel driver, an +.B INFO +line is printed giving its name. +.TP +.B "VERSION" +Causes the server to emit an +.B INFO +line stating its software version, as two words: the server name, and +its version string. The server name +.B tripe +is reserved to the Straylight/Edgeware implementation. +.TP .BR "WATCH " [\fIoptions\fP] Enables or disables asynchronous messages .IR "for the current connection only" . -This command has no effect on other connections. A watch argument -consists of a string of letters (listed below) selecting message types, -optionally interspersed with -.RB ` + ' -to enable, or -.RB ` \- ' -to disable, the subsequently listed types, similar to -.B trace +See +.B "Trace lists" above. The default watch state for the connection the server opens automatically on stdin/stdout is to show warnings and trace messages; other connections show no asynchronous messages. (This is done in order @@ -414,7 +493,7 @@ to guarantee that a program reading the server's stdout does not miss any warnings.) .RS .PP -Currently, the following watch options are supported: +Message types provided are: .TP .B t .B TRACE @@ -432,14 +511,6 @@ messages. All of the above. .RE .TP -.B "VERSION" -Causes the server to emit an -.B INFO -line stating its software version, as two words: the server name, and -its version string. The server name -.B tripe -is reserved to the Straylight/Edgeware implementation. -.TP .BI "WARN " tokens\fR... Issues a .B USER diff --git a/doc/tripe-keys.8 b/doc/tripe-keys.8 new file mode 100644 index 00000000..9055c87f --- /dev/null +++ b/doc/tripe-keys.8 @@ -0,0 +1,212 @@ +.\" -*-nroff-*- +.\". +.de hP +.IP +\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c +.. +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.ie t \{\ +. ds o \(bu +. ds ss \s8\u +. ds se \d\s0 +. if \n(.g \{\ +. fam P +. \} +.\} +.el \{\ +. ds o o +. ds ss ^ +. ds se +.\} +.TH tripe-keys 8 "14 September 2005" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption" +.SH "NAME" +tripe-keys \- simple centralized key management for tripe +.SH "SYNOPSIS" +.B tripe-keys +.I operation +.IP "Operations supported:" +.B "help" +.br +.BI "generate " tag +.br +.B "update" +.br +.B "rebuild" +.br +.B "setup" +.br +.B "upload" +.SH "DESCRIPTION" +The +.B tripe-keys +script implements a very simple, centralized key management system for +.BR tripe (8). +It assumes that there is a central authority who knows all the public +keys for a private network. +.SS "Overview" +The +.B tripe-keys +program maintains a +.I repository +of public keys. It provides a way for a master authority to publish the +repository and for clients to obtain authentic copies of it. +.PP +The repository is very simple: it consists of a directory +.B repos +full of public-key files, each named +.BI peer- tag .pub \fR. +.PP +The repository setup process creates a master signing key, stored in the +.B master +keyring, and a key describing the parameters to be used for generating +key-exchange keys, stored in +.BR repos/param . +.PP +The master authority has a configuration file +.BR tripe-keys.master , +usually created by copying the template provided and editing it. +.PP +The published repository consists of a tarball of the +.B repos +directory, containing the key-generation parameters and all the peers' +public keys, and a client configuration file +.BR tripe-keys.conf . +The tarball is signed by the master authority's signing key. +.PP +The client configuration file is essentially a copy of +.B tripe-keys.master +with some extra bits filled in: in particular, it contains the +fingerprint of the master signing key, so that the client can be sure +it's checking the right key. +.PP +A peer starts by downloading a copy of +.B tripe-keys.conf +and then making sure it's authentic. (This is one of the tricky bits. +The other is getting public keys back to the master authority.) This is +enough for the peer to fetch a copy of the repository, verify the +signature, and assemble a public keyring for the other peers in the +network. +.PP +In fact, it's not +.I quite +that simple. The system allows new signing keys to replace old ones, so +in fact the publication process signs the repository archive using a +collection of keys. Each signing key is given a sequence number. The +client configuration file contains the sequence number of the master +signing key whose fingerprint it knows. During an update, the right +signature is fetched and checked; if there's a new master key, then the +.B tripe-keys.conf +in the new repository archive will have its sequence number and +fingerprint: the update process will replace its configuration file with +the new version, and the peer will use the new key from then on. +.SS "Options" +The +.B tripe-keys +program accepts some standard command-line options: +.TP +.B "\-h, \-\-help" +Print general help about +.B tripe-keys +to standard output and exit successfully. +.TP +.B "\-v, \-\-version" +Print the version number of +.B tripe-keys +to standard output and exit successfully. +.TP +.B "\-u, \-\-usage" +Print brief usage about +.B tripe-keys +to standard output and exit successfully. +.SS "Subcommands" +.TP +.BI help\fR[ command \fR] +With no arguments, shows help, as for the +.B \-\-help +option. With an argument, shows help about that +.IR command . +.TP +.B "setup" +Constructs a new repository and makes a signing key (as for +.BR newmaster ) +and key-exchange parameters. Fails if +.B repos +already exists. +.TP +.B "upload" +Build a repository archive, sign it with the active signing keys, and +make a +.B tripe-keys.conf +file. Copy the results to the places named by +.IR repos-file , +.IR sig-file , +and +.I conf-file +respectively. (This command is currently misnamed. It only copies +stuff about the local filesystem. Some day it'll really upload stuff.) +.TP +.BI "generate " tag +Generate a peer key for the peer named +.IR tag . +The private key ends up in +.BR keyring ; +the public key is written to +.BI peer- tag .pub +in the +.I current +directory. +.TP +.B update +Fetches a new copy of the repository archive and its signature. It +unpacks the archive in a temporary directory, and checks the enclosed +master public key against the fingerprint in the configuration file. It +then verifies the signature on the archive using this public key. If +all is well, it replaces the current +.B repos +directory with the version in the new archive, and if necessary it +replaces the current configuration file with the new one in the +archive. It then does a +.B rebuild +to construct a new +.B keyring.pub +file. +.TP +.B newmaster +Generates a new master signing key. The old master key is not deleted. +.TP +.B rebuild +Rebuilds the public keyring +.B keyring.pub +from the public keys in the +.B repos +directory. +.TP +.B clean +Deletes everything which +.B tripe-keys +might have written to a directory. In particular, it deletes +.BR repos , +.BR tmp , +.BR master , +.BR keyring , +.BR keying.pub , +and their associated +.B .old +files. +.SH "SEE ALSO" +.BR key (1), +.BR tripe\-keys.conf (5), +.BR tripe (8). +.SH "AUTHOR" +Mark Wooding, diff --git a/doc/tripe-keys.conf.5 b/doc/tripe-keys.conf.5 new file mode 100644 index 00000000..dccb8557 --- /dev/null +++ b/doc/tripe-keys.conf.5 @@ -0,0 +1,267 @@ +.\" -*-nroff-*- +.\". +.de hP +.IP +\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c +.. +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.ie t \{\ +. ds o \(bu +. ds ss \s8\u +. ds se \d\s0 +. if \n(.g \{\ +. fam P +. \} +.\} +.el \{\ +. ds o o +. ds ss ^ +. ds se +.\} +.TH tripe-keys.conf 5 "14 September 2005" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption" +.SH "NAME" +tripe-keys.conf \- configuration file format for tripe-keys +.SH "DESCRIPTION" +The +.B tripe-keys.master +or +.B tripe-keys.conf +file is a simple line-based configuration file read by +.BR tripe-keys (1). +Lines may be empty (consist only of whitespace), be comments (first +non-whitespace character is +.RB ` # ') +or have the form +.IP +.I name +.RB [ = ] +.I value +.PP +A +.I name +consists of alphanumeric characters and hyphens. Values may contain +substitutions, of the form +.BI ${ name } \fR, +which are replaced by the value assigned to +.IR name . +Many +.IR name s +have significance to the +.B tripe-keys +program: these are described below. Many have sensible defaults. +.SS "The tripe-keys.master file" +The client configuration file is built by applying substitutions to the +.B tripe-keys.master +file. The following tokens are substituted: +.TP +.B @MASTER-SEQUENCE@ +The sequence number of the most recently-added signing key. +.TP +.B @HK-MASTER@ +The fingerprint of the signing key identified by +.BR @MASTER-SEQUENCE@ . +.SS "Master repository parameters" +.TP +.I base-url +The base URL of the key repository (usually with a trailing +.RB ` / '). +Typically, this will be something like +.RB http://www.distorted.org.uk/vpn/ . +No default. +.TP +.I repos-base +The basename for the repository archive. Default is +.BR tripe-keys.tar.gz . +.TP +.I sig-base +The basename template for repository signatures. Default is +.BR tripe-keys.sig- . +The +.RB ` ' +portion, if any, is replaced by the sequence number of the key which +made the signature. +.TP +.I repos-url +The URL for the key repository tarball. Default is the concatenation of +.I base-url +and +.IR repos-base . +.TP +.I sig-url +The URL template for key repository signatures. Default is the +concatenation of +.I sig-url +and +.IR sig-base . +.TP +.I master-sequence +The sequence number of the master authority's current signing key. No +default. Usually set up automatically. +.TP +.I hk-master +The fingerprint of the current master signing key. No default. Usually +set up automatically. +.SS "Crypto parameters" +.TP +.I kx +Key-exchange algorithm to use. Either +.B dh +(integer Diffie-Hellman) +or +.B ec +(elliptic curves). The default is +.BR dh . +.TP +.I kx-param +Options to pass to +.B "key add" +when generating the parameters key. Default depends on +.I kx +as follows. +.TS +center; +| ci | ci | +| lb | lb |. +_ +kx kx-param +_ +dh \-LS \-b2048 \-B256 +ec \-Cnist-p256 +_ +.TE +.TP +.I kx-expire +Expiry time for generated keys. Default is +.BR "now + 1 day" . +.TP +.I hash +Hashing algorithm to use. Default is +.BR sha256 . +.TP +.I mac +Message authentication algorithm to use. Default is +.IB hash -hmac/ halfhashlen \fR, +where +.I halfhashlen +is half of +.IR hash 's +output length. +.TP +.I mgf +Mask-generation algorithm to use. Default is +.IB hash -mgf \fR. +This is probably a good choice. +.TP +.I cipher +Symmetric encryption scheme to use. Default is +.BR blowfish-cbc . +.TP +.I sig +Signature scheme to use. Must be one of those recognized by +.BR catsign (1). +Default is +.B dsa +if +.I kx +is +.BR dh , +or +.B ecdsa +if +.I kx +is +.BR ec . +.TP +.I sig-genalg +Key-generation algorithm for signing key. Default depends on +.I sig +as follows. +.TS +center; +| ci | ci | +| lb | lb |. +_ +sig sig-genalg +_ +kcdsa dh +dsa dsa +rsapcs1 rsa +rsapss rsa +ecdsa ec +eckcdsa ec +_ +.TE +.TP +.I sig-param +Signature-key generation parameters. Default depends on +.I sig-genalg +as follows. +.TS +center; +| ci | ci | +| lb | lb |. +_ +sig-genalg sig-param +_ +dh \-LS \-b2048 \-B256 +dsa \-b2048 \-B256 +rsa \-b2048 +ec \-Cnist-p256 +_ +.TE +.TP +.I sig-hash +Hash function to use for making signatures. Default is +.IR hash . +.TP +.I sig-fresh +Oldest time we should consider a signed archive to be fresh. Default is +.BR always , +meaning that all signatures are fresh. +.TP +.I sig-expire +Expiry time for master signing key. Default is +.BR forever . +.TP +.I fingerprint-hash +Hash function to use for key fingerprinting. Default is +.IR hash . +.SS "Master maintenance parameters" +.TP +.I base-dir +Local base directory for the repository files. This probably ought to +end in a +.RB ` / ' +character. No default. +.TP +.I repos-file +Filename for local repository tarball. Default is the concatenation of +.I base-dir +and +.IB repos-base . +.TP +.I sig-file +Tempalte for repository signatures. Default is the concatenation of +.I base-dir +and +.IR sig-base . +.TP +.I conf-file +Filename for local repository configuration file. Default is +.IB basedir /tripe-keys.conf \fR. +.SH "SEE ALSO" +.BR tripe (8), +.BR tripe\-keys (8). +.SH "AUTHOR" +Mark Wooding, diff --git a/doc/tripemon.1 b/doc/tripemon.1 new file mode 100644 index 00000000..55184938 --- /dev/null +++ b/doc/tripemon.1 @@ -0,0 +1,88 @@ +.\" -*-nroff-*- +.\". +.de hP +.IP +\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c +.. +.de VS +.sp 1 +.RS +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.ie t \{\ +. ds o \(bu +. ds ss \s8\u +. ds se \d\s0 +. if \n(.g \{\ +. fam P +. \} +.\} +.el \{\ +. ds o o +. ds ss ^ +. ds se +.\} +.TH tripemon 1 "4 October 2005" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption" +.SH "NAME" +tripemon \- graphical monitor for tripe +.SH "SYNOPSIS" +.B tripemon +.RB [ \-d +.IR dir ] +.RB [ \-a +.IR socket ] +.SH "DESCRIPTION" +The +.B tripemon +program is a fairly simple graphical monitor program for TrIPE. It's +not ever-so user-friendly in some ways, but it works fairly well. A +perusal of +.BR tripe-admin (5) +would help you understand it better. That said, describing the program +in detail is probably less interesting than letting you explore it for +yourself. +.PP +The command-line options available are: +.TP +.B "\-h, \-\-help" +Writes a brief description of the command-line options available to +standard output and exits with status 0. +.TP +.B "\-v, \-\-version" +Writes tripe's version number to standard output and exits with status +0. +.TP +.B "\-u, \-\-usage" +Writes a brief usage summary to standard output and exits with status 0. +.TP +.BI "\-d, \-\-directory=" dir +Make +.I dir +the current directory, before doing anything else. Note that all the +other filenames (e.g., the log output file) are relative to this +directory. The default directory, if this option is not specified, is +taken from the environment variable +.BR TRIPEDIR ; +if that's not defined either, a default default of +.BR /var/lib/tripe +is used. +.TP +.BI "\-a, \-\-admin=" socket +If connecting to a running server, connect to the socket named +.IR socket ; +if running a new server, instruct it to listen for admin +connections on +.IR socket . +.SH "SEE ALSO" +.BR tripectl (1), +.BR tripe\-admin (5), +.BR tripe (8). +.SH "AUTHOR" +Mark Wooding, diff --git a/peer.c b/peer.c index 0e2664bf..5e4c4f9b 100644 --- a/peer.c +++ b/peer.c @@ -53,6 +53,17 @@ const tunnel_ops *tunnels[] = { /*----- Main code ---------------------------------------------------------*/ +static void checktimers(void) +{ + sel_timer *t, **tt; + + tt = &sel.timers; + while (*tt) { + assert((*tt)->prev == tt); + tt = &(*tt)->next; + } +} + /* --- @p_pingtype@ --- * * * Arguments: @unsigned msg@ = message type @@ -340,12 +351,13 @@ static void p_pingwrite(ping *p, buf *b) 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; + T( trace(T_PEER, "peer: ping 0x%08lx done (rc = %d)", + (unsigned long)p->id, rc); ) +checktimers(); if (rc >= 0) p->func(rc, p->arg); } @@ -418,10 +430,12 @@ int p_pingsend(peer *p, ping *pg, unsigned type, pg->p = p; pg->func = func; pg->arg = arg; + if (p->pings) p->pings->prev = pg; p->pings = pg; gettimeofday(&tv, 0); tv.tv_sec += timeout; sel_addtimer(&sel, &pg->t, &tv, p_pingtimeout, pg); +checktimers(); T( trace(T_PEER, "peer: send %s 0x%08lx to %s", p_pingtype(type), (unsigned long)pg->id, p->spec.name); ) return (0); @@ -679,6 +693,15 @@ tidy_0: const char *p_name(peer *p) { return (p->spec.name); } +/* --- @p_spec@ --- * + * + * Arguments: @peer *p@ = pointer to a peer block + * + * Returns: Pointer to the peer's specification + */ + +const peerspec *p_spec(peer *p) { return (&p->spec); } + /* --- @p_find@ --- * * * Arguments: @const char *name@ = name to look up diff --git a/tripe-keys.in b/tripe-keys.in new file mode 100644 index 00000000..099f27e8 --- /dev/null +++ b/tripe-keys.in @@ -0,0 +1,347 @@ +#! @PYTHON@ +# -*-python-*- + +### External dependencies + +import catacomb as C +import os as OS +import sys as SYS +import sre as RX +import getopt as O +from cStringIO import StringIO +from errno import * +from stat import * + +### Useful regular expressions + +r_comment = RX.compile(r'^\s*(#|$)') +r_keyval = RX.compile(r'^\s*([-\w]+)(?:\s+(?!=)|\s*=\s*)(|\S|\S.*\S)\s*$') +r_dollarsubst = RX.compile(r'\$\{([-\w]+)\}') +r_atsubst = RX.compile(r'@([-\w]+)@') +r_nonalpha = RX.compile(r'\W') + +### Utility functions + +class SubprocessError (Exception): pass +class VerifyError (Exception): pass + +quis = OS.path.basename(SYS.argv[0]) +PACKAGE = "@PACKAGE@" +VERSION = "@VERSION@" + +def moan(msg): + SYS.stderr.write('%s: %s\n' % (quis, msg)) + +def die(msg, rc = 1): + moan(msg) + SYS.exit(rc) + +def subst(s, rx, map): + out = StringIO() + i = 0 + for m in rx.finditer(s): + out.write(s[i:m.start()] + map[m.group(1)]) + i = m.end() + out.write(s[i:]) + return out.getvalue() + +def rmtree(path): + try: + st = OS.stat(path) + except OSError, err: + if err.errno == ENOENT: + return + raise + if not S_ISDIR(st.st_mode): + OS.unlink(path) + else: + cwd = OS.getcwd() + try: + OS.chdir(path) + for i in OS.listdir('.'): + rmtree(i) + finally: + OS.chdir(cwd) + OS.rmdir(path) + +def zap(file): + try: + OS.unlink(file) + except OSError, err: + if err.errno == ENOENT: return + raise + +def run(args): + args = map(conf_subst, args.split()) + nargs = [] + for a in args: + if len(a) > 0 and a[0] != '!': + nargs += [a] + else: + nargs += a[1:].split() + args = nargs + print '+ %s' % ' '.join(args) + rc = OS.spawnvp(OS.P_WAIT, args[0], args) + if rc != 0: + raise SubprocessError, rc + +def hexhyphens(bytes): + out = StringIO() + for i in xrange(0, len(bytes)): + if i > 0 and i % 4 == 0: out.write('-') + out.write('%02x' % ord(bytes[i])) + return out.getvalue() + +def fingerprint(kf, ktag): + h = C.gchashes[conf['fingerprint-hash']]() + k = C.KeyFile(kf)[ktag].fingerprint(h, '-secret') + return h.done() + +### Read configuration + +class ConfigFileError (Exception): pass +conf = {} + +def conf_subst(s): return subst(s, r_dollarsubst, conf) + +## Read the file +def conf_read(f): + lno = 0 + for line in file(f): + lno += 1 + if r_comment.match(line): continue + if line[-1] == '\n': line = line[:-1] + match = r_keyval.match(line) + if not match: + raise ConfigFileError, "%s:%d: bad line `%s'" % (f, lno, line) + k, v = match.groups() + conf[k] = conf_subst(v) + +## Sift the wreckage +def conf_defaults(): + for k, v in [('sig-url', '${base-url}tripe-keys.sig'), + ('repos-url', '${base-url}tripe-keys.tar.gz'), + ('sig-file', '${base-dir}tripe-keys.sig'), + ('repos-file', '${base-dir}tripe-keys.tar.gz'), + ('conf-file', '${base-dir}tripe-keys.conf'), + ('kx', 'dh'), + ('kx-param', lambda: {'dh': '-LS -b2048 -B256', + 'ec': '-Cnist-p256'}[conf['kx']]), + ('kx-expire', 'now + 1 year'), + ('cipher', 'blowfish-cbc'), + ('hash', 'sha256'), + ('mgf', '${hash}-mgf'), + ('mac', lambda: '%s-hmac/%d' % + (conf['hash'], + C.gchashes[conf['hash']].hashsz * 4)), + ('sig', lambda: {'dh': 'dsa', 'ec': 'ecdsa'}[conf['kx']]), + ('sig-fresh', 'always'), + ('sig-genalg', lambda: {'kcdsa': 'dh', + 'dsa': 'dsa', + 'rsapkcs1': 'rsa', + 'rsapss': 'rsa', + 'ecdsa': 'ec', + 'eckcdsa': 'ec'}[conf['sig']]), + ('sig-param', lambda: {'dh': '-LS -b2048 -B256', + 'dsa': '-b2048 -B256', + 'ec': '-Cnist-p256', + 'rsa': '-b2048'}[conf['sig-genalg']]), + ('sig-hash', '${hash}'), + ('sig-expire', 'forever'), + ('fingerprint-hash', '${hash}')]: + try: + if k in conf: continue + if type(v) == str: + conf[k] = conf_subst(v) + else: + conf[k] = v() + except KeyError, exc: + if len(exc.args) == 0: raise + conf[k] = '' % exc.args[0] + +### Commands + +def version(fp = SYS.stdout): + fp.write('%s, %s version %s\n' % (quis, PACKAGE, VERSION)) + +def usage(fp): + fp.write('Usage: %s SUBCOMMAND [ARGS...]\n' % quis) + +def cmd_help(args): + if len(args) == 0: + version(SYS.stdout) + print + usage(SYS.stdout) + print """ +Key management utility for TrIPE. + +Options supported: + +-h, --help Show this help message. +-v, --version Show the version number. +-u, --usage Show pointlessly short usage string. + +Subcommands available: +""" + args = commands.keys() + args.sort() + for c in args: + func, min, max, help = commands[c] + print '%s %s' % (c, help) + +def cmd_setup(args): + OS.mkdir('repos') + + ## Generate the master key + run('''key -kmaster add + -a${sig-genalg} !${sig-param} + -e${sig-expire} -l -ttripe-keys-master ccsig + sig=${sig} hash=${sig-hash}''') + run('key -kmaster extract -f-secret repos/master.pub tripe-keys-master') + + ## Generate the parameters key + run('''key -krepos/param add + -a${kx}-param !${kx-param} + -eforever -tparam tripe-${kx}-param + cipher=${cipher} hash=${hash} mac=${mac} mgf=${mgf}''') + + ## Get fingerprints + print 'Setup OK: master key = %s' % \ + hexhyphens(fingerprint('repos/master.pub', 'tripe-keys-master')) + +def cmd_upload(args): + + ## Sanitize the repository directory + umask = OS.umask(0); OS.umask(umask) + mode = 0666 & ~umask + for f in OS.listdir('repos'): + ff = OS.path.join('repos', f) + if f.endswith('.old'): + OS.unlink(ff) + continue + OS.chmod(OS.path.join('repos', f), mode) + + ## Build the configuration file + v = {'HK-MASTER': hexhyphens(fingerprint('repos/master.pub', + 'tripe-keys-master'))} + fin = file('tripe-keys.master') + fout = file(conf_subst('${conf-file}.new'), 'w') + for line in fin: + fout.write(subst(line, r_atsubst, v)) + fin.close(); fout.close() + + ## Make and sign the repository archive + run('tar chozf ${repos-file}.new repos') + run('''catsign -kmaster sign -abdC -ktripe-keys-master + -o${sig-file}.new ${repos-file}.new''') + + ## Commit the changes + for i in ['conf-file', 'repos-file', 'sig-file']: + base = conf[i] + new = '%s.new' % base + OS.rename(new, base) + +def cmd_update(args): + cwd = OS.getcwd() + rmtree('tmp') + try: + + ## Fetch a new distribution + OS.mkdir('tmp') + OS.chdir('tmp') + run('wget -q -O tripe-keys.tar.gz ${repos-url}') + run('wget -q -O tripe-keys.sig ${sig-url}') + run('tar xfz tripe-keys.tar.gz') + + ## Verify the signature + want = C.bytes(r_nonalpha.sub('', conf['hk-master'])) + got = fingerprint('repos/master.pub', 'tripe-keys-master') + if want != got: raise VerifyError + run('''catsign -krepos/master.pub verify -avC -ktripe-keys-master + -t${sig-fresh} tripe-keys.sig tripe-keys.tar.gz''') + + ## OK: update our copy + OS.chdir(cwd) + if OS.path.exists('repos'): OS.rename('repos', 'repos.old') + OS.rename('tmp/repos', 'repos') + rmtree('repos.old') + + finally: + OS.chdir(cwd) + rmtree('tmp') + cmd_rebuild(args) + +def cmd_rebuild(args): + zap('keyring.pub') + for i in OS.listdir('repos'): + if i.startswith('peer-') and i.endswith('.pub'): + run('key -kkeyring.pub merge %s' % OS.path.join('repos', i)) + +def cmd_generate(args): + tag, = args + keyring_pub = 'peer-%s.pub' % tag + zap('keyring'); zap(keyring_pub) + run('key -kkeyring merge repos/param') + run('key -kkeyring add -a${kx} -pparam -e${kx-expire} -t%s tripe-${kx}' % + (tag,)) + run('key -kkeyring extract -f-secret %s %s' % (keyring_pub, tag)) + print 'Generated %s key = %s' % \ + (tag, + hexhyphens(fingerprint('repos/master.pub', 'tripe-keys-master'))) + + +def cmd_clean(args): + rmtree('repos') + rmtree('tmp') + for i in 'master', 'keyring.pub': + zap(i) + zap('%s.old' % i) + +### Main driver + +class UsageError (Exception): pass + +commands = {'help': (cmd_help, 0, 1, ''), + 'setup': (cmd_setup, 0, 0, ''), + 'upload': (cmd_upload, 0, 0, ''), + 'update': (cmd_update, 0, 0, ''), + 'clean': (cmd_clean, 0, 0, ''), + 'generate': (cmd_generate, 1, 1, 'TAG'), + 'rebuild': (cmd_rebuild, 0, 0, '')} + +def init(): + for f in ['tripe-keys.master', 'tripe-keys.conf']: + if OS.path.exists(f): + conf_read(f) + break + conf_defaults() +def main(argv): + try: + opts, args = O.getopt(argv[1:], 'hvu', + ['help', 'version', 'usage']) + except O.GetoptError, exc: + moan(exc) + usage(SYS.stderr) + SYS.exit(1) + for o, v in opts: + if o in ('-h', '--help'): + cmd_help([]) + SYS.exit(0) + elif o in ('-v', '--version'): + version(SYS.stdout) + SYS.exit(0) + elif o in ('-u', '--usage'): + usage(SYS.stdout) + SYS.exit(0) + if len(argv) < 2: + cmd_help([]) + else: + c = argv[1] + func, min, max, help = commands[c] + args = argv[2:] + if len(args) < min or (max > 0 and len(args) > max): + raise UsageError, (c, help) + func(args) + +init() +main(SYS.argv) diff --git a/tripe-keys.master b/tripe-keys.master new file mode 100644 index 00000000..5e6e9d18 --- /dev/null +++ b/tripe-keys.master @@ -0,0 +1,46 @@ +# tripe-keys configuration file +# +# see tripe-keys.conf(5) for full details + +### File locations (required) + +# The base URL for the repository files. Include the trailing slash if +# necessary. +# base-url = http://some.server.somewhere/blah/ + +# The local directory name for the repository files. Again, include the +# trailing slash if necessary. +# base-dir = /some/directory/blah/ + +### Crypto parameters + +# The key-exchange type. May be `dh' or `ec'. +# kx = dh + +# Key-generation parameters for key exchange group. +# kx-param = -LS -b2048 -B256 + +# Expiry time for peer key-exchange keys. +# kx-expire = now + 1 day + +# Symmetric encryption scheme to use. +# cipher = blowfish-cbc + +# Hash function to use. (We derive the MGF and MAC from this.) +# hash = sha256 + +# Signature scheme to use for signing/verifying repository archives. +# sig = dsa + +# How recently an archive must have been signed to be valid. +# sig-fresh = always + +# When the signing key expires. We're not good at rolling these over. +# sig-expire = forever + +### Master key hash + +# Since the master public key is contained within the repository, we must +# check its integrity: therefore we record its fingerprint here. This is +# filled in automatically by `tripe-keys upload'. Leave it as it is. +hk-master = @HK-MASTER@ diff --git a/tripe.h b/tripe.h index a358fb91..47719fe9 100644 --- a/tripe.h +++ b/tripe.h @@ -372,6 +372,7 @@ typedef struct admin_pingop { typedef struct admin { struct admin *next, *prev; /* Links to next and previous */ unsigned f; /* Various useful flags */ + unsigned ref; /* Reference counter */ #ifndef NTRACE unsigned seq; /* Sequence number for tracing */ #endif @@ -383,13 +384,12 @@ typedef struct admin { } admin; #define AF_DEAD 1u /* Destroy this admin block */ -#define AF_LOCK 2u /* Don't destroy it yet */ +#define AF_CLOSE 2u /* Client closed connection */ #define AF_NOTE 4u /* Catch notifications */ #define AF_WARN 8u /* Catch warning messages */ #ifndef NTRACE #define AF_TRACE 16u /* Catch tracing */ #endif -#define AF_CLOSE 32u /* Client closed connection */ #ifndef NTRACE # define AF_ALLMSGS (AF_NOTE | AF_TRACE | AF_WARN) @@ -918,10 +918,21 @@ extern peer *p_create(peerspec */*spec*/); * Arguments: @peer *p@ = pointer to a peer block * * Returns: A pointer to the peer's name. + * + * Use: Equivalent to @p_spec(p)->name@. */ extern const char *p_name(peer */*p*/); +/* --- @p_spec@ --- * + * + * Arguments: @peer *p@ = pointer to a peer block + * + * Returns: Pointer to the peer's specification + */ + +extern const peerspec *p_spec(peer */*p*/); + /* --- @p_find@ --- * * * Arguments: @const char *name@ = name to look up diff --git a/tripemon.in b/tripemon.in new file mode 100644 index 00000000..7ab68882 --- /dev/null +++ b/tripemon.in @@ -0,0 +1,1296 @@ +#! @PYTHON@ +# -*-python-*- + +#----- Dependencies --------------------------------------------------------- + +import socket as S +from sys import argv, exit, stdin, stdout, stderr +import os as OS +from os import environ +import sets as SET +import getopt as O +import time as T +import sre as RX +from cStringIO import StringIO + +import pygtk +pygtk.require('2.0') +import gtk as G +import gobject as GO +import gtk.gdk as GDK + +#----- Configuration -------------------------------------------------------- + +tripedir = "@configdir@" +socketdir = "@socketdir@" +PACKAGE = "@PACKAGE@" +VERSION = "@VERSION@" + +debug = False + +#----- Utility functions ---------------------------------------------------- + +## Program name, shorn of extraneous stuff. +quis = OS.path.basename(argv[0]) + +def moan(msg): + """Report a message to standard error.""" + stderr.write('%s: %s\n' % (quis, msg)) + +def die(msg, rc = 1): + """Report a message to standard error and exit.""" + moan(msg) + exit(rc) + +rx_space = RX.compile(r'\s+') +rx_ordinary = RX.compile(r'[^\\\'\"\s]+') +rx_weird = RX.compile(r'([\\\'])') +rx_time = RX.compile(r'^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)$') +rx_num = RX.compile(r'^[-+]?\d+$') + +c_red = GDK.color_parse('red') + +def getword(s): + """Pull a word from the front of S, handling quoting according to the + tripe-admin(5) rules. Returns the word and the rest of S, or (None, None) + if there are no more words left.""" + i = 0 + m = rx_space.match(s, i) + if m: i = m.end() + r = '' + q = None + if i >= len(s): + return None, None + while i < len(s) and (q or not s[i].isspace()): + m = rx_ordinary.match(s, i) + if m: + r += m.group() + i = m.end() + elif s[i] == '\\': + r += s[i + 1] + i += 2 + elif s[i] == q: + q = None + i += 1 + elif not q and s[i] == '`' or s[i] == "'": + q = "'" + i += 1 + elif not q and s[i] == '"': + q = '"' + i += 1 + else: + r += s[i] + i += 1 + if q: + raise SyntaxError, 'missing close quote' + m = rx_space.match(s, i) + if m: i = m.end() + return r, s[i:] + +def quotify(s): + """Quote S according to the tripe-admin(5) rules.""" + m = rx_ordinary.match(s) + if m and m.end() == len(s): + return s + else: + return "'" + rx_weird.sub(r'\\\1', s) + "'" + +#----- Random bits of infrastructure ---------------------------------------- + +class struct (object): + """Simple object which stores attributes and has a sensible construction + syntax.""" + def __init__(me, **kw): + me.__dict__.update(kw) + +class peerinfo (struct): pass +class pingstate (struct): pass + +def invoker(func): + """Return a function which throws away its arguments and calls FUNC. (If + for loops worked by binding rather than assignment then we wouldn't need + this kludge.""" + return lambda *hunoz, **hukairz: func() + +class HookList (object): + """I maintain a list of functions, and provide the ability to call them + when something interesting happens. The functions are called in the order + they were added to the list, with all the arguments. If a function returns + a non-None result, no further functions are called.""" + def __init__(me): + me.list = [] + def add(me, func, obj): + me.list.append((obj, func)) + def prune(me, obj): + new = [] + for o, f in me.list: + if o is not obj: + new.append((o, f)) + me.list = new + def run(me, *args, **kw): + for o, hook in me.list: + rc = hook(*args, **kw) + if rc is not None: return rc + return None + +class HookClient (object): + def __init__(me): + me.hooks = SET.Set() + def hook(me, hk, func): + hk.add(func, me) + me.hooks.add(hk) + def unhook(me, hk): + hk.prune(me) + me.hooks.discard(hk) + def unhookall(me): + for hk in me.hooks: + hk.prune(me) + me.hooks.clear() + ##def __del__(me): + ## print '%s dying' % me + +#----- Connections and commands --------------------------------------------- + +class ConnException (Exception): + """Some sort of problem occurred while communicating with the tripe + server.""" + pass + +class Error (ConnException): + """A command caused the server to issue a FAIL message.""" + pass + +class ConnectionFailed (ConnException): + """The connection failed while communicating with the server.""" + +jobid_seq = 0 +def jobid(): + """Return a job tag. Used for background commands.""" + global jobid_seq + jobid_seq += 1 + return 'bg-%d' % jobid_seq + +class BackgroundCommand (HookClient): + def __init__(me, conn, cmd): + HookClient.__init__(me) + me.conn = conn + me.tag = None + me.cmd = cmd + me.donehook = HookList() + me.losthook = HookList() + me.info = [] + me.submit() + me.hook(me.conn.disconnecthook, me.lost) + def submit(me): + me.conn.bgcommand(me.cmd, me) + def lost(me): + me.losthook.run() + me.unhookall() + def fail(me, msg): + me.conn.error("Unexpected error from server command `%s': %s" % + (me.cmd % msg)) + me.unhookall() + def ok(me): + me.donehook.run(me.info) + me.unhookall() + +class SimpleBackgroundCommand (BackgroundCommand): + def submit(me): + try: + BackgroundCommand.submit(me) + except ConnectionFailed, err: + me.conn.error('Unexpected error communicating with server: %s' % msg) + raise + +class Connection (HookClient): + + """I represent a connection to the TrIPE server. I provide facilities for + sending commands and receiving replies. The connection is notional: the + underlying socket connection can come and go under our feet. + + Useful attributes: + connectedp: whether the connection is active + connecthook: called when we have connected + disconnecthook: called if we have disconnected + notehook: called with asynchronous notifications + errorhook: called if there was a command error""" + + def __init__(me, sockname): + """Make a new connection to the server listening to SOCKNAME. In fact, + we're initially disconnected, to allow the caller to get his life in + order before opening the floodgates.""" + HookClient.__init__(me) + me.sockname = sockname + me.sock = None + me.connectedp = False + me.connecthook = HookList() + me.disconnecthook = HookList() + me.errorhook = HookList() + me.inbuf = '' + me.info = [] + me.waitingp = False + me.bgcmd = None + me.bgmap = {} + def connect(me): + "Connect to the server. Runs connecthook if it works.""" + if me.sock: return + sock = S.socket(S.AF_UNIX, S.SOCK_STREAM) + try: + sock.connect(me.sockname) + except S.error, err: + me.error('error opening connection: %s' % err[1]) + me.disconnecthook.run() + return + sock.setblocking(0) + me.socketwatch = GO.io_add_watch(sock, GO.IO_IN, me.ready) + me.sock = sock + me.connectedp = True + me.connecthook.run() + def disconnect(me): + "Disconnects from the server. Runs disconnecthook." + if not me.sock: return + GO.source_remove(me.socketwatch) + me.sock.close() + me.sock = None + me.connectedp = False + me.disconnecthook.run() + def error(me, msg): + """Reports an error on the connection.""" + me.errorhook.run(msg) + + def bgcommand(me, cmd, bg): + """Sends a background command and feeds it properly.""" + try: + me.bgcmd = bg + err = me.docommand(cmd) + if err: + bg.fail(err) + finally: + me.bgcmd = None + def command(me, cmd): + """Sends a command to the server. Returns a list of INFO responses. Do + not use this for backgrounded commands: create a BackgroundCommand + instead. Raises apprpopriate exceptions on error, but doesn't send + report them to the errorhook.""" + err = me.docommand(cmd) + if err: + raise Error, err + return me.info + def docommand(me, cmd): + if not me.sock: + raise ConnException, 'not connected' + if debug: print ">>> %s" % cmd + me.sock.sendall(cmd + '\n') + me.waitingp = True + me.info = [] + try: + me.sock.setblocking(1) + while True: + rc, err = me.collect() + if rc: break + finally: + me.waitingp = False + me.sock.setblocking(0) + if len(me.inbuf) > 0: + GO.idle_add(lambda: me.flushbuf() and False) + return err + def simplecmd(me, cmd): + """Like command(), but reports errors via the errorhook as well as + raising exceptions.""" + try: + i = me.command(cmd) + except Error, msg: + me.error("Unexpected error from server command `%s': %s" % (cmd, msg)) + raise + except ConnectionFailed, msg: + me.error("Unexpected error communicating with server: %s" % msg); + raise + return i + def ready(me, sock, condition): + try: + me.collect() + except ConnException, msg: + me.error("Error watching server connection: %s" % msg) + if me.sock: + me.disconnect() + me.connect() + return True + def collect(me): + data = me.sock.recv(16384) + if data == '': + me.disconnect() + raise ConnectionFailed, 'server disconnected' + me.inbuf += data + return me.flushbuf() + def flushbuf(me): + while True: + nl = me.inbuf.find('\n') + if nl < 0: break + line = me.inbuf[:nl] + if debug: print "<<< %s" % line + me.inbuf = me.inbuf[nl + 1:] + tag, line = getword(line) + rc, err = me.parseline(tag, line) + if rc: return rc, err + return False, None + def parseline(me, code, line): + if code == 'BGDETACH': + if not me.bgcmd: + raise ConnectionFailed, 'unexpected detach' + me.bgcmd.tag = line + me.bgmap[line] = me.bgcmd + me.waitingp = False + me.bgcmd = None + return True, None + elif code == 'BGINFO': + tag, line = getword(line) + me.bgmap[tag].info.append(line) + return False, None + elif code == 'BGFAIL': + tag, line = getword(line) + me.bgmap[tag].fail(line) + del me.bgmap[tag] + return False, None + elif code == 'BGOK': + tag, line = getword(line) + me.bgmap[tag].ok() + del me.bgmap[tag] + return False, None + elif code == 'INFO': + if not me.waitingp or me.bgcmd: + raise ConnectionFailed, 'unexpected INFO response' + me.info.append(line) + return False, None + elif code == 'OK': + if not me.waitingp or me.bgcmd: + raise ConnectionFailed, 'unexpected OK response' + return True, None + elif code == 'FAIL': + if not me.waitingp: + raise ConnectionFailed, 'unexpected FAIL response' + return True, line + else: + raise ConnectionFailed, 'unknown response code `%s' % code + +class Monitor (Connection): + """I monitor a TrIPE server, noticing when it changes state and keeping + track of its peers. I also provide facilities for sending the server + commands and collecting the answers. + + Useful attributes: + addpeerhook: called with a new Peer when the server adds one + delpeerhook: called with a Peer when the server kills one + tracehook: called with a trace message + warnhook: called with a warning message + peers: mapping from names to Peer objects""" + def __init__(me, sockname): + """Initializes the monitor.""" + Connection.__init__(me, sockname) + me.addpeerhook = HookList() + me.delpeerhook = HookList() + me.tracehook = HookList() + me.warnhook = HookList() + me.notehook = HookList() + me.hook(me.connecthook, me.connected) + me.delay = [] + me.peers = {} + def addpeer(me, peer): + if peer not in me.peers: + p = Peer(me, peer) + me.peers[peer] = p + me.addpeerhook.run(p) + def delpeer(me, peer): + if peer in me.peers: + p = me.peers[peer] + me.delpeerhook.run(p) + p.dead() + del me.peers[peer] + def updatelist(me, peers): + newmap = {} + for p in peers: + newmap[p] = True + if p not in me.peers: + me.addpeer(p) + oldpeers = me.peers.copy() + for p in oldpeers: + if p not in newmap: + me.delpeer(p) + def connected(me): + try: + me.simplecmd('WATCH -A+wnt') + me.updatelist([s.strip() for s in me.simplecmd('LIST')]) + except ConnException: + me.disconnect() + return + def parseline(me, code, line): + ## Delay async messages until the current command is done. Otherwise the + ## handler for the async message might send another command before this + ## one's complete, and the whole edifice turns to jelly. + ## + ## No, this isn't the server's fault. If we rely on the server to delay + ## notifications then there's a race between when we send a command and + ## when the server gets it. + if me.waitingp and code in ('TRACE', 'WARN', 'NOTE'): + if len(me.delay) == 0: GO.idle_add(me.flushdelay) + me.delay.append((code, line)) + elif code == 'TRACE': + me.tracehook.run(line) + elif code == 'WARN': + me.warnhook.run(line) + elif code == 'NOTE': + note, line = getword(line) + me.notehook.run(note, line) + if note == 'ADD': + me.addpeer(getword(line)[0]) + elif note == 'KILL': + me.delpeer(line) + else: + ## Well, I asked for it. + pass + else: + return Connection.parseline(me, code, line) + return False, None + def flushdelay(me): + delay = me.delay + me.delay = [] + for tag, line in delay: + me.parseline(tag, line) + return False + +def parseinfo(info): + """Parse key=value output into a dictionary.""" + d = {} + for i in info: + for w in i.split(' '): + q = w.index('=') + d[w[:q]] = w[q + 1:] + return d + +class Peer (object): + """I represent a TrIPE peer. Useful attributes are: + + name: peer's name + addr: human-friendly representation of the peer's address + ifname: interface associated with the peer + alivep: true if the peer hasn't been killed + deadhook: called with no arguments when the peer is killed""" + def __init__(me, monitor, name): + me.mon = monitor + me.name = name + addr = me.mon.simplecmd('ADDR %s' % name)[0].split(' ') + if addr[0] == 'INET': + ipaddr, port = addr[1:] + try: + name = S.gethostbyaddr(ipaddr)[0] + me.addr = 'INET %s:%s [%s]' % (name, port, ipaddr) + except S.herror: + me.addr = 'INET %s:%s' % (ipaddr, port) + else: + me.addr = ' '.join(addr) + me.ifname = me.mon.simplecmd('IFNAME %s' % me.name)[0] + me.__dict__.update(parseinfo(me.mon.simplecmd('PEERINFO %s' % me.name))) + me.deadhook = HookList() + me.alivep = True + def dead(me): + me.alivep = False + me.deadhook.run() + +#----- Window management cruft ---------------------------------------------- + +class MyWindowMixin (G.Window, HookClient): + """Mixin for windows which call a closehook when they're destroyed.""" + def mywininit(me): + me.closehook = HookList() + HookClient.__init__(me) + me.connect('destroy', invoker(me.close)) + def close(me): + me.closehook.run() + me.destroy() + me.unhookall() +class MyWindow (MyWindowMixin): + """A window which calls a closehook when it's destroyed.""" + def __init__(me, kind = G.WINDOW_TOPLEVEL): + G.Window.__init__(me, kind) + me.mywininit() +class MyDialog (G.Dialog, MyWindowMixin, HookClient): + """A dialogue box with a closehook and sensible button binding.""" + def __init__(me, title = None, flags = 0, buttons = []): + """The buttons are a list of (STOCKID, THUNK) pairs: call the appropriate + THUNK when the button is pressed. The others are just like GTK's Dialog + class.""" + i = 0 + br = [] + me.rmap = [] + for b, f in buttons: + br.append(b) + br.append(i) + me.rmap.append(f) + i += 1 + G.Dialog.__init__(me, title, None, flags, tuple(br)) + HookClient.__init__(me) + me.mywininit() + me.set_default_response(i - 1) + me.connect('response', me.respond) + def respond(me, hunoz, rid, *hukairz): + if rid >= 0: me.rmap[rid]() + +class WindowSlot (HookClient): + """A place to store a window. If the window is destroyed, remember this; + when we come to open the window, raise it if it already exists; otherwise + make a new one.""" + def __init__(me, createfunc): + """Constructor: CREATEFUNC must return a new Window which supports the + closehook protocol.""" + HookClient.__init__(me) + me.createfunc = createfunc + me.window = None + def open(me): + """Opens the window, creating it if necessary.""" + if me.window: + me.window.window.raise_() + else: + me.window = me.createfunc() + me.hook(me.window.closehook, me.closed) + def closed(me): + me.unhook(me.window.closehook) + me.window = None + +class ValidationError (Exception): + """Raised by ValidatingEntry.get_text() if the text isn't valid.""" + pass +class ValidatingEntry (G.Entry): + """Like an Entry, but makes the text go red if the contents are invalid. + If get_text is called, and the text is invalid, ValidationError is + raised.""" + def __init__(me, valid, text = '', size = -1, *arg, **kw): + """Make an Entry. VALID is a regular expression or a predicate on + strings. TEXT is the default text to insert. SIZE is the size of the + box to set, in characters (ish). Other arguments are passed to Entry.""" + G.Entry.__init__(me, *arg, **kw) + me.connect("changed", me.check) + if callable(valid): + me.validate = valid + else: + me.validate = RX.compile(valid).match + me.ensure_style() + me.c_ok = me.get_style().text[G.STATE_NORMAL] + me.c_bad = c_red + if size != -1: me.set_width_chars(size) + me.set_activates_default(True) + me.set_text(text) + me.check() + def check(me, *hunoz): + if me.validate(G.Entry.get_text(me)): + me.validp = True + me.modify_text(G.STATE_NORMAL, me.c_ok) + else: + me.validp = False + me.modify_text(G.STATE_NORMAL, me.c_bad) + def get_text(me): + if not me.validp: + raise ValidationError + return G.Entry.get_text(me) + +def numericvalidate(min = None, max = None): + """Validation function for numbers. Entry must consist of an optional sign + followed by digits, and the resulting integer must be within the given + bounds.""" + return lambda x: (rx_num.match(x) and + (min is None or long(x) >= min) and + (max is None or long(x) <= max)) + +#----- Various minor dialog boxen ------------------------------------------- + +GPL = """This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.""" + +class AboutBox (G.AboutDialog, MyWindowMixin): + """The program `About' box.""" + def __init__(me): + G.AboutDialog.__init__(me) + me.mywininit() + me.set_name('TrIPEmon') + me.set_version(VERSION) + me.set_license(GPL) + me.set_authors(['Mark Wooding']) + me.connect('unmap', invoker(me.close)) + me.show() +aboutbox = WindowSlot(AboutBox) + +def moanbox(msg): + """Report an error message in a window.""" + d = G.Dialog('Error from %s' % quis, + flags = G.DIALOG_MODAL, + buttons = ((G.STOCK_OK, G.RESPONSE_NONE))) + label = G.Label(msg) + label.set_padding(20, 20) + d.vbox.pack_start(label) + label.show() + d.run() + d.destroy() + +#----- Logging windows ------------------------------------------------------ + +class LogModel (G.ListStore): + """A simple list of log messages.""" + def __init__(me, columns): + """Call with a list of column names. All must be strings. We add a time + column to the left.""" + me.cols = ('Time',) + columns + G.ListStore.__init__(me, *((GO.TYPE_STRING,) * len(me.cols))) + def add(me, *entries): + """Adds a new log message, with a timestamp.""" + now = T.strftime('%Y-%m-%d %H:%M:%S') + me.append((now,) + entries) + +class TraceLogModel (LogModel): + """Log model for trace messages.""" + def __init__(me): + LogModel.__init__(me, ('Message',)) + def notify(me, line): + """Call with a new trace message.""" + me.add(line) + +class WarningLogModel (LogModel): + """Log model for warnings. We split the category out into a separate + column.""" + def __init__(me): + LogModel.__init__(me, ('Category', 'Message')) + def notify(me, line): + """Call with a new warning message.""" + me.add(*getword(line)) + +class LogViewer (MyWindow): + """Log viewer window. Nothing very exciting.""" + def __init__(me, model): + MyWindow.__init__(me) + me.model = model + scr = G.ScrolledWindow() + scr.set_policy(G.POLICY_AUTOMATIC, G.POLICY_AUTOMATIC) + me.list = G.TreeView(me.model) + me.closehook = HookList() + i = 0 + for c in me.model.cols: + me.list.append_column(G.TreeViewColumn(c, + G.CellRendererText(), + text = i)) + i += 1 + me.set_default_size(440, 256) + scr.add(me.list) + me.add(scr) + me.show_all() + +def makeactiongroup(name, acts): + """Creates an ActionGroup called NAME. ACTS is a list of tuples + containing: + ACT: an action name + LABEL: the label string for the action + ACCEL: accelerator string, or None + FUNC: thunk to call when the action is invoked""" + actgroup = G.ActionGroup(name) + for act, label, accel, func in acts: + a = G.Action(act, label, None, None) + if func: a.connect('activate', invoker(func)) + actgroup.add_action_with_accel(a, accel) + return actgroup + +class TraceOptions (MyDialog): + """Tracing options window.""" + def __init__(me, monitor): + MyDialog.__init__(me, title = 'Tracing options', + buttons = [(G.STOCK_CLOSE, me.destroy), + (G.STOCK_OK, me.ok)]) + me.mon = monitor + me.opts = [] + for o in me.mon.simplecmd('TRACE'): + char = o[0] + onp = o[1] + text = o[3].upper() + o[4:] + if char.isupper(): continue + ticky = G.CheckButton(text) + ticky.set_active(onp != ' ') + me.vbox.pack_start(ticky) + me.opts.append((char, ticky)) + me.show_all() + def ok(me): + on = [] + off = [] + for char, ticky in me.opts: + if ticky.get_active(): + on.append(char) + else: + off.append(char) + setting = ''.join(on) + '-' + ''.join(off) + me.mon.simplecmd('TRACE %s' % setting) + me.destroy() + +def unimplemented(*hunoz): + """Indicator of laziness.""" + moanbox("I've not written that bit yet.") + +class GridPacker (G.Table): + """Like a Table, but with more state: makes filling in the widgets + easier.""" + def __init__(me): + G.Table.__init__(me) + me.row = 0 + me.col = 0 + me.rows = 1 + me.cols = 1 + me.set_border_width(4) + me.set_col_spacings(4) + me.set_row_spacings(4) + def pack(me, w, width = 1, newlinep = False, + xopt = G.EXPAND | G.FILL | G.SHRINK, yopt = 0, + xpad = 0, ypad = 0): + """Packs a new widget. W is the widget to add. XOPY, YOPT, XPAD and + YPAD are as for Table. WIDTH is how many cells to take up horizontally. + NEWLINEP is whether to start a new line for this widget. Returns W.""" + if newlinep: + me.row += 1 + me.col = 0 + bot = me.row + 1 + right = me.col + width + if bot > me.rows or right > me.cols: + if bot > me.rows: me.rows = bot + if right > me.cols: me.cols = right + me.resize(me.rows, me.cols) + me.attach(w, me.col, me.col + width, me.row, me.row + 1, + xopt, yopt, xpad, ypad) + me.col += width + return w + def labelled(me, lab, w, newlinep = False, **kw): + """Packs a labelled widget. Other arguments are as for pack. Returns + W.""" + label = G.Label(lab) + label.set_alignment(1.0, 0) + me.pack(label, newlinep = newlinep, xopt = G.FILL) + me.pack(w, **kw) + return w + def info(me, label, text = None, len = 18, **kw): + e = G.Entry() + if text is not None: e.set_text(text) + e.set_width_chars(len) + e.set_editable(False) + me.labelled(label, e, **kw) + return e + +def xlate_time(t): + """Translate a time in tripe's stats format to something a human might + actually want to read.""" + if t == 'NEVER': return '(never)' + Y, M, D, h, m, s = map(int, rx_time.match(t).group(1, 2, 3, 4, 5, 6)) + return '%04d:%02d:%02d %02d:%02d:%02d' % (Y, M, D, h, m, s) +def xlate_bytes(b): + """Translate a number of bytes into something a human might want to read.""" + suff = 'B' + b = int(b) + for s in 'KMG': + if b < 4096: break + b /= 1024 + suff = s + return '%d %s' % (b, suff) + +## How to translate peer stats. Maps the stat name to a translation +## function. +statsxlate = \ + [('start-time', xlate_time), + ('last-packet-time', xlate_time), + ('last-keyexch-time', xlate_time), + ('bytes-in', xlate_bytes), + ('bytes-out', xlate_bytes), + ('keyexch-bytes-in', xlate_bytes), + ('keyexch-bytes-out', xlate_bytes), + ('ip-bytes-in', xlate_bytes), + ('ip-bytes-out', xlate_bytes)] + +## How to lay out the stats dialog. Format is (LABEL, FORMAT): LABEL is +## the label to give the entry box; FORMAT is the format string to write into +## the entry. +statslayout = \ + [('Start time', '%(start-time)s'), + ('Last key-exchange', '%(last-keyexch-time)s'), + ('Last packet', '%(last-packet-time)s'), + ('Packets in/out', + '%(packets-in)s (%(bytes-in)s) / %(packets-out)s (%(bytes-out)s)'), + ('Key-exchange in/out', + '%(keyexch-packets-in)s (%(keyexch-bytes-in)s) / %(keyexch-packets-out)s (%(keyexch-bytes-out)s)'), + ('IP in/out', + '%(ip-packets-in)s (%(ip-bytes-in)s) / %(ip-packets-in)s (%(ip-bytes-in)s)'), + ('Rejected packets', '%(rejected-packets)s')] + +class PeerWindow (MyWindow): + """Show information about a peer.""" + def __init__(me, monitor, peer): + MyWindow.__init__(me) + me.set_title('TrIPE statistics: %s' % peer.name) + me.mon = monitor + me.peer = peer + table = GridPacker() + me.add(table) + me.e = {} + def add(label, text = None): + me.e[label] = table.info(label, text, len = 42, newlinep = True) + add('Peer name', peer.name) + add('Tunnel', peer.tunnel) + add('Interface', peer.ifname) + add('Keepalives', + (peer.keepalive == '0' and 'never') or '%s s' % peer.keepalive) + add('Address', peer.addr) + add('Transport pings') + add('Encrypted pings') + for label, format in statslayout: add(label) + me.timeout = None + me.hook(me.mon.connecthook, me.tryupdate) + me.hook(me.mon.disconnecthook, me.stopupdate) + me.hook(me.closehook, me.stopupdate) + me.hook(me.peer.deadhook, me.dead) + me.hook(me.peer.pinghook, me.ping) + me.tryupdate() + me.ping() + me.show_all() + def update(me): + if not me.peer.alivep or not me.mon.connectedp: return False + stat = parseinfo(me.mon.simplecmd('STATS %s' % me.peer.name)) + for s, trans in statsxlate: + stat[s] = trans(stat[s]) + for label, format in statslayout: + me.e[label].set_text(format % stat) + return True + def tryupdate(me): + if me.timeout is None and me.update(): + me.timeout = GO.timeout_add(1000, me.update) + def stopupdate(me): + if me.timeout is not None: + GO.source_remove(me.timeout) + me.timeout = None + def dead(me): + me.set_title('TrIPE statistics: %s [defunct]' % me.peer.name) + me.e['Peer name'].set_text('%s [defunct]' % me.peer.name) + me.stopupdate() + def ping(me): + for ping in me.peer.ping, me.peer.eping: + s = '%d/%d' % (ping.ngood, ping.n) + if ping.ngood: + s += '; %.2f ms (last %.1f ms)' % (ping.ttot/ping.ngood, ping.tlast); + me.e[ping.cmd].set_text(s) + +class AddPeerCommand (SimpleBackgroundCommand): + def __init__(me, conn, dlg, name, addr, port, + keepalive = None, tunnel = None): + me.name = name + me.addr = addr + me.port = port + me.keepalive = keepalive + me.tunnel = tunnel + cmd = StringIO() + cmd.write('ADD %s' % name) + cmd.write(' -background %s' % jobid()) + if keepalive is not None: cmd.write(' -keepalive %s' % keepalive) + if tunnel is not None: cmd.write(' -tunnel %s' % tunnel) + cmd.write(' INET %s %s' % (addr, port)) + SimpleBackgroundCommand.__init__(me, conn, cmd.getvalue()) + me.hook(me.donehook, invoker(dlg.destroy)) + def fail(me, err): + token, msg = getword(str(err)) + if token in ('resolve-error', 'resolver-timeout'): + moanbox("Unable to resolve hostname `%s'" % me.addr) + elif token == 'peer-create-fail': + moanbox("Couldn't create new peer `%s'" % me.name) + elif token == 'peer-exists': + moanbox("Peer `%s' already exists" % me.name) + else: + moanbox("Unexpected error from server command `ADD': %s" % err) + +class AddPeerDialog (MyDialog): + def __init__(me, monitor): + MyDialog.__init__(me, 'Add peer', + buttons = [(G.STOCK_CANCEL, me.destroy), + (G.STOCK_OK, me.ok)]) + me.mon = monitor + table = GridPacker() + me.vbox.pack_start(table) + me.e_name = table.labelled('Name', + ValidatingEntry(r'^[^\s.:]+$', '', 16), + width = 3) + me.e_addr = table.labelled('Address', + ValidatingEntry(r'^[a-zA-Z0-9.-]+$', '', 24), + newlinep = True) + me.e_port = table.labelled('Port', + ValidatingEntry(numericvalidate(0, 65535), + '22003', + 5)) + me.c_keepalive = G.CheckButton('Keepalives') + me.l_tunnel = table.labelled('Tunnel', + G.combo_box_new_text(), + newlinep = True, width = 3) + me.tuns = me.mon.simplecmd('TUNNELS') + for t in me.tuns: + me.l_tunnel.append_text(t) + me.l_tunnel.set_active(0) + table.pack(me.c_keepalive, newlinep = True, xopt = G.FILL) + me.c_keepalive.connect('toggled', + lambda t: me.e_keepalive.set_sensitive\ + (t.get_active())) + me.e_keepalive = ValidatingEntry(r'^\d+[hms]?$', '', 5) + me.e_keepalive.set_sensitive(False) + table.pack(me.e_keepalive, width = 3) + me.show_all() + def ok(me): + try: + if me.c_keepalive.get_active(): + ka = me.e_keepalive.get_text() + else: + ka = None + t = me.l_tunnel.get_active() + if t == 0: + tun = None + else: + tun = me.tuns[t] + AddPeerCommand(me.mon, me, + me.e_name.get_text(), + me.e_addr.get_text(), + me.e_port.get_text(), + keepalive = ka, + tunnel = tun) + except ValidationError: + GDK.beep() + return + +class ServInfo (MyWindow): + def __init__(me, monitor): + MyWindow.__init__(me) + me.set_title('TrIPE server info') + me.mon = monitor + me.table = GridPacker() + me.add(me.table) + me.e = {} + def add(label, tag, text = None, **kw): + me.e[tag] = me.table.info(label, text, **kw) + add('Implementation', 'implementation') + add('Version', 'version', newlinep = True) + me.update() + me.hook(me.mon.connecthook, me.update) + me.show_all() + def update(me): + info = parseinfo(me.mon.simplecmd('SERVINFO')) + for i in me.e: + me.e[i].set_text(info[i]) + +class PingCommand (SimpleBackgroundCommand): + def __init__(me, conn, cmd, peer, func): + me.peer = peer + me.func = func + SimpleBackgroundCommand.__init__ \ + (me, conn, '%s -background %s %s' % (cmd, jobid(), peer.name)) + def ok(me): + tok, rest = getword(me.info[0]) + if tok == 'ping-ok': + me.func(me.peer, float(rest)) + else: + me.func(me.peer, None) + me.unhookall() + def fail(me, err): me.unhookall() + def lost(me): me.unhookall() + +class MonitorWindow (MyWindow): + + def __init__(me, monitor): + MyWindow.__init__(me) + me.set_title('TrIPE monitor') + me.mon = monitor + me.hook(me.mon.errorhook, me.report) + me.warnings = WarningLogModel() + me.hook(me.mon.warnhook, me.warnings.notify) + me.trace = TraceLogModel() + me.hook(me.mon.tracehook, me.trace.notify) + + me.warnview = WindowSlot(lambda: LogViewer(me.warnings)) + me.traceview = WindowSlot(lambda: LogViewer(me.trace)) + me.traceopts = WindowSlot(lambda: TraceOptions(me.mon)) + me.addpeerwin = WindowSlot(lambda: AddPeerDialog(me.mon)) + me.servinfo = WindowSlot(lambda: ServInfo(me.mon)) + + vbox = G.VBox() + me.add(vbox) + + me.ui = G.UIManager() + actgroup = makeactiongroup('monitor', + [('file-menu', '_File', None, None), + ('connect', '_Connect', 'C', me.mon.connect), + ('disconnect', '_Disconnect', 'D', me.mon.disconnect), + ('quit', '_Quit', 'Q', me.close), + ('server-menu', '_Server', None, None), + ('daemon', 'Run in _background', None, + lambda: me.mon.simplecmd('DAEMON')), + ('server-version', 'Server version', None, me.servinfo.open), + ('server-quit', 'Terminate server', None, + lambda: me.mon.simplecmd('QUIT')), + ('logs-menu', '_Logs', None, None), + ('show-warnings', 'Show _warnings', 'W', me.warnview.open), + ('show-trace', 'Show _trace', 'T', me.traceview.open), + ('trace-options', 'Trace _options...', None, me.traceopts.open), + ('help-menu', '_Help', None, None), + ('about', '_About tripemon...', None, aboutbox.open), + ('add-peer', '_Add peer...', 'A', me.addpeerwin.open), + ('kill-peer', '_Kill peer', None, me.killpeer), + ('force-kx', 'Force key e_xchange', None, me.forcekx)]) + uidef = ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''' + me.ui.insert_action_group(actgroup, 0) + me.ui.add_ui_from_string(uidef) + vbox.pack_start(me.ui.get_widget('/menubar'), expand = False) + me.add_accel_group(me.ui.get_accel_group()) + me.status = G.Statusbar() + + me.listmodel = G.ListStore(*(GO.TYPE_STRING,) * 6) + me.listmodel.set_sort_column_id(0, G.SORT_ASCENDING) + me.hook(me.mon.addpeerhook, me.addpeer) + me.hook(me.mon.delpeerhook, me.delpeer) + + scr = G.ScrolledWindow() + scr.set_policy(G.POLICY_AUTOMATIC, G.POLICY_AUTOMATIC) + me.list = G.TreeView(me.listmodel) + me.list.append_column(G.TreeViewColumn('Peer name', + G.CellRendererText(), + text = 0)) + me.list.append_column(G.TreeViewColumn('Address', + G.CellRendererText(), + text = 1)) + me.list.append_column(G.TreeViewColumn('T-ping', + G.CellRendererText(), + text = 2, + foreground = 3)) + me.list.append_column(G.TreeViewColumn('E-ping', + G.CellRendererText(), + text = 4, + foreground = 5)) + me.list.get_column(1).set_expand(True) + me.list.connect('row-activated', me.activate) + me.list.connect('button-press-event', me.buttonpress) + me.list.set_reorderable(True) + me.list.get_selection().set_mode(G.SELECTION_NONE) + scr.add(me.list) + vbox.pack_start(scr) + + vbox.pack_start(me.status, expand = False) + me.hook(me.mon.connecthook, me.connected) + me.hook(me.mon.disconnecthook, me.disconnected) + me.hook(me.mon.notehook, me.notify) + me.pinger = None + me.set_default_size(420, 180) + me.mon.connect() + me.show_all() + + def addpeer(me, peer): + peer.i = me.listmodel.append([peer.name, peer.addr, + '???', 'green', '???', 'green']) + peer.win = WindowSlot(lambda: PeerWindow(me.mon, peer)) + peer.pinghook = HookList() + peer.ping = pingstate(n = 0, ngood = 0, nmiss = 0, nmissrun = 0, + tlast = 0, ttot = 0, + tcol = 2, ccol = 3, cmd = 'Transport pings') + peer.eping = pingstate(n = 0, ngood = 0, nmiss = 0, nmissrun = 0, + tlast = 0, ttot = 0, + tcol = 4, ccol = 5, cmd = 'Encrypted pings') + def delpeer(me, peer): + me.listmodel.remove(peer.i) + def path_peer(me, path): + return me.mon.peers[me.listmodel[path][0]] + + def activate(me, l, path, col): + peer = me.path_peer(path) + peer.win.open() + def buttonpress(me, l, ev): + if ev.button == 3: + r = me.list.get_path_at_pos(ev.x, ev.y) + for i in '/peer-popup/kill-peer', '/peer-popup/force-kx': + me.ui.get_widget(i).set_sensitive(me.mon.connectedp and + r is not None) + if r: + me.menupeer = me.path_peer(r[0]) + else: + me.menupeer = None + me.ui.get_widget('/peer-popup').popup(None, None, None, + ev.button, ev.time) + + def killpeer(me): + me.mon.simplecmd('KILL %s' % me.menupeer.name) + def forcekx(me): + me.mon.simplecmd('FORCEKX %s' % me.menupeer.name) + + def reping(me): + if me.pinger is not None: + GO.source_remove(me.pinger) + me.pinger = GO.timeout_add(10000, me.ping) + me.ping() + def unping(me): + if me.pinger is not None: + GO.source_remove(me.pinger) + me.pinger = None + def ping(me): + for name in me.mon.peers: + p = me.mon.peers[name] + PingCommand(me.mon, 'PING', p, lambda p, t: me.pong(p, p.ping, t)) + PingCommand(me.mon, 'EPING', p, lambda p, t: me.pong(p, p.eping, t)) + return True + def pong(me, p, ping, t): + ping.n += 1 + if t is None: + ping.nmiss += 1 + ping.nmissrun += 1 + me.listmodel[p.i][ping.tcol] = '(miss %d)' % ping.nmissrun + me.listmodel[p.i][ping.ccol] = 'red' + else: + ping.ngood += 1 + ping.nmissrun = 0 + ping.tlast = t + ping.ttot += t + me.listmodel[p.i][ping.tcol] = '%.1f ms' % t + me.listmodel[p.i][ping.ccol] = 'black' + p.pinghook.run() + def setstatus(me, status): + me.status.pop(0) + me.status.push(0, status) + def notify(me, note, rest): + if note == 'DAEMON': + me.ui.get_widget('/menubar/server-menu/daemon').set_sensitive(False) + def connected(me): + me.setstatus('Connected (port %s)' % me.mon.simplecmd('PORT')[0]) + me.ui.get_widget('/menubar/server-menu/connect').set_sensitive(False) + for i in ('/menubar/server-menu/disconnect', + '/menubar/server-menu/server-version', + '/menubar/server-menu/add-peer', + '/menubar/server-menu/server-quit', + '/menubar/logs-menu/trace-options'): + me.ui.get_widget(i).set_sensitive(True) + me.ui.get_widget('/menubar/server-menu/daemon'). \ + set_sensitive(parseinfo(me.mon.simplecmd('SERVINFO'))['daemon'] == + 'nil') + me.reping() + def disconnected(me): + me.setstatus('Disconnected') + me.ui.get_widget('/menubar/server-menu/connect').set_sensitive(True) + for i in ('/menubar/server-menu/disconnect', + '/menubar/server-menu/server-version', + '/menubar/server-menu/add-peer', + '/menubar/server-menu/daemon', + '/menubar/server-menu/server-quit', + '/menubar/logs-menu/trace-options'): + me.ui.get_widget(i).set_sensitive(False) + me.unping() + def destroy(me): + if me.pinger is not None: + GO.source_remove(me.pinger) + def report(me, msg): + moanbox(msg) + return True + +#----- Parse options -------------------------------------------------------- + +def version(fp = stdout): + """Print the program's version number.""" + fp.write('%s, %s version %s\n' % (quis, PACKAGE, VERSION)) + +def usage(fp): + """Print a brief usage message for the program.""" + fp.write('Usage: %s [-d DIR] [-a SOCK]\n' % quis) + +def main(): + global tripedir + if 'TRIPEDIR' in environ: + tripedir = environ['TRIPEDIR'] + tripesock = '%s/%s' % (socketdir, 'tripesock') + + try: + opts, args = O.getopt(argv[1:], + 'hvud:a:', + ['help', 'version', 'usage', + 'directory=', 'admin-socket=']) + except O.GetoptError, exc: + moan(exc) + usage(stderr) + exit(1) + for o, v in opts: + if o in ('-h', '--help'): + version(stdout) + print + usage(stdout) + print """ +Graphical monitor for TrIPE VPN. + +Options supported: + +-h, --help Show this help message. +-v, --version Show the version number. +-u, --usage Show pointlessly short usage string. + +-d, --directory=DIR Use TrIPE directory DIR. +-a, --admin-socket=FILE Select socket to connect to.""" + exit(0) + elif o in ('-v', '--version'): + version(stdout) + exit(0) + elif o in ('-u', '--usage'): + usage(stdout) + exit(0) + elif o in ('-d', '--directory'): + tripedir = v + elif o in ('-a', '--admin-socket'): + tripesock = v + else: + raise "can't happen!" + if len(args) > 0: + usage(stderr) + exit(1) + + OS.chdir(tripedir) + mon = Monitor(tripesock) + root = MonitorWindow(mon) + HookClient().hook(root.closehook, exit) + G.main() + +if __name__ == '__main__': + main() + diff --git a/tun-bsd.c b/tun-bsd.c index 5efe5ef8..22f01e6f 100644 --- a/tun-bsd.c +++ b/tun-bsd.c @@ -80,8 +80,8 @@ static void t_read(int fd, unsigned mode, void *v) return; } IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: packet arrived"); - trace_block(T_PACKET, "tunnel: packet contents", buf_i, n); + trace(T_TUNNEL, "tun-bsd: packet arrived"); + trace_block(T_PACKET, "tun-bsd: packet contents", buf_i, n); }) buf_init(&b, buf_i, n); p_tun(t->p, &b); @@ -141,7 +141,7 @@ static tunnel *t_create(peer *p) t->n = n; sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t); sel_addfile(&t->f); - T( trace(T_TUNNEL, "tunnel: attached interface %s to peer `%s'", + T( trace(T_TUNNEL, "tun-bsd: attached interface %s to peer `%s'", t_ifname(t), p_name(p)); ) return (t); } @@ -159,8 +159,8 @@ static tunnel *t_create(peer *p) static void t_inject(tunnel *t, buf *b) { IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: inject decrypted packet"); - trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b)); + trace(T_TUNNEL, "tun-bsd: inject decrypted packet"); + trace_block(T_PACKET, "tun-bsd: packet contents", BBASE(b), BLEN(b)); }) write(t->f.fd, BBASE(b), BLEN(b)); } diff --git a/tun-linux.c b/tun-linux.c index 8c7278ce..bf449300 100644 --- a/tun-linux.c +++ b/tun-linux.c @@ -72,8 +72,8 @@ static void t_read(int fd, unsigned mode, void *v) return; } IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: packet arrived"); - trace_block(T_PACKET, "tunnel: packet contents", buf_i, n); + trace(T_TUNNEL, "tun-linux: packet arrived"); + trace_block(T_PACKET, "tun-linux: packet contents", buf_i, n); }) buf_init(&b, buf_i, n); p_tun(t->p, &b); @@ -112,6 +112,7 @@ static tunnel *t_create(peer *p) return (0); } fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + memset(&iff, 0, sizeof(iff)); iff.ifr_name[0] = 0; iff.ifr_flags = IFF_TUN | IFF_NO_PI; if ((f = ioctl(fd, TUNSETIFF, &iff)) < 0) { @@ -126,7 +127,7 @@ static tunnel *t_create(peer *p) sel_addfile(&t->f); iff.ifr_name[IFNAMSIZ - 1] = 0; strcpy(t->ifn, iff.ifr_name); - T( trace(T_TUNNEL, "tunnel: attached interface %s to peer `%s'", + T( trace(T_TUNNEL, "tun-linux: attached interface %s to peer `%s'", t->ifn, p_name(p)); ) return (t); } @@ -153,7 +154,7 @@ static const char *t_ifname(tunnel *t) { return (t->ifn); } static void t_inject(tunnel *t, buf *b) { IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: inject decrypted packet"); + trace(T_TUNNEL, "tun-linux: inject decrypted packet"); trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b)); }) write(t->f.fd, BBASE(b), BLEN(b)); diff --git a/tun-slip.c b/tun-slip.c index 2d94033b..2b74a774 100644 --- a/tun-slip.c +++ b/tun-slip.c @@ -110,7 +110,7 @@ static void t_read(int fd, unsigned mode, void *v) return; } IF_TRACING(T_TUNNEL, { - trace_block(T_PACKET, "tunnel: SLIP-encapsulated data", + trace_block(T_PACKET, "tun-slip: SLIP-encapsulated data", buf_t, n); }) @@ -128,11 +128,11 @@ static void t_read(int fd, unsigned mode, void *v) else if (st & ST_ESC) a_warn("TUN %s slip escape-end", t->sl->name); else if (q == t->buf) { - T( trace(T_TUNNEL, "tunnel: empty packet"); ) + T( trace(T_TUNNEL, "tun-slip: empty packet"); ) } else { IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: packet arrived"); - trace_block(T_PACKET, "tunnel: packet contents", + trace(T_TUNNEL, "tun-slip: packet arrived"); + trace_block(T_PACKET, "tun-slip: packet contents", t->buf, q - t->buf); }) buf_init(&b, t->buf, q - t->buf); @@ -208,7 +208,7 @@ static void t_init(void) for (;;) { if (*p == '/' || *p == '.') { slipcmd = p; - T( trace(T_TUNNEL, "tunnel: declared slip command `%s'", slipcmd); ) + T( trace(T_TUNNEL, "tun-slip: declared slip command `%s'", slipcmd); ) break; } uli = strtoul(p, &q, 0); @@ -237,7 +237,7 @@ static void t_init(void) sl->name[n] = 0; *tail = sl; tail = &sl->next; - T( trace(T_TUNNEL, "tunnel: declared slipif %d,%d=%s", + T( trace(T_TUNNEL, "tun-slip: declared slipif %d,%d=%s", sl->ifd, sl->ofd, sl->name); ) p = q + n + 1; if (!*p) @@ -273,7 +273,7 @@ static tunnel *t_create(peer *p) for (sl = slipifs; sl; sl = sl->next) { if (!(sl->f & F_INUSE)) { - T( trace(T_TUNNEL, "tunnel: %s using static slipif %s", + T( trace(T_TUNNEL, "tun-slip: %s using static slipif %s", p_name(p), sl->name); ) goto found; } @@ -327,7 +327,7 @@ static tunnel *t_create(peer *p) sl->kid = kid; sl->next = 0; sl->f = F_DYNAMIC; - T( trace(T_TUNNEL, "tunnel: %s using dynamic slipif %s", + T( trace(T_TUNNEL, "tun-slip: %s using dynamic slipif %s", p_name(p), sl->name); ) fdflags(pout[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); fdflags(pin[1], O_NONBLOCK, 0, FD_CLOEXEC, FD_CLOEXEC); @@ -387,8 +387,8 @@ static void t_inject(tunnel *t, buf *b) octet *q; IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: inject decrypted packet"); - trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b)); + trace(T_TUNNEL, "tun-slip: inject decrypted packet"); + trace_block(T_PACKET, "tun-slip: packet contents", BBASE(b), BLEN(b)); }) q = buf; @@ -402,7 +402,7 @@ static void t_inject(tunnel *t, buf *b) } *q++ = SL_END; IF_TRACING(T_TUNNEL, { - trace_block(T_PACKET, "tunnel: SLIP-encapsulated contents", + trace_block(T_PACKET, "tun-slip: SLIP-encapsulated contents", buf, q - buf); }) write(t->sl->ofd, buf, q - buf); @@ -428,7 +428,7 @@ static void t_destroy(tunnel *t) sl->f &= ~F_INUSE; } if (sl && (sl->f & F_DYNAMIC)) { - T( trace(T_TUNNEL, "tunnel: releasing dynamic slipif %s", sl->name); ) + T( trace(T_TUNNEL, "tun-slip: releasing dynamic slipif %s", sl->name); ) close(sl->ofd); close(sl->ifd); kill(sl->kid, SIGTERM); diff --git a/tun-unet.c b/tun-unet.c index a6b53eb9..c93803ee 100644 --- a/tun-unet.c +++ b/tun-unet.c @@ -94,8 +94,8 @@ static void t_read(int fd, unsigned mode, void *v) return; } IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: packet arrived"); - trace_block(T_PACKET, "tunnel: packet contents", buf_i, n); + trace(T_TUNNEL, "tun-unet: packet arrived"); + trace_block(T_PACKET, "tun-unet: packet contents", buf_i, n); }) buf_init(&b, buf_i, n); p_tun(t->p, &b); @@ -145,7 +145,7 @@ static tunnel *t_create(peer *p) t->p = p; sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t); sel_addfile(&t->f); - T( trace(T_TUNNEL, "tunnel: attached interface %s to peer `%s'", + T( trace(T_TUNNEL, "tun-unet: attached interface %s to peer `%s'", t_ifname(t), p_name(p)); ) return (t); } @@ -163,8 +163,8 @@ static tunnel *t_create(peer *p) static void t_inject(tunnel *t, buf *b) { IF_TRACING(T_TUNNEL, { - trace(T_TUNNEL, "tunnel: inject decrypted packet"); - trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b)); + trace(T_TUNNEL, "tun-unet: inject decrypted packet"); + trace_block(T_PACKET, "tun-unet: packet contents", BBASE(b), BLEN(b)); }) write(t->f.fd, BBASE(b), BLEN(b)); } -- [mdw]