--- /dev/null
+Known bugs in secnet
+
If you're using TUN/TAP on a platform other than Linux-2.4, see
http://vtun.sourceforge.net/tun/
-Note than TUN comes in two flavours, one (called 'tun' in the secnet
-config file) which has only one device file (usually /dev/net/tun) and
-the other (called 'tun-old') which has many device files (/dev/tun*).
-Linux-2.4 has new-style TUN, Linux-2.2, BSD and Solaris have old-style
-TUN.
+Note than TUN comes in several flavours. Their names in the
+configuration file are:
+ tun: Linux-2.4; only one device file (usually /dev/net/tun)
+ tun-old: Linux-2.2, BSD; device files /dev/tun*
+ tun-solaris: Solaris (not yet implemented)
** System and network configuration
If you are joining an existing VPN, read that VPN's documentation now.
It may supersede the next paragraph.
-You will need to allocate two IP addresses for use by secnet. One
-will be for the tunnel interface on your tunnel endpoint machine (i.e.
-the address you see in 'ifconfig' when you look at the tunnel
-interface). The other will be for secnet itself. These addresses
-should probably be allocated from the range used by your internal
-network: if you do this, you should provide appropriate proxy-ARP on
-the internal network interface of the machine running secnet (eg. add
-an entry net/ipv4/conf/eth_whatever/proxy_arp = 1 to /etc/sysctl.conf
-on Debian systems and run sysctl -p). Alternatively the addresses
-could be from some other range - this works well if the machine
-running secnet is the default route out of your network - but this
-requires more thought.
+In most configurations, you will need to allocate two IP addresses for
+use by secnet. One will be for the tunnel interface on your tunnel
+endpoint machine (i.e. the address you see in 'ifconfig' when you look
+at the tunnel interface). The other will be for secnet itself. These
+addresses should probably be allocated from the range used by your
+internal network: if you do this, you should provide appropriate
+proxy-ARP on the internal network interface of the machine running
+secnet (eg. add an entry net/ipv4/conf/eth_whatever/proxy_arp = 1 to
+/etc/sysctl.conf on Debian systems and run sysctl -p). Alternatively
+the addresses could be from some other range - this works well if the
+machine running secnet is the default route out of your network - but
+this requires more thought.
http://www.ucam.org/cam-grin/ may be useful.
# mkdir /etc/secnet
(Note: you may see the following warning while compiling
-conffile.tab.c; I believe this is a bison bug:
+conffile.tab.c; this is a bug in bison-1.28:
/usr/share/bison/bison.simple: In function `yyparse':
/usr/share/bison/bison.simple:285: warning: `yyval' might be used
uninitialized in this function
+
+You may if you wish apply the following patch to bison.simple:
+diff -pu -r1.28.0.1 -r1.28.0.3
+--- bison.s1 1999/08/30 19:23:24 1.28.0.1
++++ bison.s1 1999/08/30 21:15:18 1.28.0.3
+@@ -523,8 +523,14 @@ yydefault:
+ /* Do a reduction. yyn is the number of a rule to reduce with. */
+ yyreduce:
+ yylen = yyr2[yyn];
+- if (yylen > 0)
+- yyval = yyvsp[1-yylen]; /* implement default value of the action */
++
++ /* If yylen is nonzero, implement the default value of the action.
++ Otherwise, the following line sets yyval to the semantic value of
++ the lookahead token. This behavior is undocumented and bison
++ users should not rely upon it. Assigning to yyval
++ unconditionally makes the parser a bit smaller, and it avoids a
++ GCC warning that yyval may be used uninitialized. */
++ yyval = yyvsp[1-yylen];
+
+ #if YYDEBUG != 0
+ if (yydebug)
)
Any other warnings or errors should be reported to
$ gmake CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib"
XXX this should eventually be worked out automatically by 'configure'.]
-Generate a site file fragment for your site (see below), and submit it
-for inclusion in your VPN's 'sites' file. Download the vpn-sites file
-to /etc/secnet/sites - MAKE SURE YOU GET AN AUTHENTIC COPY because the
-sites file contains public keys for all the sites in the VPN. Use the
-make-secnet-sites program provided with the secnet distribution to
-convert the distributed sites file into one that can be included in a
-secnet configuration file:
+Generate a site file fragment for your site (see your VPN's
+documentation, or see below), and submit it for inclusion in your
+VPN's 'sites' file. Download the vpn-sites file to /etc/secnet/sites
+- MAKE SURE YOU GET AN AUTHENTIC COPY because the sites file contains
+public keys for all the sites in the VPN. Use the make-secnet-sites
+program provided with the secnet distribution to convert the
+distributed sites file into one that can be included in a secnet
+configuration file:
-# make-secnet-sites sites sites.conf
+# make-secnet-sites /etc/secnet/sites /etc/secnet/sites.conf
* Configuration
Should be reasonably obvious - edit /etc/secnet/secnet.conf as
-prompted by the comments. XXX Fuller documentation of the
-configuration file format should be forthcoming in time. Its syntax
-is described in the README file at the moment.
+prompted by the comments in example.conf. XXX Fuller documentation of
+the configuration file format should be forthcoming in time. Its
+syntax is described in the README file at the moment.
* Constructing your site file fragment
.PHONY: all clean realclean dist install distclean
PACKAGE:=secnet
-VERSION:=0.1.10
+VERSION:=0.1.11
@SET_MAKE@
serpent.o md5.o version.o tun.o slip.o sha1.o ipaddr.o log.o \
process.o @LIBOBJS@
-DISTFILES:=COPYING CREDITS INSTALL LICENSE.txt Makefile.in \
+DISTFILES:=BUGS COPYING CREDITS INSTALL LICENSE.txt Makefile.in \
NEWS NOTES README TODO \
alloca.c \
conffile.c conffile.fl conffile.h conffile.y \
Modular transform code: choice of block ciphers, modes, sequence
numbers / timestamps, etc. similar to IWJ's udptunnel
-* New in versino 0.1.11
+* New in version 0.1.11
+
+Lists of IP addresses in the configuration file can now include
+exclusions as well as inclusions. For example, you can specify all
+the hosts on a subnet except one as follows:
+
+networks "192.168.73.0/24","!192.168.73.70";
+
+(If you were only allowed inclusions, you'd have to specify that like
+this:
+networks "192.168.73.71/32","192.168.73.68/31","192.168.73.64/30",
+ "192.168.73.72/29","192.168.73.80/28","192.168.73.96/27",
+ "192.168.73.0/26","192.168.73.128/25";
+)
+
+secnet now ensures that it invokes userv-ipif with a non-overlapping
+list of subnets.
+
+There is a new command-line option, --sites-key or -s, that enables
+the configuration file key that's checked to determine the list of
+active sites (default "sites") to be changed. This enables a single
+configuration file to contain multiple cofigurations conveniently.
+
+NAKs are now sent when packets arrive that are not understood. The
+tunnel code initiates a key setup if it sees a NAK. Future
+developments should include configuration options that control this.
+
+The tunnel code notifies its peer when secnet is terminating, so the
+peer can close the session.
+
+The netlink "exclude-remote-networks" option has now been replaced by
+a "remote-networks" option; instead of specifying networks that no
+site may access, you specify the set of networks that remote sites are
+allowed to access. A sensible example: "192.168.0.0/16",
+"172.16.0.0/12", "10.0.0.0/8", "!your-local-network"
* New in version 0.1.10
sent when a key times out, or the tunnel is forcibly terminated for
some reason.
-XXX not yet implemented.
-
-8) i?,i?,NAK/msg8
+8) i?,i?,NAK (encoded as zero)
If the link-layer can't work out what to do with a packet (session has
gone away, etc.) it can transmit a NAK back to the sender. The sender
Makefile.in: autodep stuff
+Make it work using the distributed install.sh (which doesn't support -D)
dh.c: change format to binary from decimal string (without introducing
endianness problems)
-ipaddr.c: implement the useful functionality from ipaddr.py
+netlink.c: test the 'allow_route' option properly.
-netlink.c: investigate why 'default' routes don't appear to work
-(reported by JDA).
-Test the 'allow_route' option properly.
+process.c: capture output from children in sys_cmd() and log it
random.c: test
+resolver.c: ought to return a list of addresses for each address; the
+site code ought to remember them and try contacting them in turn.
+
rsa.c: check padding type, change format to binary from decimal string
(without introducing endianness problems)
slip.c: restart userv-ipif to cope with soft routes? Restart it if it
fails in use?
-userv-ipif doesn't like the same bit of network to be specified
-twice. Use the new functionality in ipaddr.c once it's done to prevent
-this.
-Work out why slip.c doesn't compile on Solaris-2.5.1
transform.c: separate the transforms into multiple parts, which can
then be combined in the configuration file. Will allow the user to
plug in different block ciphers, invent an authenticity-only mode,
etc.
-
-udp.c: actually send NAKs rather than just complaining.
return r;
}
-static struct subnet string_to_subnet(item_t *i, string_t desc)
-{
- struct subnet s;
- uint32_t a, b, c, d, n;
- uint32_t match;
-
- /* i is not guaranteed to be a string */
- if (i->type!=t_string) {
- cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
- }
-
- if (strcmp(i->data.string,"default")==0) {
- s.prefix=0;
- s.mask=0;
- s.len=0;
- return s;
- }
-
- /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
- NOT optional. The subnet mask is optional; if missing it is assumed
- to be /32. */
- match=sscanf(i->data.string,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
- if (match<4) {
- cfgfatal(i->loc,desc,"\"%s\" is not a valid "
- "subnet specification\n",i->data.string);
- }
- if (match<5) {
- n=32;
- }
- if (a>255 || b>255 || c>255 || d>255 || n>32) {
- cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
- }
- s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
- s.mask=n?(~0UL << (32-n)):0;
- s.len=n;
- if (s.prefix & ~s.mask) {
- cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
- "in mask\n",i->data.string);
- }
- return s;
-}
-
-uint32_t string_to_ipaddr(item_t *i, string_t desc)
-{
- uint32_t a, b, c, d;
- uint32_t match;
-
- /* i is not guaranteed to be a string */
- if (i->type!=t_string) {
- cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
- }
-
- match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
- if (match<4) {
- cfgfatal(i->loc,desc,"\"%s\" is not a valid "
- "IP address\n",i->data.string);
- }
- if (a>255 || b>255 || c>255 || d>255) {
- cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
- }
- return (a<<24)|(b<<16)|(c<<8)|(d);
-}
-
-void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
- string_t desc, struct cloc loc,
- struct subnet_list *sl)
-{
- list_t *l, *li;
- item_t *i;
- uint32_t e=0;
-
- sl->entries=0;
- sl->list=NULL;
- l=dict_lookup(dict, key);
- if (!l) {
- if (!required) return;
- cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key);
- }
- /* Count the items in the list */
- e=list_length(l);
- if (e==0) return;
- sl->entries=e;
- sl->list=safe_malloc(sizeof(struct subnet)*e, "dict_read_subnet_list");
- e=0;
- /* Fill in the list */
- for (li=l; li; li=li->next) {
- i=li->item;
- if (i->type!=t_string) {
- cfgfatal(loc,desc,"parameter \"%s\": all elements must "
- "be strings\n",key);
- }
- sl->list[e++]=string_to_subnet(i,desc);
- }
-}
-
uint32_t string_to_word(string_t s, struct cloc loc,
struct flagstr *f, string_t desc)
{
#else
#if SIZEOF_UNSIGNED_LONG_LONG==8
typedef unsigned long long uint64_t;
+typedef long long int64_t;
#elif SIZEOF_UNSIGNED_LONG==8
typedef unsigned long uint64_t;
+typedef long int64_t;
#else
#error I do not know what to use for a uint64_t.
#endif
/* Give us an unsigned 32-bit data type. */
#if SIZEOF_UNSIGNED_LONG==4
typedef unsigned long uint32_t;
+typedef long int32_t;
#elif SIZEOF_UNSIGNED_INT==4
typedef unsigned int uint32_t;
+typedef int int32_t;
#else
#error I do not know what to use for a uint32_t.
#endif
/* An unsigned 16-bit data type. */
#if SIZEOF_UNSIGNED_INT==2
typedef unsigned int uint16_t;
+typedef int int16_t;
#elif SIZEOF_UNSIGNED_SHORT==2
typedef unsigned short uint16_t;
+typedef short int16_t;
#else
#error I do not know what to use for a uint16_t.
#endif
#else
#if SIZEOF_UNSIGNED_LONG_LONG==8
typedef unsigned long long uint64_t;
+typedef long long int64_t;
#elif SIZEOF_UNSIGNED_LONG==8
typedef unsigned long uint64_t;
+typedef long int64_t;
#else
#error I do not know what to use for a uint64_t.
#endif
/* Give us an unsigned 32-bit data type. */
#if SIZEOF_UNSIGNED_LONG==4
typedef unsigned long uint32_t;
+typedef long int32_t;
#elif SIZEOF_UNSIGNED_INT==4
typedef unsigned int uint32_t;
+typedef int int32_t;
#else
#error I do not know what to use for a uint32_t.
#endif
/* An unsigned 16-bit data type. */
#if SIZEOF_UNSIGNED_INT==2
typedef unsigned int uint16_t;
+typedef int int16_t;
#elif SIZEOF_UNSIGNED_SHORT==2
typedef unsigned short uint16_t;
+typedef short int16_t;
#else
#error I do not know what to use for a uint16_t.
#endif
-secnet (0.1.10-1) unstable; urgency=low
+secnet (0.1.11-1) unstable; urgency=low
* New upstream version.
- -- Stephen Early <steve@greenend.org.uk> Sat, 29 Jun 2001 15:21:25 +0100
+ * Note configuration file format has changed slightly; re-run
+ make-secnet-sites to fix it
+
+ -- Stephen Early <steve@greenend.org.uk> Sat, 27 Oct 2001 15:00:00 +0100
+++ /dev/null
-secnet_0.1.9-1_i386.deb net extra
+++ /dev/null
-# Automatically added by dh_installdocs
-if [ "$1" = "configure" ]; then
- if [ -d /usr/doc -a ! -e /usr/doc/secnet -a -d /usr/share/doc/secnet ]; then
- ln -sf ../share/doc/secnet /usr/doc/secnet
- fi
-fi
-# End automatically added section
-# Automatically added by dh_installinit
-if [ -e "/etc/init.d/secnet" ]; then
- update-rc.d secnet defaults >/dev/null
- /etc/init.d/secnet start
-fi
-# End automatically added section
+++ /dev/null
-# Automatically added by dh_installinit
-if [ "$1" = "purge" ] ; then
- update-rc.d secnet remove >/dev/null
-fi
-# End automatically added section
+++ /dev/null
-# Automatically added by dh_installdocs
-if [ \( "$1" = "upgrade" -o "$1" = "remove" \) -a -L /usr/doc/secnet ]; then
- rm -f /usr/doc/secnet
-fi
-# End automatically added section
-# Automatically added by dh_installinit
-if [ -e "/etc/init.d/secnet" ]; then
- /etc/init.d/secnet stop
-fi
-# End automatically added section
dh_testdir
dh_testroot
# dh_installdebconf
- dh_installdocs INSTALL README NOTES TODO NEWS
+ dh_installdocs INSTALL README NOTES TODO NEWS BUGS CREDITS
dh_installexamples example.conf
# dh_installmenu
# dh_installlogrotate
+++ /dev/null
-shlibs:Depends=libc6 (>= 2.2.3-7), libgmp2
+/* The 'ipset' data structure and related algorithms in this file were
+ inspired by the 'ipaddr.py' library from Cendio Systems AB. */
+
#include "secnet.h"
#include <stdio.h>
+#include "ipaddr.h"
+
+#define DEFAULT_ALLOC 2
+#define EXTEND_ALLOC_BY 4
+
+struct subnet_list *subnet_list_new(void)
+{
+ struct subnet_list *r;
+ r=safe_malloc(sizeof(*r),"subnet_list_new:list");
+ r->entries=0;
+ r->alloc=DEFAULT_ALLOC;
+ r->list=safe_malloc(sizeof(*r->list)*r->alloc,"subnet_list_new:data");
+ return r;
+}
+
+void subnet_list_free(struct subnet_list *a)
+{
+ if (a->list) free(a->list);
+ free(a);
+}
+
+static void subnet_list_set_len(struct subnet_list *a, uint32_t l)
+{
+ struct subnet *nd;
+ uint32_t na;
+
+ if (l>a->alloc) {
+ na=a->alloc+EXTEND_ALLOC_BY;
+ nd=realloc(a->list,sizeof(*nd)*na);
+ if (!nd) {
+ fatal_perror("subnet_list_set_len: realloc");
+ }
+ a->alloc=na;
+ a->list=nd;
+ }
+ a->entries=l;
+}
+
+void subnet_list_append(struct subnet_list *a, uint32_t prefix, uint32_t len)
+{
+ struct subnet *sn;
+ subnet_list_set_len(a,a->entries+1);
+ sn=&a->list[a->entries-1];
+ sn->prefix=prefix;
+ sn->len=len;
+ sn->mask=len?(0xffffffff << (32-len)):0;
+}
-/* This file should eventually incorporate all the functionality of
- ipaddr.py */
+struct ipset *ipset_new(void)
+{
+ struct ipset *r;
+ r=safe_malloc(sizeof(*r),"ipset_new:set");
+ r->l=0;
+ r->a=DEFAULT_ALLOC;
+ r->d=safe_malloc(sizeof(*r->d)*r->a,"ipset_new:data");
+ return r;
+}
-bool_t subnet_match(struct subnet *s, uint32_t address)
+void ipset_free(struct ipset *a)
{
- return (s->prefix==(address&s->mask));
+ if (a->d) free(a->d);
+ free(a);
}
-bool_t subnet_matches_list(struct subnet_list *list, uint32_t address)
+#ifdef DEBUG
+static void ipset_dump(struct ipset *a, string_t name)
{
uint32_t i;
- for (i=0; i<list->entries; i++) {
- if (list->list[i].prefix == (address&list->list[i].mask)) return True;
+
+ printf("%s: ",name);
+ for (i=0; i<a->l; i++) {
+ printf("[%08x-%08x] ",a->d[i].a,a->d[i].b);
}
- return False;
+ printf("\n");
}
+#endif
-bool_t subnets_intersect(struct subnet a, struct subnet b)
+struct ipset *ipset_from_subnet(struct subnet s)
{
- uint32_t mask=a.mask&b.mask;
- return ((a.prefix&mask)==(b.prefix&mask));
+ struct ipset *r;
+
+ r=ipset_new();
+ r->l=1;
+ r->d[0].a=s.prefix;
+ r->d[0].b=s.prefix | (~s.mask);
+ return r;
}
-bool_t subnet_intersects_with_list(struct subnet a, struct subnet_list *b)
+struct ipset *ipset_from_subnet_list(struct subnet_list *l)
{
+ struct ipset *r, *a, *b;
uint32_t i;
- for (i=0; i<b->entries; i++) {
- if (subnets_intersect(a,b->list[i])) return True;
+ r=ipset_new();
+ for (i=0; i<l->entries; i++) {
+ a=ipset_from_subnet(l->list[i]);
+ b=ipset_union(r,a);
+ ipset_free(a);
+ ipset_free(r);
+ r=b;
}
- return False;
+ return r;
}
-bool_t subnet_lists_intersect(struct subnet_list *a, struct subnet_list *b)
+static void ipset_set_len(struct ipset *a, uint32_t l)
+{
+ struct iprange *nd;
+ uint32_t na;
+
+ if (l>a->a) {
+ na=a->a+EXTEND_ALLOC_BY;
+ nd=realloc(a->d,sizeof(*nd)*na);
+ if (!nd) {
+ fatal_perror("ipset_set_len: realloc");
+ }
+ a->a=na;
+ a->d=nd;
+ }
+ a->l=l;
+}
+
+static void ipset_append_range(struct ipset *a, struct iprange r)
+{
+ ipset_set_len(a,a->l+1);
+ a->d[a->l-1]=r;
+}
+
+#define max(a,b) (a>b?a:b)
+struct ipset *ipset_union(struct ipset *a, struct ipset *b)
+{
+ struct ipset *c;
+ struct iprange r;
+ uint32_t ia,ib;
+
+ c=ipset_new();
+ ia=0; ib=0;
+ while (ia<a->l || ib<b->l) {
+ if (ia<a->l)
+ if (ib<b->l)
+ if (a->d[ia].a < b->d[ib].a)
+ r=a->d[ia++];
+ else
+ r=b->d[ib++];
+ else
+ r=a->d[ia++];
+ else
+ r=b->d[ib++];
+
+ if (c->l==0)
+ ipset_append_range(c,r);
+ else if (r.a <= c->d[c->l-1].b+1)
+ /* Extends (or is consumed by) the last range */
+ c->d[c->l-1].b=max(c->d[c->l-1].b, r.b);
+ else
+ ipset_append_range(c,r);
+ }
+ return c;
+}
+
+struct ipset *ipset_intersection(struct ipset *a, struct ipset *b)
+{
+ struct ipset *r;
+ struct iprange ra, rb;
+ uint32_t ia,ib;
+
+ r=ipset_new();
+ ia=0; ib=0;
+
+ while (ia<a->l && ib<b->l) {
+ ra=a->d[ia];
+ rb=b->d[ib];
+ if (ra.b < rb.a)
+ /* The first entry of a doesn't overlap with any part of b */
+ ia++;
+ else if (ra.a > rb.b)
+ /* The first entry of b doesn't overlap with any part of a */
+ ib++;
+ else {
+ /* Trim away any leading edges */
+ if (ra.a < rb.a)
+ /* a starts before b */
+ ra.a=rb.a;
+ else if (ra.a > rb.a)
+ /* b starts before a */
+ rb.a=ra.a;
+
+ /* Now the ranges start at the same point */
+ if (ra.b == rb.b) {
+ /* The ranges are equal */
+ ipset_append_range(r,ra);
+ ia++;
+ ib++;
+ } else if (ra.b < rb.b) {
+ /* a is the smaller range */
+ ipset_append_range(r,ra);
+ ia++;
+ } else {
+ /* y is the smaller range */
+ ipset_append_range(r,rb);
+ ib++;
+ }
+ }
+ }
+ return r;
+}
+
+struct ipset *ipset_complement(struct ipset *a)
+{
+ struct ipset *r;
+ struct iprange n;
+ int64_t pre;
+ uint32_t i,lo,hi;
+
+ r=ipset_new();
+ pre=-1;
+ for (i=0; i<a->l; i++) {
+ lo=a->d[i].a;
+ hi=a->d[i].b;
+ if (lo!=0) {
+ n.a=pre+1;
+ n.b=lo-1;
+ ipset_append_range(r,n);
+ }
+ pre=hi;
+ }
+ if (pre!=0xffffffff) {
+ n.a=pre+1;
+ n.b=0xffffffff;
+ ipset_append_range(r,n);
+ }
+ return r;
+}
+
+/* Return a-b */
+struct ipset *ipset_subtract(struct ipset *a, struct ipset *b)
+{
+ struct ipset *c, *r;
+ c=ipset_complement(b);
+ r=ipset_intersection(a,c);
+ ipset_free(c);
+ return r;
+}
+
+bool_t ipset_is_empty(struct ipset *a)
+{
+ return (a->l==0);
+}
+
+bool_t ipset_contains_addr(struct ipset *a, uint32_t addr)
{
uint32_t i;
- for (i=0; i<a->entries; i++) {
- if (subnet_intersects_with_list(a->list[i],b)) return True;
+ struct iprange r;
+
+ for (i=0; i<a->l; i++) {
+ r=a->d[i];
+ if (addr>=r.a && addr<=r.b) return True;
+ if (addr<r.a) return False;
}
return False;
}
+/* sub is a subset of super if it does not intersect with the complement
+ of super */
+bool_t ipset_is_subset(struct ipset *super, struct ipset *sub)
+{
+ struct ipset *superc;
+ struct ipset *inter;
+ bool_t empty;
+
+ superc=ipset_complement(super);
+ inter=ipset_intersection(superc,sub);
+ empty=ipset_is_empty(inter);
+ ipset_free(inter);
+ ipset_free(superc);
+ return empty;
+}
+
+struct subnet_list *ipset_to_subnet_list(struct ipset *is)
+{
+ struct subnet_list *r;
+ int64_t a,b;
+ uint32_t i;
+ uint32_t lomask,lobit,himask,bits;
+
+ r=subnet_list_new();
+ for (i=0; i<is->l; i++) {
+ a=is->d[i].a;
+ b=is->d[i].b;
+
+ lomask=1;
+ lobit=1;
+ himask=0xfffffffe;
+ bits=32;
+ while (a<=b) {
+ if ((a & lomask) != 0) {
+ subnet_list_append(r,a,bits);
+ a=a+lobit;
+ } else if ((b & lomask) != lomask) {
+ subnet_list_append(r,b&himask,bits);
+ b=b-lobit;
+ } else {
+ lomask = (lomask << 1) | 1;
+ lobit = (lobit << 1);
+ himask = himask ^ lobit;
+ bits = bits - 1;
+ ASSERT(bits>=0);
+ }
+ }
+ }
+ /* Sort the list? */
+ return r;
+}
+
/* The string buffer must be at least 16 bytes long */
string_t ipaddr_to_string(uint32_t addr)
{
return s;
}
-string_t subnet_to_string(struct subnet *sn)
+string_t subnet_to_string(struct subnet sn)
{
- uint32_t mask=sn->mask, addr=sn->prefix;
+ uint32_t addr=sn.prefix;
uint8_t a,b,c,d;
string_t s;
- int i;
s=safe_malloc(19,"subnet_to_string");
a=addr>>24;
b=addr>>16;
c=addr>>8;
d=addr;
- for (i=0; mask; i++) {
- mask=(mask<<1);
+ snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn.len);
+ return s;
+}
+
+static struct subnet string_item_to_subnet(item_t *i, string_t desc,
+ bool_t *invert)
+{
+ struct subnet s;
+ uint32_t a, b, c, d, n;
+ uint32_t match;
+ string_t in;
+
+ *invert=False;
+
+ /* i is not guaranteed to be a string */
+ if (i->type!=t_string) {
+ cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n");
+ }
+ in=i->data.string;
+
+ if (strcmp(in,"default")==0) {
+ s.prefix=0;
+ s.mask=0;
+ s.len=0;
+ return s;
+ }
+
+ if (*in=='!') {
+ *invert=True;
+ in++;
+ }
+ /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are
+ NOT optional. The subnet mask is optional; if missing it is assumed
+ to be /32. */
+ match=sscanf(in,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n);
+ if (match<4) {
+ cfgfatal(i->loc,desc,"\"%s\" is not a valid "
+ "subnet specification\n",in);
+ }
+ if (match<5) {
+ n=32;
}
- if (i!=sn->len) {
- fatal("subnet_to_string: invalid subnet structure "
- "(i=%d sn->len=%d mask=0x%08x)!\n",i,sn->len,sn->mask);
+ if (a>255 || b>255 || c>255 || d>255 || n>32) {
+ cfgfatal(i->loc,desc,"\"%s\": range error\n",in);
+ }
+ s.prefix=(a<<24)|(b<<16)|(c<<8)|(d);
+ s.mask=n?(~0UL << (32-n)):0;
+ s.len=n;
+ if (s.prefix & ~s.mask) {
+ cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained "
+ "in mask\n",in);
}
- snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn->len);
return s;
}
+
+uint32_t string_item_to_ipaddr(item_t *i, string_t desc)
+{
+ uint32_t a, b, c, d;
+ uint32_t match;
+
+ /* i is not guaranteed to be a string */
+ if (i->type!=t_string) {
+ cfgfatal(i->loc,desc,"expecting a string (IP address)\n");
+ }
+
+ match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d);
+ if (match<4) {
+ cfgfatal(i->loc,desc,"\"%s\" is not a valid "
+ "IP address\n",i->data.string);
+ }
+ if (a>255 || b>255 || c>255 || d>255) {
+ cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string);
+ }
+ return (a<<24)|(b<<16)|(c<<8)|(d);
+}
+
+struct ipset *string_list_to_ipset(list_t *l, struct cloc loc,
+ string_t module, string_t param)
+{
+ struct ipset *r, *n, *isn;
+ uint32_t e,i;
+ item_t *item;
+ bool_t inv;
+
+ r=ipset_new();
+ e=list_length(l);
+ for (i=0; i<e; i++) {
+ item=list_elem(l,i);
+ isn=ipset_from_subnet(string_item_to_subnet(item,param,&inv));
+ if (inv) {
+ n=ipset_subtract(r,isn);
+ } else {
+ n=ipset_union(r,isn);
+ }
+ ipset_free(r);
+ ipset_free(isn);
+ r=n;
+ }
+ return r;
+}
+/* Useful functions for dealing with collections of IP addresses */
+
#ifndef ipaddr_h
#define ipaddr_h
-/* Match an address (in HOST byte order) with a subnet list.
- Returns True if matched. */
-extern bool_t subnet_match(struct subnet *s, uint32_t address);
-extern bool_t subnet_matches_list(struct subnet_list *list, uint32_t address);
-extern bool_t subnets_intersect(struct subnet a, struct subnet b);
-extern bool_t subnet_intersects_with_list(struct subnet a,
- struct subnet_list *b);
-extern bool_t subnet_lists_intersect(struct subnet_list *a,
- struct subnet_list *b);
+struct subnet {
+ uint32_t prefix;
+ uint32_t mask;
+ uint32_t len;
+};
+
+struct subnet_list {
+ uint32_t entries;
+ uint32_t alloc;
+ struct subnet *list;
+};
+
+struct iprange {
+ uint32_t a,b;
+};
+
+struct ipset {
+ uint32_t l; /* Number of entries in list */
+ uint32_t a; /* Allocated space in list */
+ struct iprange *d;
+};
+extern struct subnet_list *subnet_list_new(void);
+extern void subnet_list_free(struct subnet_list *a);
+extern void subnet_list_append(struct subnet_list *a, uint32_t prefix,
+ uint32_t len);
+
+static inline bool_t subnet_match(struct subnet s, uint32_t address)
+{
+ return (s.prefix==(address&s.mask));
+}
+
+extern struct ipset *ipset_new(void);
+extern void ipset_free(struct ipset *a);
+extern struct ipset *ipset_from_subnet(struct subnet s);
+extern struct ipset *ipset_from_subnet_list(struct subnet_list *l);
+extern struct ipset *ipset_union(struct ipset *a, struct ipset *b);
+extern struct ipset *ipset_intersection(struct ipset *a, struct ipset *b);
+extern struct ipset *ipset_complement(struct ipset *a);
+extern struct ipset *ipset_subtract(struct ipset *a, struct ipset *b);
+extern bool_t ipset_is_empty(struct ipset *a);
+extern bool_t ipset_contains_addr(struct ipset *a, uint32_t addr);
+extern bool_t ipset_is_subset(struct ipset *super, struct ipset *sub);
+extern struct subnet_list *ipset_to_subnet_list(struct ipset *is);
extern string_t ipaddr_to_string(uint32_t addr);
-extern string_t subnet_to_string(struct subnet *sn);
+extern string_t subnet_to_string(struct subnet sn);
+
+extern struct ipset *string_list_to_ipset(list_t *l,struct cloc loc,
+ string_t module,string_t param);
+
+extern uint32_t string_item_to_ipaddr(item_t *i, string_t desc);
#endif /* ipaddr_h */
best_quality=0;
best_match=-1;
for (i=0; i<st->n_routes; i++) {
- if (st->routes[i].up && subnet_match(&st->routes[i].net,dest)) {
+ if (st->routes[i].up && subnet_match(st->routes[i].net,dest)) {
/* It's an available route to the correct destination. But is
it better than the one we already have? */
if (best_match==-1) {
/* The packet's not going down a tunnel. It might (ought to)
be for the host. */
- if (subnet_matches_list(&st->networks,dest)) {
+ if (ipset_contains_addr(st->networks,dest)) {
st->deliver_to_host(st->dst,buf);
st->outcount++;
BUF_ASSERT_FREE(buf);
dest=ntohl(iph->daddr);
/* Check source */
+ /* XXX consider generating ICMP if we're not point-to-point and we
+ don't like the packet */
if (client) {
/* Check that the packet source is appropriate for the tunnel
it came down */
- if (!subnet_matches_list(&client->networks,source)) {
+ if (!ipset_contains_addr(client->networks,source)) {
string_t s,d;
s=ipaddr_to_string(source);
d=ipaddr_to_string(dest);
/* Check that the packet originates in our configured local
network, and hasn't been forwarded from elsewhere or
generated with the wrong source address */
- if (!subnet_matches_list(&st->networks,source)) {
+ if (!ipset_contains_addr(st->networks,source)) {
string_t s,d;
s=ipaddr_to_string(source);
d=ipaddr_to_string(dest);
}
}
- /* If this is a point-to-point device we don't examine the packet at
- all; we blindly send it down our one-and-only registered tunnel,
- or to the host, depending on where it came from. */
+ /* If this is a point-to-point device we don't examine the
+ destination address at all; we blindly send it down our
+ one-and-only registered tunnel, or to the host, depending on
+ where it came from. */
+ /* XXX I think we should check destination addresses */
if (st->ptp) {
if (client) {
st->deliver_to_host(st->dst,buf);
BUF_ASSERT_FREE(buf);
return;
}
- if (client) {
- /* Check for free routing */
- if (!subnet_matches_list(&st->networks,dest)) {
- string_t s,d;
- s=ipaddr_to_string(source);
- d=ipaddr_to_string(dest);
- Message(M_WARNING,"%s: incoming packet from tunnel %s "
- "with bad destination address "
- "(s=%s,d=%s)\n",st->name,client->name,s,d);
- free(s); free(d);
- BUF_FREE(buf);
- return;
- }
- }
netlink_packet_forward(st,client,buf);
BUF_ASSERT_FREE(buf);
}
st->name, net);
free(net);
for (i=0; i<st->n_routes; i++) {
- net=subnet_to_string(&st->routes[i].net);
+ net=subnet_to_string(st->routes[i].net);
Message(c,"%s ",net);
free(net);
}
} else {
Message(c,"%s: routing table:\n",st->name);
for (i=0; i<st->n_routes; i++) {
- net=subnet_to_string(&st->routes[i].net);
+ net=subnet_to_string(st->routes[i].net);
Message(c,"%s -> tunnel %s (%s,%s route,%s,quality %d,use %d)\n",net,
st->routes[i].c->name,
st->routes[i].hard?"hard":"soft",
Message(c,"%s/32 -> netlink \"%s\" (use %d)\n",
net,st->name,st->localcount);
free(net);
- for (i=0; i<st->networks.entries; i++) {
- net=subnet_to_string(&st->networks.list[i]);
- Message(c,"%s -> host (use %d)\n",net,st->outcount);
+ for (i=0; i<st->subnets->entries; i++) {
+ net=subnet_to_string(st->subnets->list[i]);
+ Message(c,"%s ",net);
free(net);
}
+ if (i>0)
+ Message(c,"-> host (use %d)\n",st->outcount);
}
}
/* Fill the table */
i=0;
for (c=st->clients; c; c=c->next) {
- for (j=0; j<c->networks.entries; j++) {
- st->routes[i].net=c->networks.list[j];
+ for (j=0; j<c->subnets->entries; j++) {
+ st->routes[i].net=c->subnets->list[j];
st->routes[i].c=c;
/* Hard routes are always up;
soft routes default to down; routes with no 'deliver' function
netlink_dump_routes(st,True);
}
+static void netlink_inst_output_config(void *sst, struct buffer_if *buf)
+{
+/* struct netlink_client *c=sst; */
+/* struct netlink *st=c->nst; */
+
+ /* For now we don't output anything */
+ BUF_ASSERT_USED(buf);
+}
+
+static bool_t netlink_inst_check_config(void *sst, struct buffer_if *buf)
+{
+/* struct netlink_client *c=sst; */
+/* struct netlink *st=c->nst; */
+
+ BUF_ASSERT_USED(buf);
+ /* We need to eat all of the configuration information from the buffer
+ for backward compatibility. */
+ buf->size=0;
+ return True;
+}
+
static void netlink_inst_reg(void *sst, netlink_deliver_fn *deliver,
void *dst, uint32_t max_start_pad,
uint32_t max_end_pad)
{
struct netlink_client *c;
string_t name;
- struct subnet_list networks;
+ struct ipset *networks;
uint32_t options;
+ list_t *l;
name=dict_read_string(dict, "name", True, st->name, loc);
- dict_read_subnet_list(dict, "routes", True, st->name, loc,
- &networks);
+ l=dict_lookup(dict,"routes");
+ if (!l)
+ cfgfatal(loc,st->name,"required parameter \"routes\" not found\n");
+ networks=string_list_to_ipset(l,loc,st->name,"routes");
options=string_list_to_word(dict_lookup(dict,"options"),
netlink_option_table,st->name);
}
}
- /* Check that nets do not intersect st->exclude_remote_networks;
- refuse to register if they do. */
- if (subnet_lists_intersect(&st->exclude_remote_networks,&networks)) {
- cfgfatal(loc,st->name,"networks intersect with the explicitly "
- "excluded remote networks\n");
+ /* Check that nets are a subset of st->remote_networks;
+ refuse to register if they are not. */
+ if (!ipset_is_subset(st->remote_networks,networks)) {
+ cfgfatal(loc,st->name,"routes are not allowed\n");
return NULL;
}
c->ops.reg=netlink_inst_reg;
c->ops.deliver=netlink_inst_incoming;
c->ops.set_quality=netlink_set_quality;
+ c->ops.output_config=netlink_inst_output_config;
+ c->ops.check_config=netlink_inst_check_config;
c->nst=st;
c->networks=networks;
+ c->subnets=ipset_to_subnet_list(networks);
c->deliver=NULL;
c->dst=NULL;
c->name=name;
c->link_quality=LINK_QUALITY_DOWN;
c->next=st->clients;
st->clients=c;
- st->n_routes+=networks.entries;
+ st->n_routes+=c->subnets->entries;
return &c->cl;
}
item_t *item;
closure_t *cl;
- Message(M_DEBUG_CONFIG,"netlink_inst_apply\n");
-
item=list_elem(args,0);
if (!item || item->type!=t_dict) {
cfgfatal(loc,st->name,"must have a dictionary argument\n");
netlink_deliver_fn *to_host)
{
item_t *sa, *ptpa;
+ list_t *l;
st->dst=dst;
st->cl.description=description;
st->set_route=set_route;
st->deliver_to_host=to_host;
- st->name=dict_read_string(dict,"name",False,"netlink",loc);
+ st->name=dict_read_string(dict,"name",False,description,loc);
if (!st->name) st->name=description;
- dict_read_subnet_list(dict, "networks", True, "netlink", loc,
- &st->networks);
- dict_read_subnet_list(dict, "exclude-remote-networks", False, "netlink",
- loc, &st->exclude_remote_networks);
+ l=dict_lookup(dict,"networks");
+ if (l)
+ st->networks=string_list_to_ipset(l,loc,st->name,"networks");
+ else {
+ Message(M_WARNING,"%s: no local networks (parameter \"networks\") "
+ "defined\n",st->name);
+ st->networks=ipset_new();
+ }
+ l=dict_lookup(dict,"remote-networks");
+ if (l) {
+ st->remote_networks=string_list_to_ipset(l,loc,st->name,
+ "remote-networks");
+ } else {
+ struct ipset *empty;
+ empty=ipset_new();
+ st->remote_networks=ipset_complement(empty);
+ ipset_free(empty);
+ }
+
sa=dict_find_item(dict,"secnet-address",False,"netlink",loc);
ptpa=dict_find_item(dict,"ptp-address",False,"netlink",loc);
if (sa && ptpa) {
"ptp-address for this netlink device\n");
}
if (sa) {
- st->secnet_address=string_to_ipaddr(sa,"netlink");
+ st->secnet_address=string_item_to_ipaddr(sa,"netlink");
st->ptp=False;
} else {
- st->secnet_address=string_to_ipaddr(ptpa,"netlink");
+ st->secnet_address=string_item_to_ipaddr(ptpa,"netlink");
st->ptp=True;
}
+ /* XXX we may want to subtract secnet_address from networks here, to
+ be strictly correct. It shouldn't make any practical difference,
+ though, and will make the route dump look complicated... */
+ st->subnets=ipset_to_subnet_list(st->networks);
st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
buffer_new(&st->icmp,ICMP_BUFSIZE);
st->n_routes=0;
string_t t;
if (route->up!=route->kup) {
- t=subnet_to_string(&route->net);
+ t=subnet_to_string(route->net);
Message(M_INFO,"%s: setting route %s to state %s\n",st->nl.name,
t, route->up?"up":"down");
free(t);
closure_t cl;
struct netlink_if ops;
struct netlink *nst;
- struct subnet_list networks;
+ struct ipset *networks;
+ struct subnet_list *subnets; /* Same information as 'networks' */
netlink_deliver_fn *deliver;
void *dst;
string_t name;
string_t name;
uint32_t max_start_pad;
uint32_t max_end_pad;
- struct subnet_list networks;
- struct subnet_list exclude_remote_networks;
+ struct ipset *networks; /* Local networks */
+ struct subnet_list *subnets; /* Same information as networks */
+ struct ipset *remote_networks; /* Allowable remote networks */
uint32_t secnet_address; /* our own address, or the address of the
other end of a point-to-point link */
bool_t ptp;
#include <sys/wait.h>
#include "process.h"
+/* Advice about children from Peter:
+Better way: before the fork, make a pipe. In the child close the
++reading end. Make the writing end close-on-exec. If the dup2 or exec fails,
++write the errno value. In the parent, close the writing end. Now you can read
++from it. If you get an errno value from the pipe, the process failed and you
++know why. If you get EOF, the exec succeeded.
+
+<Senji> So, close on exec only closes if exec isn't going to return then?
+<Diziet> qu: I wouldn't bother with all that with pipes. Remember that the
++runtime system can still make exec fail when it's `too late'.
+<Senji> Diz - I would rather have a coherant error message than 'child failed'
+<Diziet> The child, if it fails to exec, should print a message to stderr
++(giving errno and what it was trying to execute, most likely), and exit
++nonzero.
+<Diziet> It should exit calling _exit.
+*/
+
/* Process handling - subprocesses, signals, etc. */
static bool_t signal_handling=False;
/* XXX should be from autoconf */
static char *configfile="/etc/secnet/secnet.conf";
+static char *sites_key="sites";
bool_t just_check_config=False;
static char *userid=NULL;
static uid_t uid=0;
{"debug", 1, 0, 'd'},
{"config", 1, 0, 'c'},
{"just-check-config", 0, 0, 'j'},
+ {"sites-key", 1, 0, 's'},
{0,0,0,0}
};
- c=getopt_long(argc, argv, "vwdnjc:ft:",
+ c=getopt_long(argc, argv, "vwdnjc:ft:s:",
long_options, &option_index);
if (c==-1)
break;
" -w, --nowarnings suppress warnings\n"
" -v, --verbose output extra diagnostics\n"
" -c, --config=filename specify a configuration file\n"
- " -j, --just-check-config stop after reading configfile\n"
+ " -j, --just-check-config stop after reading "
+ "configuration file\n"
+ " -s, --sites-key=name configuration key that "
+ "specifies active sites\n"
" -n, --nodetach do not run in background\n"
" -d, --debug=item,... set debug options\n"
" --help display this help and exit\n"
- " --version output version information and exit\n"
+ " --version output version information "
+ "and exit\n"
);
exit(0);
break;
just_check_config=True;
break;
+ case 's':
+ if (optarg)
+ sites_key=safe_strdup(optarg,"sites-key");
+ else
+ fatal("secnet: no sites key specified");
+ break;
+
case '?':
break;
}
/* Go along site list, starting sites */
- l=dict_lookup(config,"sites");
+ l=dict_lookup(config,sites_key);
if (!l) {
- Message(M_WARNING,"secnet: configuration did not define any "
- "remote sites\n");
+ Message(M_WARNING,"secnet: configuration key \"%s\" is missing; no "
+ "remote sites are defined\n",sites_key);
} else {
i=0;
while ((site=list_elem(l, i++))) {
typedef char *string_t;
typedef enum {False,True} bool_t;
-#define ASSERT(x) do { if (!(x)) { fatal("assertion failed line " __LINE__ \
- " file " __FILE__ "\n"); } while(0)
-
-/***** SHARED types *****/
-
-/* These are stored in HOST byte order */
-struct subnet {
- uint32_t prefix;
- uint32_t mask;
- uint32_t len;
-};
-
-struct subnet_list {
- uint32_t entries;
- struct subnet *list;
-};
-
-/***** END of shared types *****/
+#define ASSERT(x) do { if (!(x)) { fatal("assertion failed line %d file " \
+ __FILE__ "\n",__LINE__); } } while(0)
/***** CONFIGURATION support *****/
string_t desc, struct cloc loc, uint32_t def);
extern bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
string_t desc, struct cloc loc, bool_t def);
-extern void dict_read_subnet_list(dict_t *dict, string_t key, bool_t required,
- string_t desc, struct cloc loc,
- struct subnet_list *sl);
-extern uint32_t string_to_ipaddr(item_t *i, string_t desc);
struct flagstr {
string_t name;
uint32_t value;
typedef void netlink_register_fn(void *st, netlink_deliver_fn *deliver,
void *dst, uint32_t max_start_pad,
uint32_t max_end_pad);
-
+typedef void netlink_output_config_fn(void *st, struct buffer_if *buf);
+typedef bool_t netlink_check_config_fn(void *st, struct buffer_if *buf);
struct netlink_if {
void *st;
netlink_register_fn *reg;
netlink_deliver_fn *deliver;
netlink_link_quality_fn *set_quality;
+ netlink_output_config_fn *output_config;
+ netlink_check_config_fn *check_config;
};
/* DH interface */
}
static void set_link_quality(struct site *st);
-static bool_t initiate_key_setup(struct site *st);
+static void delete_key(struct site *st, string_t reason, uint32_t loglevel);
+static bool_t initiate_key_setup(struct site *st, string_t reason);
static void enter_state_run(struct site *st);
static bool_t enter_state_resolve(struct site *st);
static bool_t enter_state_sentmsg1(struct site *st);
/* We are going to add three words to the transformed message */
buffer_init(&st->buffer,st->transform->max_start_pad+(4*3));
buf_append_uint32(&st->buffer,LABEL_MSG5);
+ /* Give the netlink code an opportunity to put its own stuff in the
+ message (configuration information, etc.) */
+ st->netlink->output_config(st->netlink->st,&st->buffer);
st->new_transform->forwards(st->new_transform->st,&st->buffer,
&transform_err);
buf_prepend_uint32(&st->buffer,LABEL_MSG5);
slog(st,LOG_SEC,"MSG5/PING packet contained invalid data");
return False;
}
+ if (!st->netlink->check_config(st->netlink->st,msg5)) {
+ slog(st,LOG_SEC,"MSG5/PING packet contained bad netlink config");
+ return False;
+ }
CHECK_EMPTY(msg5);
return True;
}
/* We are going to add three words to the transformed message */
buffer_init(&st->buffer,st->transform->max_start_pad+(4*3));
buf_append_uint32(&st->buffer,LABEL_MSG6);
+ /* Give the netlink code an opportunity to put its own stuff in the
+ message (configuration information, etc.) */
+ st->netlink->output_config(st->netlink->st,&st->buffer);
st->new_transform->forwards(st->new_transform->st,&st->buffer,
&transform_err);
buf_prepend_uint32(&st->buffer,LABEL_MSG6);
slog(st,LOG_SEC,"MSG6/PONG packet contained invalid data");
return False;
}
+ if (!st->netlink->check_config(st->netlink->st,msg6)) {
+ slog(st,LOG_SEC,"MSG6/PONG packet contained bad netlink config");
+ return False;
+ }
CHECK_EMPTY(msg6);
return True;
}
if (!st->current_valid) {
slog(st,LOG_DROP,"incoming message but no current key -> dropping");
- return initiate_key_setup(st);
+ return initiate_key_setup(st,"incoming message but no current key");
}
if (!unpick_msg0(st,msg0,&m)) return False;
msg0,&transform_err)) {
/* There's a problem */
slog(st,LOG_SEC,"transform: %s",transform_err);
- return initiate_key_setup(st);
+ return initiate_key_setup(st,"incoming message would not decrypt");
}
CHECK_AVAIL(msg0,4);
type=buf_unprepend_uint32(msg0);
switch(type) {
+ case LABEL_MSG7:
+ /* We must forget about the current session. */
+ delete_key(st,"request from peer",LOG_SEC);
+ return True;
+ break;
case LABEL_MSG9:
/* Deliver to netlink layer */
st->netlink->deliver(st->netlink->st,msg0);
}
}
-static bool_t initiate_key_setup(struct site *st)
+static bool_t initiate_key_setup(struct site *st,string_t reason)
{
if (st->state!=SITE_RUN) return False;
+ slog(st,LOG_SETUP_INIT,"initiating key exchange (%s)",reason);
if (st->address) {
- slog(st,LOG_SETUP_INIT,"initiating key exchange; resolving address");
+ slog(st,LOG_SETUP_INIT,"resolving peer address");
return enter_state_resolve(st);
} else if (st->peer_valid) {
- slog(st,LOG_SETUP_INIT,"initiating key exchange using old "
- "peer address");
+ slog(st,LOG_SETUP_INIT,"using old peer address");
st->setup_peer=st->peer;
return enter_state_sentmsg1(st);
}
enter_state_run(st);
}
+static void delete_key(struct site *st, string_t reason, uint32_t loglevel)
+{
+ if (st->current_valid) {
+ slog(st,loglevel,"session closed (%s)",reason);
+
+ st->current_valid=False;
+ st->current_transform->delkey(st->current_transform->st);
+ st->current_key_timeout=0;
+ set_link_quality(st);
+ }
+}
+
static void state_assert(struct site *st, bool_t ok)
{
if (!ok) fatal("state_assert\n");
{
st->state=SITE_STOP;
st->timeout=0;
- st->current_transform->delkey(st->current_transform->st);
- st->current_valid=False;
- st->current_key_timeout=0;
-
- st->peer_valid=False;
-
- set_link_quality(st);
-
+ delete_key(st,"entering state STOP",LOG_TIMEOUT_KEY);
st->new_transform->delkey(st->new_transform->st);
}
return False;
}
+static bool_t send_msg7(struct site *st,string_t reason)
+{
+ string_t transform_err;
+
+ if (st->current_valid && st->peer_valid && st->buffer.free) {
+ BUF_ALLOC(&st->buffer,"site:MSG7");
+ buffer_init(&st->buffer,st->transform->max_start_pad+(4*3));
+ buf_append_uint32(&st->buffer,LABEL_MSG7);
+ buf_append_string(&st->buffer,reason);
+ st->current_transform->forwards(st->current_transform->st,
+ &st->buffer, &transform_err);
+ buf_prepend_uint32(&st->buffer,LABEL_MSG0);
+ buf_prepend_uint32(&st->buffer,(uint32_t)st);
+ buf_prepend_uint32(&st->buffer,st->remote_session_id);
+ st->comm->sendmsg(st->comm->st,&st->buffer,&st->peer);
+ BUF_FREE(&st->buffer);
+ return True;
+ }
+ return False;
+}
+
/* We go into this state if our peer becomes uncommunicative. Similar to
the "stop" state, we forget all session keys for a while, before
re-entering the "run" state. */
}
}
if (st->current_key_timeout && *now>st->current_key_timeout) {
- slog(st,LOG_TIMEOUT_KEY,"maximum key life exceeded; session closed");
- st->current_valid=False;
- st->current_transform->delkey(st->current_transform->st);
- st->current_key_timeout=0;
- set_link_quality(st);
+ delete_key(st,"maximum key life exceeded",LOG_TIMEOUT_KEY);
}
}
slog(st,LOG_DROP,"discarding outgoing packet of size %d",buf->size);
BUF_FREE(buf);
- initiate_key_setup(st);
+ initiate_key_setup(st,"outgoing packet");
}
/* This function is called by the communication device to deliver
return False; /* Not for us. */
}
if (dest==(uint32_t)st) {
- uint32_t msgtype=ntohl(*(uint32_t *)(buf->start+8));
+ uint32_t msgtype=ntohl(get_uint32(buf->start+8));
/* Explicitly addressed to us */
if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True);
switch (msgtype) {
+ case 0: /* NAK */
+ /* If the source is our current peer then initiate a key setup,
+ because our peer's forgotten the key */
+ if (get_uint32(buf->start+4)==st->remote_session_id) {
+ initiate_key_setup(st,"received a NAK");
+ } else {
+ slog(st,LOG_SEC,"bad incoming NAK");
+ }
+ break;
case LABEL_MSG0:
process_msg0(st,buf,source);
break;
slog(st,LOG_SEC,"invalid MSG6");
}
break;
- case LABEL_MSG8:
- /* NAK packet: enter state where we ping and check for response */
- slog(st,LOG_ERROR,"received a NAK");
- break;
default:
slog(st,LOG_SEC,"received message of unknown type 0x%08x",
msgtype);
else enter_state_stop(st);
}
+static void site_phase_hook(void *sst, uint32_t newphase)
+{
+ struct site *st=sst;
+
+ /* The program is shutting down; tell our peer */
+ send_msg7(st,"shutting down");
+}
+
static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
list_t *args)
{
enter_state_stop(st);
+ add_hook(PHASE_SHUTDOWN,site_phase_hook,st);
+
return new_closure(&st->cl);
}
netlink_init(&st->nl,st,loc,dict,
"netlink-userv-ipif",NULL,to_host);
st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"name",loc);
- st->local_address=string_to_ipaddr(
+ st->local_address=string_item_to_ipaddr(
dict_find_item(dict,"local-address", True, name, loc),"netlink");
BUF_ALLOC(st->buff,"slip_init");
st->pending_esc=False;
string_t nets;
string_t s;
struct netlink_route *r;
+ struct ipset *isnets;
+ struct subnet_list *snets;
int i;
uint8_t confirm;
ipaddr_to_string(st->slip.local_address),
ipaddr_to_string(st->slip.nl.secnet_address),st->slip.nl.mtu);
- nets=safe_malloc(1024,"userv_invoke_userv:nets");
- *nets=0;
r=st->slip.nl.routes;
+ isnets=ipset_new();
for (i=0; i<st->slip.nl.n_routes; i++) {
if (r[i].up) {
+ struct ipset *sn,*nis;
r[i].kup=True;
- s=subnet_to_string(&r[i].net);
- strcat(nets,s);
- strcat(nets,",");
- free(s);
+ sn=ipset_from_subnet(r[i].net);
+ nis=ipset_union(isnets,sn);
+ ipset_free(sn);
+ ipset_free(isnets);
+ isnets=nis;
}
}
+ snets=ipset_to_subnet_list(isnets);
+ ipset_free(isnets);
+ nets=safe_malloc(20*snets->entries,"userv_invoke_userv:nets");
+ *nets=0;
+ for (i=0; i<snets->entries; i++) {
+ s=subnet_to_string(snets->list[i]);
+ strcat(nets,s);
+ strcat(nets,",");
+ free(s);
+ }
nets[strlen(nets)-1]=0;
+ subnet_list_free(snets);
Message(M_INFO,"%s: about to invoke: %s %s %s %s %s\n",st->slip.nl.name,
st->userv_path,st->service_user,st->service_name,addrs,nets);
if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
if (!st->route_path) st->route_path="route";
st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
- st->local_address=string_to_ipaddr(
+ st->local_address=string_item_to_ipaddr(
dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
add_hook(PHASE_GETRESOURCES,tun_phase_hook,st);
if (!st->ifconfig_path) st->ifconfig_path="ifconfig";
if (!st->route_path) st->route_path="route";
st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc);
- st->local_address=string_to_ipaddr(
+ st->local_address=string_item_to_ipaddr(
dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
/* Old TUN interface: the network interface name depends on which
#include <sys/socket.h>
#include <sys/wait.h>
#include "util.h"
+#include "unaligned.h"
static beforepoll_fn udp_beforepoll;
static afterpoll_fn udp_afterpoll;
}
}
if (!done) {
+ uint32_t source,dest;
/* XXX manufacture and send NAK packet */
- Message(M_WARNING,"Need to send NAK\n");
+ source=get_uint32(st->rbuf->start); /* Us */
+ dest=get_uint32(st->rbuf->start+4); /* Them */
+ Message(M_INFO,"udp (port %d): sending NAK\n",st->port);
+ buffer_init(st->rbuf,0);
+ buf_append_uint32(st->rbuf,dest);
+ buf_append_uint32(st->rbuf,source);
+ buf_append_uint32(st->rbuf,0); /* NAK is msg type 0 */
+ sendto(st->fd, st->rbuf->start, st->rbuf->size, 0,
+ (struct sockaddr *)&from, sizeof(from));
BUF_FREE(st->rbuf);
}
BUF_ASSERT_FREE(st->rbuf);