chiark / gitweb /
Import release 0.1.11 v0.1.11
authorStephen Early <steve@greenend.org.uk>
Sat, 27 Oct 2001 14:29:00 +0000 (15:29 +0100)
committerStephen Early <steve@greenend.org.uk>
Wed, 18 May 2011 17:38:55 +0000 (18:38 +0100)
27 files changed:
BUGS [new file with mode: 0644]
INSTALL
Makefile.in
NEWS
NOTES
TODO
conffile.c
config.h.bot
config.h.in
debian/changelog
debian/files [deleted file]
debian/postinst.debhelper [deleted file]
debian/postrm.debhelper [deleted file]
debian/prerm.debhelper [deleted file]
debian/rules
debian/substvars [deleted file]
ipaddr.c
ipaddr.h
netlink.c
netlink.h
process.c
secnet.c
secnet.h
site.c
slip.c
tun.c
udp.c

diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..7145f3a
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,2 @@
+Known bugs in secnet
+
diff --git a/INSTALL b/INSTALL
index ab4debdc87ab6f59e04b0741ed64c024a85fbbf0..f41b4ba253b320f4ef518170280782eb0b56f991 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -32,11 +32,11 @@ linux/Documentation/networking/tuntap.txt
 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
 
@@ -54,18 +54,18 @@ modify the routing table.)
 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.
 
@@ -83,10 +83,32 @@ $ make
 # 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
@@ -103,22 +125,23 @@ $ LDFLAGS="-L/usr/local/lib" ./configure
 $ 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
 
index 0e4926c0b590a311c88336ffdb141678c0ac7fcc..9a912b5d455b25d3370451b6580aef396a9b4be2 100644 (file)
@@ -18,7 +18,7 @@
 .PHONY:        all clean realclean dist install distclean
 
 PACKAGE:=secnet
-VERSION:=0.1.10
+VERSION:=0.1.11
 
 @SET_MAKE@
 
@@ -48,7 +48,7 @@ OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \
        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 \
diff --git a/NEWS b/NEWS
index be5c38ac77ff2b5a351976a472919920002a7fcc..60cf744e04edcf090abda86ddb185346b07c23b1 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,41 @@ Netlink device that implements an Ethernet bridge.
 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
 
diff --git a/NOTES b/NOTES
index 2443549cb67232ce64fe9b8f127ba20a31e32019..485443b1c13320836b6b1d7c0acd9a25e13dea1b 100644 (file)
--- a/NOTES
+++ b/NOTES
@@ -231,9 +231,7 @@ retransmit or confirm reception. It is suggested that this message be
 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
diff --git a/TODO b/TODO
index 5ae53cf8cf4070b270c44950698b472112c63da6..e02c6279dd20ea5ae65abac430a61594d2647de7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,16 +1,18 @@
 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)
 
@@ -24,14 +26,8 @@ does it take to prompt a key exchange?)
 
 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.
index f33354e6e075b36b38e2d20ad21c3c6ab9f2d5e1..3c629e4eeb6b361bfa2ae9e60f494391bfc86ec5 100644 (file)
@@ -717,101 +717,6 @@ bool_t dict_read_bool(dict_t *dict, string_t key, bool_t required,
     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)
 {
index 889736c5ee511249344e0b743ddecdaa1eda6805..cf288bdfd7beee3e854e3c2e0666b5665ddc5146 100644 (file)
 #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
@@ -19,8 +21,10 @@ typedef unsigned long uint64_t;
 /* 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
@@ -28,8 +32,10 @@ typedef unsigned int uint32_t;
 /* 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
index d7044fcaab104aae7523d80cfd3c0909d9137f7f..76d28f01d2ff82045afa26d9e863a3b66ce030b0 100644 (file)
 #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
@@ -109,8 +111,10 @@ typedef unsigned long uint64_t;
 /* 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
@@ -118,8 +122,10 @@ typedef unsigned int uint32_t;
 /* 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
index 2011174b03830a6df4aba366c9878633049c5774..8ac52602899500cae82abd79c20efc42e06ea0b8 100644 (file)
@@ -1,5 +1,8 @@
-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
diff --git a/debian/files b/debian/files
deleted file mode 100644 (file)
index 1638b95..0000000
+++ /dev/null
@@ -1 +0,0 @@
-secnet_0.1.9-1_i386.deb net extra
diff --git a/debian/postinst.debhelper b/debian/postinst.debhelper
deleted file mode 100644 (file)
index 2ebe82f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# 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
diff --git a/debian/postrm.debhelper b/debian/postrm.debhelper
deleted file mode 100644 (file)
index 5f1bce9..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# Automatically added by dh_installinit
-if [ "$1" = "purge" ] ; then
-       update-rc.d secnet remove >/dev/null
-fi
-# End automatically added section
diff --git a/debian/prerm.debhelper b/debian/prerm.debhelper
deleted file mode 100644 (file)
index 23a07d7..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-# 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
index a85db21905e6704de6645a4342bd1fbab0d53b73..d9d1dd51650c37f32a3d7106d562a6afce9ab017 100755 (executable)
@@ -46,7 +46,7 @@ binary-arch: build install
        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
diff --git a/debian/substvars b/debian/substvars
deleted file mode 100644 (file)
index aa787d5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-shlibs:Depends=libc6 (>= 2.2.3-7), libgmp2
index f42e82016319b702cd31b88e9075e8663d2ee04b..1edc334e46fc28be2b20b0e41e16920509c06346 100644 (file)
--- a/ipaddr.c
+++ b/ipaddr.c
+/* 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)
 {
@@ -58,25 +331,114 @@ 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;
+}
index 99cd1ed4ea1cbfd0da4ade67f37c4bfc76db2683..8c262bfb3a899cb092cdf422761052e5b543f57e 100644 (file)
--- a/ipaddr.h
+++ b/ipaddr.h
@@ -1,18 +1,59 @@
+/* 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 */
index 4426a7c3575d31f79d1a8293a135d5cfe9d9cd02..e41445cb47c147929e6c3f6f84e7a6691fa03144 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -302,7 +302,7 @@ static void netlink_packet_deliver(struct netlink *st,
     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? */
 
@@ -334,7 +334,7 @@ static void netlink_packet_deliver(struct netlink *st,
     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);
@@ -466,10 +466,12 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
     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);
@@ -483,7 +485,7 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
        /* 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);
@@ -495,9 +497,11 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
        }
     }
 
-    /* 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);
@@ -515,20 +519,6 @@ static void netlink_incoming(struct netlink *st, struct netlink_client *client,
        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);
 }
@@ -591,7 +581,7 @@ static void netlink_dump_routes(struct netlink *st, bool_t requested)
                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);
        }
@@ -599,7 +589,7 @@ static void netlink_dump_routes(struct netlink *st, bool_t requested)
     } 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",
@@ -613,11 +603,13 @@ static void netlink_dump_routes(struct netlink *st, bool_t requested)
        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);
     }
 }
 
@@ -645,8 +637,8 @@ static void netlink_phase_hook(void *sst, uint32_t new_phase)
     /* 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
@@ -682,6 +674,27 @@ static void netlink_signal_handler(void *sst, int signum)
     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)
@@ -710,13 +723,16 @@ static closure_t *netlink_inst_create(struct netlink *st,
 {
     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);
 
@@ -738,11 +754,10 @@ static closure_t *netlink_inst_create(struct netlink *st,
        }
     }
 
-    /* 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;
     }
 
@@ -755,9 +770,12 @@ static closure_t *netlink_inst_create(struct netlink *st,
     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;
@@ -765,7 +783,7 @@ static closure_t *netlink_inst_create(struct netlink *st,
     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;
 }
@@ -779,8 +797,6 @@ static list_t *netlink_inst_apply(closure_t *self, struct cloc loc,
     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");
@@ -799,6 +815,7 @@ netlink_deliver_fn *netlink_init(struct netlink *st,
                                 netlink_deliver_fn *to_host)
 {
     item_t *sa, *ptpa;
+    list_t *l;
 
     st->dst=dst;
     st->cl.description=description;
@@ -811,12 +828,27 @@ netlink_deliver_fn *netlink_init(struct netlink *st,
     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) {
@@ -828,12 +860,16 @@ netlink_deliver_fn *netlink_init(struct netlink *st,
                 "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;
@@ -868,7 +904,7 @@ static bool_t null_set_route(void *sst, struct netlink_route *route)
     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);
index c13b0ae44cce6c58fa8488c4c25848639f29df96..9f46fa8807a5951160e4b9f661250096369eece4 100644 (file)
--- a/netlink.h
+++ b/netlink.h
@@ -13,7 +13,8 @@ struct netlink_client {
     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;
@@ -45,8 +46,9 @@ struct netlink {
     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;
index d9d01cec2c1abc090e8c4911482c071664f3d514..8e464beb464707b5761338b0b29b81f62bacdfde 100644 (file)
--- a/process.c
+++ b/process.c
@@ -5,6 +5,23 @@
 #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;
index a3cf15334d32e60fd2122567878982203693b283..20f9999ed43834eb21d599ddb958a78162287d54 100644 (file)
--- a/secnet.c
+++ b/secnet.c
@@ -22,6 +22,7 @@ extern char version[];
 
 /* 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;
@@ -73,10 +74,11 @@ static void parse_options(int argc, char **argv)
            {"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;
@@ -90,11 +92,15 @@ static void parse_options(int argc, char **argv)
                    "  -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;
@@ -137,6 +143,13 @@ static void parse_options(int argc, char **argv)
            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;
 
@@ -202,10 +215,10 @@ static void setup(dict_t *config)
     }
 
     /* 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++))) {
index 3821b81cddf2386592409754f78f70a9d4ee75e1..c40ed1bddd70cf03b54b3de13ca71d9cde954453 100644 (file)
--- a/secnet.h
+++ b/secnet.h
 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 *****/
 
@@ -112,10 +96,6 @@ extern uint32_t dict_read_number(dict_t *dict, string_t key, bool_t required,
                                 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;
@@ -386,12 +366,15 @@ typedef void netlink_link_quality_fn(void *st, uint32_t quality);
 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 */
diff --git a/site.c b/site.c
index fc045c9098c5ebfc57cfc9604a5f0d3394d4a7bd..cb0e0aa858813eae3c95e2f415054daa92b36e57 100644 (file)
--- a/site.c
+++ b/site.c
@@ -221,7 +221,8 @@ static void slog(struct site *st, uint32_t event, string_t msg, ...)
 }
 
 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);
@@ -516,6 +517,9 @@ static bool_t generate_msg5(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);
@@ -565,6 +569,10 @@ static bool_t process_msg5(struct site *st, struct buffer_if *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;
 }
@@ -577,6 +585,9 @@ static bool_t generate_msg6(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_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);
@@ -607,6 +618,10 @@ static bool_t process_msg6(struct site *st, struct buffer_if *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;
 }
@@ -620,7 +635,7 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
 
     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;
@@ -629,11 +644,16 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
                                       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);
@@ -701,15 +721,15 @@ static void site_resolve_callback(void *sst, struct in_addr *address)
     }
 }
 
-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);
     }
@@ -736,6 +756,18 @@ static void activate_new_key(struct site *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");
@@ -745,14 +777,7 @@ static void enter_state_stop(struct site *st)
 {
     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);
 }
 
@@ -887,6 +912,27 @@ static bool_t send_msg6(struct site *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. */
@@ -943,11 +989,7 @@ static void site_afterpoll(void *sst, struct pollfd *fds, int nfds,
        }
     }
     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);
     }
 }
 
@@ -983,7 +1025,7 @@ static void site_outgoing(void *sst, struct buffer_if *buf)
 
     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
@@ -1045,10 +1087,19 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
        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;
@@ -1113,10 +1164,6 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
                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);
@@ -1136,6 +1183,14 @@ static void site_control(void *vst, bool_t run)
     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)
 {
@@ -1254,6 +1309,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
 
     enter_state_stop(st);
 
+    add_hook(PHASE_SHUTDOWN,site_phase_hook,st);
+
     return new_closure(&st->cl);
 }
 
diff --git a/slip.c b/slip.c
index d0a77c0b7a118ee7c63fa25c6216d105f2adffd7..60152c9a984deea96a8839e89d6222fd346660a4 100644 (file)
--- a/slip.c
+++ b/slip.c
@@ -114,7 +114,7 @@ static void slip_init(struct slip *st, struct cloc loc, dict_t *dict,
        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;
@@ -240,6 +240,8 @@ static void userv_invoke_userv(struct userv *st)
     string_t nets;
     string_t s;
     struct netlink_route *r;
+    struct ipset *isnets;
+    struct subnet_list *snets;
     int i;
     uint8_t confirm;
 
@@ -255,19 +257,31 @@ static void userv_invoke_userv(struct userv *st)
             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);
diff --git a/tun.c b/tun.c
index 90b9161022abc0d5c268702733ee1db7190cfb92..20ae29b130bd79306cd05fc7e52fc01f28266890 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -233,7 +233,7 @@ static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context,
     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);
@@ -276,7 +276,7 @@ static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context,
     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
diff --git a/udp.c b/udp.c
index 307a67a9f7d67cdb8dd42758040d3435e054d070..65208650f998a7cbc5f670a1d7114ae40adc677b 100644 (file)
--- a/udp.c
+++ b/udp.c
@@ -21,6 +21,7 @@
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include "util.h"
+#include "unaligned.h"
 
 static beforepoll_fn udp_beforepoll;
 static afterpoll_fn udp_afterpoll;
@@ -91,8 +92,17 @@ static void udp_afterpoll(void *state, struct pollfd *fds, int nfds,
                    }
                }
                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);