chiark / gitweb /
Import release 0.04 v0.04
authorStephen Early <steve@greenend.org.uk>
Sun, 23 Sep 2001 15:28:00 +0000 (16:28 +0100)
committerStephen Early <steve@greenend.org.uk>
Wed, 18 May 2011 12:15:25 +0000 (13:15 +0100)
17 files changed:
COPYING [new file with mode: 0644]
INSTALL
Makefile.in
TODO
config.h.bot
config.h.in
configure
configure.in
foo.pid [new file with mode: 0644]
linux/if_tun.h [new file with mode: 0644]
netlink.c
secnet.c
secnet.h
site.c
testconfig
testconfigz
util.c

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
index 88b1cdb..42584ae 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -8,6 +8,15 @@ through userv-ipif, install and configure userv-ipif. It is part of
 userv-utils, available from ftp.chiark.greenend.org.uk in
 /users/ian/userv
 
+If you intend to configure secnet to obtain packets from the kernel
+using the universal TUN/TAP driver, make sure it's configured in your
+kernel (it's under "network device support" in Linux) and that you've
+created the appropriate device files; see
+linux/Documentation/networking/tuntap.txt
+
+If you're using TUN/TAP on a platform other than Linux, see
+http://vtun.sourceforge.net/tun/
+
 Then, to install secnet do
 
 $ ./configure
index 27b5636..8ec05ce 100644 (file)
@@ -1,14 +1,14 @@
 .DUMMY:        all clean realclean dist install
 
 PACKAGE:=secnet
-VERSION:=0.03
+VERSION:=0.04
 
 @SET_MAKE@
 
 srcdir:=@srcdir@
 VPATH:=@srcdir@
 
-CFLAGS:=@CFLAGS@ @DEFS@ -DVERSION=\"$(VERSION)\" -Wall
+CFLAGS:=@CFLAGS@ @DEFS@ -DVERSION=\"$(VERSION)\" -Wall -I.
 
 LDFLAGS:=@LDFLAGS@
 
diff --git a/TODO b/TODO
index b75c633..b2e871b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,9 +1,12 @@
+configure.in: cut down to just the required tests. Support for installation.
+
+Makefile.in: support for installation.
+
 conffile.c: deal with line numbers from included conffiles correctly
 
 dh.c: change format to binary from decimal string
 
-netlink.c: initial implementation done, needs basic router functionality
-adding. Can wait. Also support tun device.
+netlink.c: done. Test tun-old code.
 
 random.c: test
 
index adb0c7b..ff6ecda 100644 (file)
@@ -6,27 +6,4 @@
 #include <sys/cdefs.h>
 #endif
 
-#if 0
-/* Use the definitions: */
-
-/* Give us an unsigned 32-bit data type. */
-#if SIZEOF_UNSIGNED_LONG==4
-#define UWORD32 unsigned long
-#elif SIZEOF_UNSIGNED_INT==4
-#define UWORD32 unsigned int
-#else
-#error I do not know what to use for a UWORD32.
-#endif
-
-/* An unsigned 8-bit data type */
-#if SIZEOF_UNSIGNED_CHAR==1
-#define UBYTE8 unsigned char
-#else
-#error I do not know what to use for a UBYTE8
-#endif
-
-
-#endif /* 0 */
-
-
 #endif /* _CONFIG_H */
index a3b3cef..5d31bab 100644 (file)
 #define _CONFIG_H
 
 
-/* Define to empty if the keyword does not work.  */
-#undef const
-
-/* Define if you don't have vprintf but do have _doprnt.  */
-#undef HAVE_DOPRNT
-
-/* Define if you have the vprintf function.  */
-#undef HAVE_VPRINTF
-
-/* Define to `int' if <sys/types.h> doesn't define.  */
-#undef pid_t
-
-/* Define to `unsigned' if <sys/types.h> doesn't define.  */
-#undef size_t
-
 /* Define if you have the ANSI C header files.  */
 #undef STDC_HEADERS
 
    byte first (like Motorola and SPARC, unlike Intel and VAX).  */
 #undef WORDS_BIGENDIAN
 
+/* Define if you have the <linux/if.h> header file.  */
+#undef HAVE_LINUX_IF_H
+
 /* Define if you have the adns library (-ladns).  */
 #undef HAVE_LIBADNS
 
 /* Define if you have the fl library (-lfl).  */
 #undef HAVE_LIBFL
 
-/* Define if you have the gmp library (-lgmp).  */
-#undef HAVE_LIBGMP
+/* Define if you have the gmp2 library (-lgmp2).  */
+#undef HAVE_LIBGMP2
 /* -*- c -*- */
 
 /* These are from config.h.bot, pasted onto the end of config.h.in. */
 #include <sys/cdefs.h>
 #endif
 
-#if 0
-/* Use the definitions: */
-
-/* Give us an unsigned 32-bit data type. */
-#if SIZEOF_UNSIGNED_LONG==4
-#define UWORD32 unsigned long
-#elif SIZEOF_UNSIGNED_INT==4
-#define UWORD32 unsigned int
-#else
-#error I do not know what to use for a UWORD32.
-#endif
-
-/* An unsigned 8-bit data type */
-#if SIZEOF_UNSIGNED_CHAR==1
-#define UBYTE8 unsigned char
-#else
-#error I do not know what to use for a UBYTE8
-#endif
-
-
-#endif /* 0 */
-
-
 #endif /* _CONFIG_H */
index 7a08124..4153c02 100755 (executable)
--- a/configure
+++ b/configure
@@ -1001,6 +1001,46 @@ EOF
 
 fi
 
+for ac_hdr in linux/if.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1009: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1014 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1019: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
 ac_aux_dir=
 for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
   if test -f $ac_dir/install-sh; then
@@ -1032,7 +1072,7 @@ ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
 # ./install, which can be erroneously created by make from ./install.sh.
 echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
-echo "configure:1036: checking for a BSD compatible install" >&5
+echo "configure:1076: checking for a BSD compatible install" >&5
 if test -z "$INSTALL"; then
 if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1084,261 +1124,15 @@ test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
 
 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
-echo $ac_n "checking for pid_t""... $ac_c" 1>&6
-echo "configure:1089: checking for pid_t" >&5
-if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  cat > conftest.$ac_ext <<EOF
-#line 1094 "configure"
-#include "confdefs.h"
-#include <sys/types.h>
-#if STDC_HEADERS
-#include <stdlib.h>
-#include <stddef.h>
-#endif
-EOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
-  egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
-  rm -rf conftest*
-  ac_cv_type_pid_t=yes
-else
-  rm -rf conftest*
-  ac_cv_type_pid_t=no
-fi
-rm -f conftest*
-
-fi
-echo "$ac_t""$ac_cv_type_pid_t" 1>&6
-if test $ac_cv_type_pid_t = no; then
-  cat >> confdefs.h <<\EOF
-#define pid_t int
-EOF
-
-fi
-
-echo $ac_n "checking for size_t""... $ac_c" 1>&6
-echo "configure:1122: checking for size_t" >&5
-if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  cat > conftest.$ac_ext <<EOF
-#line 1127 "configure"
-#include "confdefs.h"
-#include <sys/types.h>
-#if STDC_HEADERS
-#include <stdlib.h>
-#include <stddef.h>
-#endif
-EOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
-  egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
-  rm -rf conftest*
-  ac_cv_type_size_t=yes
-else
-  rm -rf conftest*
-  ac_cv_type_size_t=no
-fi
-rm -f conftest*
-
-fi
-echo "$ac_t""$ac_cv_type_size_t" 1>&6
-if test $ac_cv_type_size_t = no; then
-  cat >> confdefs.h <<\EOF
-#define size_t unsigned
-EOF
-
-fi
-
-echo $ac_n "checking for vprintf""... $ac_c" 1>&6
-echo "configure:1155: checking for vprintf" >&5
-if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  cat > conftest.$ac_ext <<EOF
-#line 1160 "configure"
-#include "confdefs.h"
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char vprintf(); below.  */
-#include <assert.h>
-/* Override any gcc2 internal prototype to avoid an error.  */
-/* We use char because int might match the return type of a gcc2
-    builtin and then its argument prototype would still apply.  */
-char vprintf();
-
-int main() {
-
-/* The GNU C library defines this for functions which it implements
-    to always fail with ENOSYS.  Some functions are actually named
-    something starting with __ and the normal name is an alias.  */
-#if defined (__stub_vprintf) || defined (__stub___vprintf)
-choke me
-#else
-vprintf();
-#endif
-
-; return 0; }
-EOF
-if { (eval echo configure:1183: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
-  rm -rf conftest*
-  eval "ac_cv_func_vprintf=yes"
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-  rm -rf conftest*
-  eval "ac_cv_func_vprintf=no"
-fi
-rm -f conftest*
-fi
-
-if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
-  echo "$ac_t""yes" 1>&6
-  cat >> confdefs.h <<\EOF
-#define HAVE_VPRINTF 1
-EOF
-
-else
-  echo "$ac_t""no" 1>&6
-fi
-
-if test "$ac_cv_func_vprintf" != yes; then
-echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
-echo "configure:1207: checking for _doprnt" >&5
-if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  cat > conftest.$ac_ext <<EOF
-#line 1212 "configure"
-#include "confdefs.h"
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char _doprnt(); below.  */
-#include <assert.h>
-/* Override any gcc2 internal prototype to avoid an error.  */
-/* We use char because int might match the return type of a gcc2
-    builtin and then its argument prototype would still apply.  */
-char _doprnt();
-
-int main() {
-
-/* The GNU C library defines this for functions which it implements
-    to always fail with ENOSYS.  Some functions are actually named
-    something starting with __ and the normal name is an alias.  */
-#if defined (__stub__doprnt) || defined (__stub____doprnt)
-choke me
-#else
-_doprnt();
-#endif
-
-; return 0; }
-EOF
-if { (eval echo configure:1235: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
-  rm -rf conftest*
-  eval "ac_cv_func__doprnt=yes"
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-  rm -rf conftest*
-  eval "ac_cv_func__doprnt=no"
-fi
-rm -f conftest*
-fi
-
-if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
-  echo "$ac_t""yes" 1>&6
-  cat >> confdefs.h <<\EOF
-#define HAVE_DOPRNT 1
-EOF
-
-else
-  echo "$ac_t""no" 1>&6
-fi
-
-fi
-
-echo $ac_n "checking for working const""... $ac_c" 1>&6
-echo "configure:1260: checking for working const" >&5
-if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
-  echo $ac_n "(cached) $ac_c" 1>&6
-else
-  cat > conftest.$ac_ext <<EOF
-#line 1265 "configure"
-#include "confdefs.h"
-
-int main() {
-
-/* Ultrix mips cc rejects this.  */
-typedef int charset[2]; const charset x;
-/* SunOS 4.1.1 cc rejects this.  */
-char const *const *ccp;
-char **p;
-/* NEC SVR4.0.2 mips cc rejects this.  */
-struct point {int x, y;};
-static struct point const zero = {0,0};
-/* AIX XL C 1.02.0.0 rejects this.
-   It does not let you subtract one const X* pointer from another in an arm
-   of an if-expression whose if-part is not a constant expression */
-const char *g = "string";
-ccp = &g + (g ? g-g : 0);
-/* HPUX 7.0 cc rejects these. */
-++ccp;
-p = (char**) ccp;
-ccp = (char const *const *) p;
-{ /* SCO 3.2v4 cc rejects this.  */
-  char *t;
-  char const *s = 0 ? (char *) 0 : (char const *) 0;
-
-  *t++ = 0;
-}
-{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
-  int x[] = {25, 17};
-  const int *foo = &x[0];
-  ++foo;
-}
-{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
-  typedef const int *iptr;
-  iptr p = 0;
-  ++p;
-}
-{ /* AIX XL C 1.02.0.0 rejects this saying
-     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
-  struct s { int j; const int *ap[3]; };
-  struct s *b; b->j = 5;
-}
-{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
-  const int foo = 10;
-}
-
-; return 0; }
-EOF
-if { (eval echo configure:1314: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-  rm -rf conftest*
-  ac_cv_c_const=yes
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-  rm -rf conftest*
-  ac_cv_c_const=no
-fi
-rm -f conftest*
-fi
-
-echo "$ac_t""$ac_cv_c_const" 1>&6
-if test $ac_cv_c_const = no; then
-  cat >> confdefs.h <<\EOF
-#define const 
-EOF
-
-fi
-
 echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
-echo "configure:1335: checking whether byte ordering is bigendian" >&5
+echo "configure:1129: checking whether byte ordering is bigendian" >&5
 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   ac_cv_c_bigendian=unknown
 # See if sys/param.h defines the BYTE_ORDER macro.
 cat > conftest.$ac_ext <<EOF
-#line 1342 "configure"
+#line 1136 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -1349,11 +1143,11 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:1353: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1147: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   # It does; now see whether it defined to BIG_ENDIAN or not.
 cat > conftest.$ac_ext <<EOF
-#line 1357 "configure"
+#line 1151 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -1364,7 +1158,7 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:1368: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1162: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_bigendian=yes
 else
@@ -1384,7 +1178,7 @@ if test "$cross_compiling" = yes; then
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 1388 "configure"
+#line 1182 "configure"
 #include "confdefs.h"
 main () {
   /* Are we little or big endian?  From Harbison&Steele.  */
@@ -1397,7 +1191,7 @@ main () {
   exit (u.c[sizeof (long) - 1] == 1);
 }
 EOF
-if { (eval echo configure:1401: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1195: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_c_bigendian=no
 else
@@ -1422,7 +1216,7 @@ fi
 
 
 echo $ac_n "checking for mpz_init_set_str in -lgmp2""... $ac_c" 1>&6
-echo "configure:1426: checking for mpz_init_set_str in -lgmp2" >&5
+echo "configure:1220: checking for mpz_init_set_str in -lgmp2" >&5
 ac_lib_var=`echo gmp2'_'mpz_init_set_str | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1430,7 +1224,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lgmp2  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1434 "configure"
+#line 1228 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1441,7 +1235,7 @@ int main() {
 mpz_init_set_str()
 ; return 0; }
 EOF
-if { (eval echo configure:1445: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1239: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1469,7 +1263,7 @@ else
 fi
 
 echo $ac_n "checking for yywrap in -lfl""... $ac_c" 1>&6
-echo "configure:1473: checking for yywrap in -lfl" >&5
+echo "configure:1267: checking for yywrap in -lfl" >&5
 ac_lib_var=`echo fl'_'yywrap | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1477,7 +1271,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lfl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1481 "configure"
+#line 1275 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1488,7 +1282,7 @@ int main() {
 yywrap()
 ; return 0; }
 EOF
-if { (eval echo configure:1492: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1286: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1516,7 +1310,7 @@ else
 fi
 
 echo $ac_n "checking for adns_init in -ladns""... $ac_c" 1>&6
-echo "configure:1520: checking for adns_init in -ladns" >&5
+echo "configure:1314: checking for adns_init in -ladns" >&5
 ac_lib_var=`echo adns'_'adns_init | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1524,7 +1318,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-ladns  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1528 "configure"
+#line 1322 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1535,7 +1329,7 @@ int main() {
 adns_init()
 ; return 0; }
 EOF
-if { (eval echo configure:1539: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1333: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
index 6c85df9..ff74f20 100644 (file)
@@ -11,11 +11,8 @@ AC_PROG_MAKE_SET
 AC_PROG_CC
 AC_PATH_PROG(RM,rm)
 AC_STDC_HEADERS
+AC_CHECK_HEADERS(linux/if.h)
 AC_PROG_INSTALL
-AC_PID_T
-AC_SIZE_T
-AC_VPRINTF
-AC_C_CONST
 AC_C_BIGENDIAN
 
 AC_CHECK_LIB(gmp2,mpz_init_set_str)
diff --git a/foo.pid b/foo.pid
new file mode 100644 (file)
index 0000000..cf16ce1
--- /dev/null
+++ b/foo.pid
@@ -0,0 +1 @@
+12157
diff --git a/linux/if_tun.h b/linux/if_tun.h
new file mode 100644 (file)
index 0000000..fd610f3
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *  Universal TUN/TAP device driver.
+ *  Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  $Id: if_tun.h,v 1.5 2000/10/24 05:13:08 maxk Exp $
+ */
+
+#ifndef __IF_TUN_H
+#define __IF_TUN_H
+
+/* Uncomment to enable debugging */
+/* #define TUN_DEBUG 1 */
+
+#ifdef __KERNEL__
+
+#ifdef TUN_DEBUG
+#define DBG  if(tun->debug)printk
+#define DBG1 if(debug==2)printk
+#else
+#define DBG( a... )
+#define DBG1( a... )
+#endif
+
+struct tun_struct {
+       char                    name[8];
+       unsigned long           flags;
+
+       struct fasync_struct    *fasync;
+       wait_queue_head_t       read_wait;
+
+       struct net_device       dev;
+       struct sk_buff_head     txq;
+        struct net_device_stats        stats;
+
+#ifdef TUN_DEBUG       
+       int debug;
+#endif  
+};
+
+#ifndef MIN
+#define MIN(a,b) ( (a)<(b) ? (a):(b) ) 
+#endif
+
+#endif /* __KERNEL__ */
+
+/* Number of devices */
+#define TUN_MAX_DEV    255
+
+/* TX queue size */
+#define TUN_TXQ_SIZE   10
+
+/* Max frame size */
+#define TUN_MAX_FRAME  4096
+
+/* TUN device flags */
+#define TUN_TUN_DEV    0x0001  
+#define TUN_TAP_DEV    0x0002
+#define TUN_TYPE_MASK   0x000f
+
+#define TUN_FASYNC     0x0010
+#define TUN_NOCHECKSUM 0x0020
+#define TUN_NO_PI      0x0040
+
+#define TUN_IFF_SET    0x1000
+
+/* Ioctl defines */
+#define TUNSETNOCSUM (('T'<< 8) | 200) 
+#define TUNSETDEBUG  (('T'<< 8) | 201) 
+#define TUNSETIFF    (('T'<< 8) | 202) 
+
+/* TUNSETIFF ifr flags */
+#define IFF_TUN                0x0001
+#define IFF_TAP                0x0002
+#define IFF_NO_PI      0x1000
+
+struct tun_pi {
+   unsigned short flags;
+   unsigned short proto;
+};
+#define TUN_PKT_STRIP  0x0001
+
+#endif /* __IF_TUN_H */
index 7db3559..0b1ebe4 100644 (file)
--- a/netlink.c
+++ b/netlink.c
@@ -1,8 +1,9 @@
 /* User-kernel network link */
 
-/* We support a variety of methods: userv-ipif, ipif on its own (when
-   we run as root), SLIP to a pty, an external netlink daemon. There
-   is a performance/security tradeoff. */
+/* We will eventually support a variety of methods for extracting
+   packets from the kernel: userv-ipif, ipif on its own (when we run
+   as root), the kernel TUN driver, SLIP to a pty, an external netlink
+   daemon. There is a performance/security tradeoff. */
 
 /* When dealing with SLIP (to a pty, or ipif) we have separate rx, tx
    and client buffers. When receiving we may read() any amount, not
    and may be part-way through receiving. */
 
 /* Each netlink device is actually a router, with its own IP
-   address. We should eventually do things like decreasing the TTL and
-   recalculating the header checksum, generating ICMP, responding to
-   pings, etc. but for now we can get away without them. We should
-   implement this stuff no matter how we get the packets to/from the
-   kernel. */
+   address. We do things like decreasing the TTL and recalculating the
+   header checksum, generating ICMP, responding to pings, etc. */
 
 /* This is where we have the anti-spoofing paranoia - before sending a
    packet to the kernel we check that the tunnel it came over could
    reasonably have produced it. */
 
+/* XXX now implement TUN. Kernel needs recompiling. */
+
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/ioctl.h>
 
+#include "config.h"
 #include "secnet.h"
 #include "util.h"
 
+#ifdef HAVE_LINUX_IF_H
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#endif
+
+/* XXX where do we find if_tun on other architectures? */
+
 #define DEFAULT_BUFSIZE 2048
+#define DEFAULT_MTU 1000
+#define ICMP_BUFSIZE 1024
 
 #define SLIP_END    192
 #define SLIP_ESC    219
@@ -39,85 +50,571 @@ struct netlink_client {
     struct subnet_list *networks;
     netlink_deliver_fn *deliver;
     void *dst;
+    string_t name;
+    bool_t can_deliver;
     struct netlink_client *next;
 };
 
-struct userv {
+/* Netlink provides one function to the device driver, to call to deliver
+   a packet from the device. The device driver provides one function to
+   netlink, for it to call to deliver a packet to the device. */
+
+struct netlink {
     closure_t cl;
     struct netlink_if ops;
+    void *dst; /* Pointer to host interface state */
+    string_t name;
     uint32_t max_start_pad;
     uint32_t max_end_pad;
-    int txfd; /* We transmit to userv */
-    int rxfd; /* We receive from userv */
-    struct netlink_client *clients;
-    string_t name;
-    string_t userv_path;
-    string_t service_user;
-    string_t service_name;
     struct subnet_list networks;
-    uint32_t local_address;
-    uint32_t secnet_address;
+    uint32_t local_address; /* host interface address */
+    uint32_t secnet_address; /* our own address */
     uint32_t mtu;
-    uint32_t txbuflen;
-    struct buffer_if *buff; /* We unstuff received packets into here
-                              and send them to the site code. */
-    bool_t pending_esc;
+    struct netlink_client *clients;
+    netlink_deliver_fn *deliver_to_host; /* Provided by driver */
+    struct buffer_if icmp; /* Buffer for assembly of outgoing ICMP */
 };
 
-static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
-                           int *timeout_io, const struct timeval *tv_now,
-                           uint64_t *now)
+/* Generic IP checksum routine */
+static inline uint16_t ip_csum(uint8_t *iph,uint32_t count)
 {
-    struct userv *st=sst;
-    *nfds_io=2;
-    fds[0].fd=st->txfd;
-    fds[0].events=POLLERR; /* Might want to pick up POLLOUT sometime */
-    fds[1].fd=st->rxfd;
-    fds[1].events=POLLIN|POLLERR|POLLHUP;
-    return 0;
+    register uint32_t sum=0;
+
+    while (count>1) {
+       sum+=ntohs(*(uint16_t *)iph);
+       iph+=2;
+       count-=2;
+    }
+    if(count>0)
+       sum+=*(uint8_t *)iph;
+    while (sum>>16)
+       sum=(sum&0xffff)+(sum>>16);
+    return htons(~sum);
 }
 
-static void process_local_packet(struct userv *st)
+#ifdef i386
+/*
+ *      This is a version of ip_compute_csum() optimized for IP headers,
+ *      which always checksum on 4 octet boundaries.
+ *
+ *      By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
+ *      Arnt Gulbrandsen.
+ */
+static inline uint16_t ip_fast_csum(uint8_t *iph, uint32_t ihl) {
+    uint32_t sum;
+
+    __asm__ __volatile__("
+            movl (%1), %0
+            subl $4, %2
+            jbe 2f
+            addl 4(%1), %0
+            adcl 8(%1), %0
+            adcl 12(%1), %0
+1:          adcl 16(%1), %0
+            lea 4(%1), %1
+            decl %2
+            jne 1b
+            adcl $0, %0
+            movl %0, %2
+            shrl $16, %0
+            addw %w2, %w0
+            adcl $0, %0
+            notl %0
+2:
+            "
+        /* Since the input registers which are loaded with iph and ipl
+           are modified, we must also specify them as outputs, or gcc
+           will assume they contain their original values. */
+        : "=r" (sum), "=r" (iph), "=r" (ihl)
+        : "1" (iph), "2" (ihl));
+    return sum;
+}
+#else
+static inline uint16_t ip_fast_csum(uint8_t *iph, uint32_t ihl)
 {
-    uint32_t source,dest;
+    return ip_csum(iph,ihl*4);
+}
+#endif
+
+struct iphdr {
+#if defined (WORDS_BIGENDIAN)
+    uint8_t    version:4,
+              ihl:4;
+#else
+    uint8_t    ihl:4,
+              version:4;
+#endif
+    uint8_t    tos;
+    uint16_t   tot_len;
+    uint16_t   id;
+    uint16_t   frag_off;
+    uint8_t    ttl;
+    uint8_t    protocol;
+    uint16_t   check;
+    uint32_t   saddr;
+    uint32_t   daddr;
+    /* The options start here. */
+};
+
+struct icmphdr {
+    struct iphdr iph;
+    uint8_t type;
+    uint8_t code;
+    uint16_t check;
+    union {
+       uint32_t unused;
+       struct {
+           uint8_t pointer;
+           uint8_t unused1;
+           uint16_t unused2;
+       } pprob;
+       uint32_t gwaddr;
+       struct {
+           uint16_t id;
+           uint16_t seq;
+       } echo;
+    } d;
+};
+    
+static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf);
+
+static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
+                                        uint32_t dest,uint16_t len)
+{
+    struct icmphdr *h;
+
+    BUF_ALLOC(&st->icmp,"netlink_icmp_tmpl");
+    buffer_init(&st->icmp,st->max_start_pad);
+    h=buf_append(&st->icmp,sizeof(*h));
+
+    h->iph.version=4;
+    h->iph.ihl=5;
+    h->iph.tos=0;
+    h->iph.tot_len=htons(len+(h->iph.ihl*4)+8);
+    h->iph.id=0;
+    h->iph.frag_off=0;
+    h->iph.ttl=255;
+    h->iph.protocol=1;
+    h->iph.saddr=htonl(st->secnet_address);
+    h->iph.daddr=htonl(dest);
+    h->iph.check=0;
+    h->iph.check=ip_fast_csum((uint8_t *)&h->iph,h->iph.ihl);
+    h->check=0;
+    h->d.unused=0;
+
+    return h;
+}
+
+/* Fill in the ICMP checksum field correctly */
+static void netlink_icmp_csum(struct icmphdr *h)
+{
+    uint32_t len;
+
+    len=ntohs(h->iph.tot_len)-(4*h->iph.ihl);
+    h->check=0;
+    h->check=ip_csum(&h->type,len);
+}
+
+/* RFC1122:
+ *       An ICMP error message MUST NOT be sent as the result of
+ *       receiving:
+ *
+ *       *    an ICMP error message, or
+ *
+ *       *    a datagram destined to an IP broadcast or IP multicast
+ *            address, or
+ *
+ *       *    a datagram sent as a link-layer broadcast, or
+ *
+ *       *    a non-initial fragment, or
+ *
+ *       *    a datagram whose source address does not define a single
+ *            host -- e.g., a zero address, a loopback address, a
+ *            broadcast address, a multicast address, or a Class E
+ *            address.
+ */
+static bool_t netlink_icmp_may_reply(struct buffer_if *buf)
+{
+    struct iphdr *iph;
+    uint32_t source;
+
+    iph=(struct iphdr *)buf->start;
+    if (iph->protocol==1) return False; /* Overly-broad; we may reply to
+                                          eg. icmp echo-request */
+    /* How do we spot broadcast destination addresses? */
+    if (ntohs(iph->frag_off)&0x1fff) return False; /* Non-initial fragment */
+    source=ntohl(iph->saddr);
+    if (source==0) return False;
+    if ((source&0xff000000)==0x7f000000) return False;
+    /* How do we spot broadcast source addresses? */
+    if ((source&0xf0000000)==0xe0000000) return False; /* Multicast */
+    if ((source&0xf0000000)==0xf0000000) return False; /* Class E */
+    return True;
+}
+
+/* How much of the original IP packet do we include in its ICMP
+   response? The header plus up to 64 bits. */
+static uint16_t netlink_icmp_reply_len(struct buffer_if *buf)
+{
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    uint16_t hlen,plen;
+
+    hlen=iph->ihl*4;
+    /* We include the first 8 bytes of the packet data, provided they exist */
+    hlen+=8;
+    plen=ntohs(iph->tot_len);
+    return (hlen>plen?plen:hlen);
+}
+
+static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
+                               uint8_t type, uint8_t code)
+{
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    struct icmphdr *h;
+    uint16_t len;
+
+    if (netlink_icmp_may_reply(buf)) {
+       len=netlink_icmp_reply_len(buf);
+       h=netlink_icmp_tmpl(st,ntohl(iph->saddr),len);
+       h->type=type; h->code=code;
+       memcpy(buf_append(&st->icmp,len),buf->start,len);
+       netlink_icmp_csum(h);
+       netlink_packet_deliver(st,&st->icmp);
+       BUF_ASSERT_FREE(&st->icmp);
+    }
+}
+
+/*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the
+ * checksum.
+ *
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly.
+ * 4. Doesn't have a bogus length
+ */
+static bool_t netlink_check(struct netlink *st, struct buffer_if *buf)
+{
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    uint32_t len;
+
+    if (iph->ihl < 5 || iph->version != 4) {
+       printf("ihl/version check failed\n");
+       return False;
+    }
+    if (buf->size < iph->ihl*4) {
+       printf("buffer size check failed\n");
+       return False;
+    }
+    if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) {
+       printf("checksum failed\n");
+       return False;
+    }
+    len=ntohs(iph->tot_len);
+    /* There should be no padding */
+    if (buf->size!=len || len<(iph->ihl<<2)) {
+       printf("length check failed buf->size=%d len=%d\n",buf->size,len);
+       return False;
+    }
+
+    /* XXX check that there's no source route specified */
+    return True;
+}
+
+static void netlink_packet_deliver(struct netlink *st, struct buffer_if *buf)
+{
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    uint32_t dest=ntohl(iph->daddr);
     struct netlink_client *c;
 
-    source=ntohl(*(uint32_t *)(st->buff->start+12));
-    dest=ntohl(*(uint32_t *)(st->buff->start+16));
+    BUF_ASSERT_USED(buf);
 
-/*    printf("process_local_packet source=%s dest=%s len=%d\n",
-      ipaddr_to_string(source),ipaddr_to_string(dest),
-      st->buff->size); */
-    if (!subnet_match(&st->networks,source)) {
-       string_t s,d;
-       s=ipaddr_to_string(source);
-       d=ipaddr_to_string(dest);
-       Message(M_WARNING,"%s: outgoing packet with bad source address "
-               "(s=%s,d=%s)\n",st->name,s,d);
-       free(s); free(d);
+    if (dest==st->secnet_address) {
+       Message(M_ERROR,"%s: trying to deliver a packet to myself!\n");
+       BUF_FREE(buf);
        return;
     }
+    
     for (c=st->clients; c; c=c->next) {
        if (subnet_match(c->networks,dest)) {
-           c->deliver(c->dst,c,st->buff);
-           BUF_ALLOC(st->buff,"netlink:process_local_packet");
+           if (c->can_deliver) {
+               c->deliver(c->dst,c,buf);
+               BUF_ASSERT_FREE(buf);
+           } else {
+               /* Generate ICMP destination unreachable */
+               netlink_icmp_simple(st,buf,3,0);
+               BUF_FREE(buf);
+           }
            return;
        }
     }
+    if (subnet_match(&st->networks,dest)) {
+       st->deliver_to_host(st->dst,NULL,buf);
+       BUF_ASSERT_FREE(buf);
+       return;
+    }
+    Message(M_ERROR,"%s: failed to deliver a packet (bad destination address)"
+           "\nXXX make this message clearer\n");
+    BUF_FREE(buf);
+}
+
+static void netlink_packet_forward(struct netlink *st, struct buffer_if *buf)
+{
+    struct iphdr *iph=(struct iphdr *)buf->start;
+    
+    BUF_ASSERT_USED(buf);
+
+    /* Packet has already been checked */
+    if (iph->ttl<=1) {
+       /* Generate ICMP time exceeded */
+       netlink_icmp_simple(st,buf,11,0);
+       BUF_FREE(buf);
+       return;
+    }
+    iph->ttl--;
+    iph->check=0;
+    iph->check=ip_fast_csum((uint8_t *)iph,iph->ihl);
+
+    netlink_packet_deliver(st,buf);
+    BUF_ASSERT_FREE(buf);
+}
+
+/* Someone has been foolish enough to address a packet to us. I
+   suppose we should reply to it, just to be polite. */
+static void netlink_packet_local(struct netlink *st, struct buffer_if *buf)
+{
+    struct icmphdr *h;
+
+    h=(struct icmphdr *)buf->start;
+
+    if ((ntohs(h->iph.frag_off)&0xbfff)!=0) {
+       Message(M_WARNING,"%s: fragmented packet addressed to us\n",st->name);
+       BUF_FREE(buf);
+       return;
+    }
+
+    if (h->iph.protocol==1) {
+       /* It's ICMP */
+       if (h->type==8 && h->code==0) {
+           /* ICMP echo-request. Special case: we re-use the buffer
+              to construct the reply. */
+           h->type=0;
+           h->iph.daddr=h->iph.saddr;
+           h->iph.saddr=htonl(st->secnet_address);
+           h->iph.ttl=255; /* Be nice and bump it up again... */
+           h->iph.check=0;
+           h->iph.check=ip_fast_csum((uint8_t *)h,h->iph.ihl);
+           netlink_icmp_csum(h);
+           netlink_packet_deliver(st,buf);
+           return;
+       }
+       Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name);
+    } else {
+       /* Send ICMP protocol unreachable */
+       netlink_icmp_simple(st,buf,3,2);
+       BUF_FREE(buf);
+       return;
+    }
+
+    BUF_FREE(buf);
+}
+
+/* Called by site code when remote packet is available */
+/* buf is allocated on entry and free on return */
+static void netlink_from_tunnel(void *sst, void *cst, struct buffer_if *buf)
+{
+    struct netlink *st=sst;
+    struct netlink_client *client=cst;
+    uint32_t source,dest;
+    struct iphdr *iph;
+
+    BUF_ASSERT_USED(buf);
+    if (!netlink_check(st,buf)) {
+       Message(M_WARNING,"%s: bad IP packet from tunnel %s\n",
+               st->name,client->name);
+       BUF_FREE(buf);
+       return;
+    }
+    iph=(struct iphdr *)buf->start;
+
+    source=ntohl(iph->saddr);
+    dest=ntohl(iph->daddr);
+
+    /* Check that the packet source is in 'nets' and its destination is
+       in client->networks */
+    if (!subnet_match(client->networks,source)) {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_WARNING,"%s: packet from tunnel %s with bad source address "
+               "(s=%s,d=%s)\n",st->name,client->name,s,d);
+       free(s); free(d);
+       BUF_FREE(buf);
+       return;
+    }
+    /* (st->secnet_address needs checking before matching against
+       st->networks because secnet's IP address may not be in the
+       range the host is willing to deal with) */
     if (dest==st->secnet_address) {
-       printf("%s: secnet received packet of len %d from %s\n",st->name,
-              st->buff->size,ipaddr_to_string(source));
+        netlink_packet_local(st,buf);
+       BUF_ASSERT_FREE(buf);
        return;
     }
-    {
+    if (!subnet_match(&st->networks,dest)) {
        string_t s,d;
        s=ipaddr_to_string(source);
        d=ipaddr_to_string(dest);
-       Message(M_WARNING,"%s: outgoing packet with bad destination address "
-                         "(s=%s,d=%s)\n",st->name,s,d);
+       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,buf);
+
+    BUF_ASSERT_FREE(buf);
+}
+
+/* Called by driver code when packet is received from kernel */
+/* cid should be NULL */
+/* buf should be allocated on entry, and is free on return */
+static void netlink_from_host(void *sst, void *cid, struct buffer_if *buf)
+{
+    struct netlink *st=sst;
+    uint32_t source,dest;
+    struct iphdr *iph;
+
+    BUF_ASSERT_USED(buf);
+    if (!netlink_check(st,buf)) {
+       Message(M_WARNING,"%s: bad IP packet from host\n",
+               st->name);
+       BUF_FREE(buf);
+       return;
+    }
+    iph=(struct iphdr *)buf->start;
+
+    source=ntohl(iph->saddr);
+    dest=ntohl(iph->daddr);
+
+    if (!subnet_match(&st->networks,source)) {
+       string_t s,d;
+       s=ipaddr_to_string(source);
+       d=ipaddr_to_string(dest);
+       Message(M_WARNING,"%s: outgoing packet with bad source address "
+               "(s=%s,d=%s)\n",st->name,s,d);
+       free(s); free(d);
+       BUF_FREE(buf);
+       return;
+    }
+    if (dest==st->secnet_address) {
+       netlink_packet_local(st,buf);
+       BUF_ASSERT_FREE(buf);
+       return;
+    }
+    netlink_packet_forward(st,buf);
+    BUF_ASSERT_FREE(buf);
+}
+
+static void netlink_set_delivery(void *sst, void *cid, bool_t can_deliver)
+{
+    struct netlink_client *c=cid;
+
+    c->can_deliver=can_deliver;
+}
+
+static void *netlink_regnets(void *sst, struct subnet_list *nets,
+                            netlink_deliver_fn *deliver, void *dst,
+                            uint32_t max_start_pad, uint32_t max_end_pad,
+                            string_t client_name)
+{
+    struct netlink *st=sst;
+    struct netlink_client *c;
+
+    Message(M_DEBUG_CONFIG,"netlink_regnets: request for %d networks, "
+           "max_start_pad=%d, max_end_pad=%d\n",
+           nets->entries,max_start_pad,max_end_pad);
+
+    c=safe_malloc(sizeof(*c),"netlink_regnets");
+    c->networks=nets;
+    c->deliver=deliver;
+    c->dst=dst;
+    c->name=client_name; /* XXX copy it? */
+    c->can_deliver=False;
+    c->next=st->clients;
+    st->clients=c;
+    if (max_start_pad > st->max_start_pad) st->max_start_pad=max_start_pad;
+    if (max_end_pad > st->max_end_pad) st->max_end_pad=max_end_pad;
+
+    return c;
+}
+
+static netlink_deliver_fn *netlink_init(struct netlink *st,
+                                       void *dst, struct cloc loc,
+                                       dict_t *dict, string_t description,
+                                       netlink_deliver_fn *to_host)
+{
+    st->dst=dst;
+    st->cl.description=description;
+    st->cl.type=CL_NETLINK;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.regnets=netlink_regnets;
+    st->ops.deliver=netlink_from_tunnel;
+    st->ops.set_delivery=netlink_set_delivery;
+    st->max_start_pad=0;
+    st->max_end_pad=0;
+    st->clients=NULL;
+    st->deliver_to_host=to_host;
+
+    st->name=dict_read_string(dict,"name",False,"netlink",loc);
+    if (!st->name) st->name=description;
+    dict_read_subnet_list(dict, "networks", True, "netlink", loc,
+                         &st->networks);
+    st->local_address=string_to_ipaddr(
+       dict_find_item(dict,"local-address", True, "netlink", loc),"netlink");
+    st->secnet_address=string_to_ipaddr(
+       dict_find_item(dict,"secnet-address", True, "netlink", loc),"netlink");
+    if (!subnet_match(&st->networks,st->local_address)) {
+       cfgfatal(loc,"netlink","local-address must be in local networks\n");
+    }
+    st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
+    buffer_new(&st->icmp,ICMP_BUFSIZE);
+
+    return netlink_from_host;
+}
+
+/* Connection to the kernel through userv-ipif */
+
+struct userv {
+    struct netlink nl;
+    int txfd; /* We transmit to userv */
+    int rxfd; /* We receive from userv */
+    string_t userv_path;
+    string_t service_user;
+    string_t service_name;
+    uint32_t txbuflen;
+    struct buffer_if *buff; /* We unstuff received packets into here
+                              and send them to the site code. */
+    bool_t pending_esc;
+    netlink_deliver_fn *netlink_to_tunnel;
+};
+
+static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                           int *timeout_io, const struct timeval *tv_now,
+                           uint64_t *now)
+{
+    struct userv *st=sst;
+    *nfds_io=2;
+    fds[0].fd=st->txfd;
+    fds[0].events=POLLERR; /* Might want to pick up POLLOUT sometime */
+    fds[1].fd=st->rxfd;
+    fds[1].events=POLLIN|POLLERR|POLLHUP;
+    return 0;
 }
 
 static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds,
@@ -140,6 +637,7 @@ static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds,
        }
        /* XXX really crude unstuff code */
        /* XXX check for buffer overflow */
+       BUF_ASSERT_USED(st->buff);
        for (i=0; i<l; i++) {
            if (st->pending_esc) {
                st->pending_esc=False;
@@ -156,9 +654,12 @@ static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds,
            } else {
                switch (rxbuf[i]) {
                case SLIP_END:
-                   if (st->buff->size>0) process_local_packet(st);
-                   BUF_ASSERT_USED(st->buff);
-                   buffer_init(st->buff,st->max_start_pad);
+                   if (st->buff->size>0) {
+                       st->netlink_to_tunnel(&st->nl,NULL,
+                                             st->buff);
+                       BUF_ALLOC(st->buff,"userv_afterpoll");
+                   }
+                   buffer_init(st->buff,st->nl.max_start_pad);
                    break;
                case SLIP_ESC:
                    st->pending_esc=True;
@@ -170,7 +671,44 @@ static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds,
            }
        }
     }
-    return;
+}
+
+/* Send buf to the kernel. Free buf before returning. */
+static void userv_deliver_to_kernel(void *sst, void *cid,
+                                   struct buffer_if *buf)
+{
+    struct userv *st=sst;
+    uint8_t txbuf[DEFAULT_BUFSIZE];
+    uint8_t *i;
+    uint32_t j;
+
+    BUF_ASSERT_USED(buf);
+
+    /* Spit the packet at userv-ipif: SLIP start marker, then
+       bytestuff the packet, then SLIP end marker */
+    /* XXX crunchy bytestuff code */
+    j=0;
+    txbuf[j++]=SLIP_END;
+    for (i=buf->start; i<(buf->start+buf->size); i++) {
+       switch (*i) {
+       case SLIP_END:
+           txbuf[j++]=SLIP_ESC;
+           txbuf[j++]=SLIP_ESCEND;
+           break;
+       case SLIP_ESC:
+           txbuf[j++]=SLIP_ESC;
+           txbuf[j++]=SLIP_ESCESC;
+           break;
+       default:
+           txbuf[j++]=*i;
+           break;
+       }
+    }
+    txbuf[j++]=SLIP_END;
+    if (write(st->txfd,txbuf,j)<0) {
+       fatal_perror("userv_deliver_to_kernel: write()");
+    }
+    BUF_FREE(buf);
 }
 
 static void userv_phase_hook(void *sst, uint32_t newphase)
@@ -189,12 +727,12 @@ static void userv_phase_hook(void *sst, uint32_t newphase)
        be using should already have been registered. */
 
     addrs=safe_malloc(512,"userv_phase_hook:addrs");
-    snprintf(addrs,512,"%s,%s,%d,slip",ipaddr_to_string(st->local_address),
-            ipaddr_to_string(st->secnet_address),st->mtu);
+    snprintf(addrs,512,"%s,%s,%d,slip",ipaddr_to_string(st->nl.local_address),
+            ipaddr_to_string(st->nl.secnet_address),st->nl.mtu);
 
     nets=safe_malloc(1024,"userv_phase_hook:nets");
     *nets=0;
-    for (c=st->clients; c; c=c->next) {
+    for (c=st->nl.clients; c; c=c->next) {
        for (i=0; i<c->networks->entries; i++) {
            s=subnet_to_string(&c->networks->list[i]);
            strcat(nets,s);
@@ -258,181 +796,296 @@ static void userv_phase_hook(void *sst, uint32_t newphase)
     /* We are the parent... */
           
     /* Register for poll() */
-    register_for_poll(st, userv_beforepoll, userv_afterpoll, 2, "netlink");
+    register_for_poll(st, userv_beforepoll, userv_afterpoll, 2, st->nl.name);
 }
 
-static void *userv_regnets(void *sst, struct subnet_list *nets,
-                          netlink_deliver_fn *deliver, void *dst,
-                          uint32_t max_start_pad, uint32_t max_end_pad)
+static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context,
+                          list_t *args)
 {
-    struct userv *st=sst;
-    struct netlink_client *c;
+    struct userv *st;
+    item_t *item;
+    dict_t *dict;
 
-    Message(M_DEBUG_CONFIG,"userv_regnets: request for %d networks, "
-           "max_start_pad=%d, max_end_pad=%d\n",
-           nets->entries,max_start_pad,max_end_pad);
+    st=safe_malloc(sizeof(*st),"userv_apply");
 
-    c=safe_malloc(sizeof(*c),"userv_regnets");
-    c->networks=nets;
-    c->deliver=deliver;
-    c->dst=dst;
-    c->next=st->clients;
-    st->clients=c;
-    if (max_start_pad > st->max_start_pad) st->max_start_pad=max_start_pad;
-    if (max_end_pad > st->max_end_pad) st->max_end_pad=max_end_pad;
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
 
-    return c;
+    st->netlink_to_tunnel=
+       netlink_init(&st->nl,st,loc,dict,
+                    "netlink-userv-ipif",userv_deliver_to_kernel);
+
+    st->userv_path=dict_read_string(dict,"userv-path",False,"userv-netlink",
+                                   loc);
+    st->service_user=dict_read_string(dict,"service-user",False,
+                                     "userv-netlink",loc);
+    st->service_name=dict_read_string(dict,"service-name",False,
+                                     "userv-netlink",loc);
+    if (!st->userv_path) st->userv_path="userv";
+    if (!st->service_user) st->service_user="root";
+    if (!st->service_name) st->service_name="ipif";
+    st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"userv-netlink",loc);
+    BUF_ALLOC(st->buff,"netlink:userv_apply");
+
+    st->rxfd=-1; st->txfd=-1;
+    add_hook(PHASE_DROPPRIV,userv_phase_hook,st);
+
+    return new_closure(&st->nl.cl);
 }
 
-static void userv_deliver(void *sst, void *cid, struct buffer_if *buf)
-{
-    struct userv *st=sst;
-    struct netlink_client *client=cid;
-    uint8_t txbuf[DEFAULT_BUFSIZE];
+/* Connection to the kernel through the universal TUN/TAP driver */
 
-    uint32_t source,dest;
-    uint8_t *i;
-    uint32_t j;
+struct tun {
+    struct netlink nl;
+    int fd;
+    string_t device_path;
+    string_t interface_name;
+    string_t ifconfig_path;
+    string_t route_path;
+    struct buffer_if *buff; /* We receive packets into here
+                              and send them to the netlink code. */
+    netlink_deliver_fn *netlink_to_tunnel;
+};
 
-    source=ntohl(*(uint32_t *)(buf->start+12));
-    dest=ntohl(*(uint32_t *)(buf->start+16));
+static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+                         int *timeout_io, const struct timeval *tv_now,
+                         uint64_t *now)
+{
+    struct tun *st=sst;
+    *nfds_io=1;
+    fds[0].fd=st->fd;
+    fds[0].events=POLLIN|POLLERR|POLLHUP;
+    return 0;
+}
 
-    /* Check that the packet source is in 'nets' and its destination is
-       in client->networks */
-    if (!subnet_match(client->networks,source)) {
-       string_t s,d;
-       s=ipaddr_to_string(source);
-       d=ipaddr_to_string(dest);
-       Message(M_WARNING,"%s: incoming packet with bad source address "
-               "(s=%s,d=%s)\n",st->name,s,d);
-       free(s); free(d);
-       return;
+static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds,
+                           const struct timeval *tv_now, uint64_t *now)
+{
+    struct tun *st=sst;
+    int l;
+
+    if (fds[0].revents&POLLERR) {
+       printf("tun_afterpoll: hup!\n");
     }
-    if (!subnet_match(&st->networks,dest)) {
-       string_t s,d;
-       s=ipaddr_to_string(source);
-       d=ipaddr_to_string(dest);
-       Message(M_WARNING,"%s: incoming packet with bad destination address "
-               "(s=%s,d=%s)\n",st->name,s,d);
-       free(s); free(d);
-       return;
+    if (fds[0].revents&POLLIN) {
+       BUF_ALLOC(st->buff,"tun_afterpoll");
+       buffer_init(st->buff,st->nl.max_start_pad);
+       l=read(st->fd,st->buff->start,st->buff->len-st->nl.max_start_pad);
+       if (l<0) {
+           fatal_perror("tun_afterpoll: read()");
+       }
+       if (l==0) {
+           fatal("tun_afterpoll: read()=0; device gone away?\n");
+       }
+       if (l>0) {
+           st->buff->size=l;
+           st->netlink_to_tunnel(&st->nl,NULL,st->buff);
+           BUF_ASSERT_FREE(st->buff);
+       }
     }
+}
 
-    /* Really we should decrease TTL, check it's above zero, and
-       recalculate header checksum here. If it gets down to zero,
-       generate an ICMP time-exceeded and send the new packet back to
-       the originating tunnel. XXX check buffer usage! */
+static void tun_deliver_to_kernel(void *sst, void *cid,
+                                 struct buffer_if *buf)
+{
+    struct tun *st=sst;
 
-    /* (Basically do full IP packet forwarding stuff. Except that we
-       know any packet passed in here is destined for the local
-       machine; only exception is if it's destined for us.) */
+    BUF_ASSERT_USED(buf);
 
-    if (dest==st->secnet_address) {
-       printf("%s: incoming tunneled packet for secnet!\n",st->name);
-       return;
-    }
+    /* No error checking, because we'd just throw the packet away anyway */
+    write(st->fd,buf->start,buf->size);
+    BUF_FREE(buf);
+}
 
-    /* Now spit the packet at userv-ipif: SLIP start marker, then
-       bytestuff the packet, then SLIP end marker */
-    /* XXX crunchy bytestuff code */
-    j=0;
-    txbuf[j++]=SLIP_END;
-    for (i=buf->start; i<(buf->start+buf->size); i++) {
-       switch (*i) {
-       case SLIP_END:
-           txbuf[j++]=SLIP_ESC;
-           txbuf[j++]=SLIP_ESCEND;
-           break;
-       case SLIP_ESC:
-           txbuf[j++]=SLIP_ESC;
-           txbuf[j++]=SLIP_ESCESC;
-           break;
-       default:
-           txbuf[j++]=*i;
-           break;
+static void tun_phase_hook(void *sst, uint32_t newphase)
+{
+    struct tun *st=sst;
+    string_t hostaddr,secnetaddr;
+    uint8_t mtu[6];
+    string_t network,mask;
+    struct netlink_client *c;
+    int i;
+
+    /* All the networks we'll be using have been registered. Invoke ifconfig
+       to set the TUN device's address, and route to add routes to all
+       our networks. */
+
+    hostaddr=ipaddr_to_string(st->nl.local_address);
+    secnetaddr=ipaddr_to_string(st->nl.secnet_address);
+    snprintf(mtu,6,"%d",st->nl.mtu);
+    mtu[5]=0;
+
+    sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name,
+           hostaddr,"netmask","255.255.255.255","-broadcast",
+           "pointopoint",secnetaddr,"mtu",mtu,"up",(char *)0);
+
+    for (c=st->nl.clients; c; c=c->next) {
+       for (i=0; i<c->networks->entries; i++) {
+           network=ipaddr_to_string(c->networks->list[i].prefix);
+           mask=ipaddr_to_string(c->networks->list[i].mask);
+           sys_cmd(st->route_path,"route","add","-net",network,
+                   "netmask",mask,"gw",secnetaddr,(char *)0);
        }
     }
-    txbuf[j++]=SLIP_END;
-    if (write(st->txfd,txbuf,j)<0) {
-       fatal_perror("userv_deliver: write()");
+
+    /* Register for poll() */
+    register_for_poll(st, tun_beforepoll, tun_afterpoll, 1, st->nl.name);
+}
+
+#ifdef HAVE_LINUX_IF_H
+static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context,
+                        list_t *args)
+{
+    struct tun *st;
+    item_t *item;
+    dict_t *dict;
+    struct ifreq ifr;
+
+    st=safe_malloc(sizeof(*st),"tun_apply");
+
+    /* First parameter must be a dict */
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"tun","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    st->netlink_to_tunnel=
+       netlink_init(&st->nl,st,loc,dict,
+                    "netlink-tun",tun_deliver_to_kernel);
+
+    st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->interface_name=dict_read_string(dict,"interface",False,
+                                       "tun-netlink",loc);
+    st->ifconfig_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->route_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+
+    if (!st->device_path) st->device_path="/dev/net/tun";
+    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);
+
+    /* New TUN interface: open the device, then do ioctl TUNSETIFF
+       to set or find out the network interface name. */
+    st->fd=open(st->device_path,O_RDWR);
+    if (st->fd==-1) {
+       fatal_perror("%s: can't open device file %s",st->nl.name,
+                    st->device_path);
+    }
+    memset(&ifr,0,sizeof(ifr));
+    ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets,
+                                           no extra headers */
+    if (st->interface_name)
+       strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ);
+    if (ioctl(st->fd,TUNSETIFF,&ifr)<0) {
+       fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name);
+    }
+    if (!st->interface_name) {
+       st->interface_name=safe_malloc(strlen(ifr.ifr_name)+1,"tun_apply");
+       strcpy(st->interface_name,ifr.ifr_name);
+       Message(M_INFO,"%s: allocated network interface %s\n",st->nl.name,
+               st->interface_name);
     }
 
-    return;
+    add_hook(PHASE_DROPPRIV,tun_phase_hook,st);
+
+    return new_closure(&st->nl.cl);
 }
+#endif /* HAVE_LINUX_IF_H */
 
-static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context,
-                          list_t *args)
+static list_t *tun_old_apply(closure_t *self, struct cloc loc, dict_t *context,
+                            list_t *args)
 {
-    struct userv *st;
+    struct tun *st;
     item_t *item;
     dict_t *dict;
+    bool_t search_for_if;
 
-    st=safe_malloc(sizeof(*st),"userv_apply (netlink)");
-    st->cl.description="userv-netlink";
-    st->cl.type=CL_NETLINK;
-    st->cl.apply=NULL;
-    st->cl.interface=&st->ops;
-    st->ops.st=st;
-    st->ops.regnets=userv_regnets;
-    st->ops.deliver=userv_deliver;
-    st->max_start_pad=0;
-    st->max_end_pad=0;
-    st->rxfd=-1; st->txfd=-1;
-    st->clients=NULL;
+    st=safe_malloc(sizeof(*st),"tun_old_apply");
+
+    Message(M_WARNING,"the tun-old code has never been tested. Please report "
+           "success or failure to steve@greenend.org.uk\n");
 
     /* First parameter must be a dict */
     item=list_elem(args,0);
     if (!item || item->type!=t_dict)
-       cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n");
+       cfgfatal(loc,"tun","parameter must be a dictionary\n");
     
     dict=item->data.dict;
-    st->name=dict_read_string(dict,"name",False,"userv-netlink",loc);
-    st->userv_path=dict_read_string(dict,"userv-path",False,"userv-netlink",
-                                   loc);
-    st->service_user=dict_read_string(dict,"service-user",False,
-                                     "userv-netlink",loc);
-    st->service_name=dict_read_string(dict,"service-name",False,
-                                     "userv-netlink",loc);
-    if (!st->name) st->name="netlink-userv-ipif";
-    if (!st->userv_path) st->userv_path="userv";
-    if (!st->service_user) st->service_user="root";
-    if (!st->service_name) st->service_name="ipif";
-    dict_read_subnet_list(dict, "networks", True, "userv-netlink", loc,
-                         &st->networks);
-    st->local_address=string_to_ipaddr(
-       dict_find_item(dict,"local-address", True, "userv-netlink", loc),
-       "userv-netlink");
-    st->secnet_address=string_to_ipaddr(
-       dict_find_item(dict,"secnet-address", True, "userv-netlink", loc),
-       "userv-netlink");
-    if (!subnet_match(&st->networks,st->local_address)) {
-       cfgfatal(loc,"netlink-userv-ipif","local-address must be in "
-             "local networks\n");
+
+    st->netlink_to_tunnel=
+       netlink_init(&st->nl,st,loc,dict,
+                    "netlink-tun",tun_deliver_to_kernel);
+
+    st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->interface_name=dict_read_string(dict,"interface",False,
+                                       "tun-netlink",loc);
+    search_for_if=dict_read_bool(dict,"interface-search",False,"tun-netlink",
+                                loc,st->device_path==NULL);
+    st->ifconfig_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+    st->route_path=dict_read_string(dict,"device",False,"tun-netlink",loc);
+
+    if (!st->device_path) st->device_path="/dev/tun";
+    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);
+
+    /* Old TUN interface: the network interface name depends on which
+       /dev/tunX file we open. If 'interface-search' is set to true, treat
+       'device' as the prefix and try numbers from 0--255. If it's set
+       to false, treat 'device' as the whole name, and require than an
+       appropriate interface name be specified. */
+    if (search_for_if) {
+       string_t dname;
+       int i;
+
+       if (st->interface_name) {
+           cfgfatal(loc,"tun-old","you may not specify an interface name "
+                    "in interface-search mode\n");
+       }
+       dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply");
+       st->interface_name=safe_malloc(8,"tun_old_apply");
+       
+       for (i=0; i<255; i++) {
+           sprintf(dname,"%s%d",st->device_path,i);
+           if ((st->fd=open(dname,O_RDWR))>0) {
+               sprintf(st->interface_name,"tun%d",i);
+               Message(M_INFO,"%s: allocated network interface %s "
+                       "through %s\n",st->nl.name,st->interface_name,dname);
+               continue;
+           }
+       }
+       if (st->fd==-1) {
+           fatal("%s: unable to open any TUN device (%s...)\n",
+                 st->nl.name,st->device_path);
+       }
+    } else {
+       if (!st->interface_name) {
+           cfgfatal(loc,"tun-old","you must specify an interface name "
+                    "when you explicitly specify a TUN device file\n");
+       }
+       st->fd=open(st->device_path,O_RDWR);
+       if (st->fd==-1) {
+           fatal_perror("%s: unable to open TUN device file %s",
+                        st->nl.name,st->device_path);
+       }
     }
-    st->mtu=dict_read_number(dict, "mtu", False, "userv-netlink", loc, 1000);
-    st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"userv-netlink",loc);
-    BUF_ALLOC(st->buff,"netlink:userv_apply");
 
-    add_hook(PHASE_DROPPRIV,userv_phase_hook,st);
+    add_hook(PHASE_DROPPRIV,tun_phase_hook,st);
 
-    return new_closure(&st->cl);
+    return new_closure(&st->nl.cl);
 }
 
+/* No connection to the kernel at all... */
+
 struct null {
-    closure_t cl;
-    struct netlink_if ops;
+    struct netlink nl;
 };
 
-static void *null_regnets(void *sst, struct subnet_list *nets,
-                         netlink_deliver_fn *deliver, void *dst,
-                         uint32_t max_start_pad, uint32_t max_end_pad)
-{
-    Message(M_DEBUG_CONFIG,"null_regnets: request for %d networks, "
-           "max_start_pad=%d, max_end_pad=%d\n",
-           nets->entries,max_start_pad,max_end_pad);
-    return NULL;
-}
-
 static void null_deliver(void *sst, void *cid, struct buffer_if *buf)
 {
     return;
@@ -442,26 +1095,34 @@ static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context,
                          list_t *args)
 {
     struct null *st;
+    item_t *item;
+    dict_t *dict;
 
-    st=safe_malloc(sizeof(*st),"null_apply (netlink)");
-    st->cl.description="null-netlink";
-    st->cl.type=CL_NETLINK;
-    st->cl.apply=NULL;
-    st->cl.interface=&st->ops;
-    st->ops.st=st;
-    st->ops.regnets=null_regnets;
-    st->ops.deliver=null_deliver;
+    st=safe_malloc(sizeof(*st),"null_apply");
 
-    return new_closure(&st->cl);
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"null-netlink","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    netlink_init(&st->nl,st,loc,dict,"null-netlink",null_deliver);
+
+    return new_closure(&st->nl.cl);
 }
 
 init_module netlink_module;
 void netlink_module(dict_t *dict)
 {
     add_closure(dict,"userv-ipif",userv_apply);
+#ifdef HAVE_LINUX_IF_H
+    add_closure(dict,"tun",tun_apply);
+#endif
+    add_closure(dict,"tun-old",tun_old_apply);
+    add_closure(dict,"null-netlink",null_apply);
 #if 0
+    /* TODO */
     add_closure(dict,"pty-slip",ptyslip_apply);
     add_closure(dict,"slipd",slipd_apply);
 #endif /* 0 */
-    add_closure(dict,"null-netlink",null_apply);
 }
index 0df6e09..b0bb4b3 100644 (file)
--- a/secnet.c
+++ b/secnet.c
@@ -156,7 +156,7 @@ static void setup(dict_t *config)
        fatal("configuration does not include a system/log facility\n");
     }
     log=init_log(l);
-    log->log(log->st,LOG_DEBUG,"setup: logging started");
+    log->log(log->st,LOG_DEBUG,"secnet " VERSION ": logging started");
 
     /* Who are we supposed to run as? */
     userid=dict_read_string(system,"userid",False,"system",loc);
index ce5f9d6..db3bee2 100644 (file)
--- a/secnet.h
+++ b/secnet.h
@@ -141,6 +141,8 @@ extern void Message(uint32_t class, char *message, ...);
 extern string_t ipaddr_to_string(uint32_t addr);
 extern string_t subnet_to_string(struct subnet *sn);
 
+extern int sys_cmd(const char *file, char *argc, ...);
+
 /***** END of utility functions *****/
 
 /***** SCHEDULING support */
@@ -345,16 +347,21 @@ struct transform_if {
 /* Used by netlink to deliver to site, and by site to deliver to netlink.
    cid is the client identifier returned by netlink_regnets_fn */
 typedef void netlink_deliver_fn(void *st, void *cid, struct buffer_if *buf);
+/* site code can tell netlink when outgoing packets will be dropped,
+   so netlink can generate appropriate ICMP */
+typedef void netlink_can_deliver_fn(void *st, void *cid, bool_t can_deliver);
 /* Register for packets from specified networks. Return value is client
    identifier. */
 typedef void *netlink_regnets_fn(void *st, struct subnet_list *networks,
                                 netlink_deliver_fn *deliver, void *dst,
-                                uint32_t max_start_pad, uint32_t max_end_pad);
+                                uint32_t max_start_pad, uint32_t max_end_pad,
+                                string_t client_name);
 
 struct netlink_if {
     void *st;
     netlink_regnets_fn *regnets;
     netlink_deliver_fn *deliver;
+    netlink_can_deliver_fn *set_delivery;
 };
 
 /* DH interface */
diff --git a/site.c b/site.c
index c63ca8b..1a664ce 100644 (file)
--- a/site.c
+++ b/site.c
@@ -106,6 +106,7 @@ struct site {
 /* configuration information */
     string_t localname;
     string_t remotename;
+    string_t tunname; /* localname<->remotename by default */
     string_t address; /* DNS name for bootstrapping, optional */
     int remoteport;
     struct netlink_if *netlink;
@@ -167,8 +168,7 @@ static void slog(struct site *st, uint32_t event, string_t msg, ...)
 
     if (event&st->log_events) {
        vsnprintf(buf,240,msg,ap);
-       st->log->log(st->log->st,0,"%s<->%s: %s",st->localname,st->remotename,
-                    buf);
+       st->log->log(st->log->st,0,"%s: %s",st->tunname,buf);
     }
     va_end(ap);
 }
@@ -699,6 +699,7 @@ static void enter_state_run(struct site *st)
     slog(st,LOG_STATE,"entering state RUN");
     st->state=SITE_RUN;
     st->timeout=0;
+    st->netlink->set_delivery(st->netlink->st,st->netlink_cid,True);
     /* XXX get rid of key setup data */
 }
 
@@ -815,6 +816,7 @@ static void enter_state_wait(struct site *st)
     st->timeout=st->now+st->wait_timeout;
     st->state=SITE_WAIT;
     st->peer_valid=False;
+    st->netlink->set_delivery(st->netlink->st,st->netlink_cid,False);
     BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */
     /* XXX Erase keys etc. */
 }
@@ -1012,8 +1014,13 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
            }
            break;
        case LABEL_MSG5:
-           /* Setup packet: expected only in state SENTMSG4 or RUN */
-           if (st->state!=SITE_SENTMSG4 && st->state!=SITE_RUN) {
+           /* Setup packet: expected only in state SENTMSG4 */
+           /* (may turn up in state RUN if our return MSG6 was lost
+              and the new key has already been activated. In that
+              case we should treat it as an ordinary PING packet. We
+              can't pass it to process_msg5() because the
+              new_transform will now be null. XXX) */
+           if (st->state!=SITE_SENTMSG4) {
                slog(st,LOG_UNEXPECTED,"unexpected MSG5");
            } else if (process_msg5(st,buf,source)) {
                send_msg6(st);
@@ -1112,6 +1119,10 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->log_events=LOG_SECURITY|LOG_ERROR|
        LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SETUP_INIT|LOG_SETUP_TIMEOUT;
 
+    st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5,
+                           "site_apply");
+    sprintf(st->tunname,"%s<->%s",st->localname,st->remotename);
+
     /* The information we expect to see in incoming messages of type 1 */
     st->setupsiglen=strlen(st->remotename)+strlen(st->localname)+8;
     st->setupsig=safe_malloc(st->setupsiglen,"site_apply");
@@ -1142,7 +1153,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->netlink_cid=st->netlink->regnets(st->netlink->st, &st->remotenets,
                                         site_outgoing, st,
                                         st->transform->max_start_pad+(4*4),
-                                        st->transform->max_end_pad);
+                                        st->transform->max_end_pad,
+                                        st->tunname);
 
     st->comm->request_notify(st->comm->st, st, site_incoming);
 
index 4541199..35b3776 100644 (file)
@@ -61,7 +61,7 @@ log logfile("secnet","local2"); # Not yet implemented, goes to stderr
 # userid       who we try to run as after setup
 # pidfile
 system {
-#      userid "steve";
+       userid "steve";
 #      pidfile "/var/run/secnet.pid";
        pidfile "foo.pid";
 };
@@ -99,8 +99,9 @@ system {
 # setup-timeout         wait between retransmits of key setup packets, in ms
 # wait-time             wait between unsuccessful key setup attempts, in ms
 
-netlink userv-ipif {
-       name "userv-ipif"; # Printed in log messages from this netlink
+netlink tun {
+       name "fred"; # Printed in log messages from this netlink
+       interface "fred";
        # userv-path "/usr/bin/userv";
        # service-user "root";
        # service-name "ipif";
index f2a66a6..59027f8 100644 (file)
@@ -95,15 +95,15 @@ system {
 # A buffer for all sites to share, to construct outgoing packets
 buffer sysbuffer(4096,{lockdown=yes;});
 
-netlink userv-ipif {
-       name "userv-ipif"; # Printed in log messages from this netlink
+netlink tun {
+#      name "foo"; # Printed in log messages from this netlink
        # userv-path "/usr/bin/userv";
        # service-user "root";
        # service-name "ipif";
 
        # local networks served by this netlink device
        # incoming tunneled packets for other networks will be discarded
-       networks "192.168.73.74/32";
+       networks "192.168.73.74/32","192.168.73.75/32";
        local-address "192.168.73.74"; # IP address of interface
        secnet-address "192.168.73.75"; # IP address of secnet
        mtu 1400;
diff --git a/util.c b/util.c
index 7e5a39a..1929383 100644 (file)
--- a/util.c
+++ b/util.c
@@ -17,6 +17,8 @@
 #include <unistd.h>
 #include <values.h>
 #include <assert.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include "util.h"
 #include "secnet.h"
 
@@ -283,6 +285,35 @@ string_t subnet_to_string(struct subnet *sn)
     return s;
 }
 
+int sys_cmd(const char *path, char *arg, ...)
+{
+    va_list ap;
+    int rv;
+    pid_t c;
+
+    va_start(ap,arg);
+    c=fork();
+    if (c) {
+       /* Parent -> wait for child */
+       waitpid(c,&rv,0);
+    } else if (c==0) {
+       char *args[100];
+       int i;
+       /* Child -> exec command */
+       args[0]=arg;
+       i=1;
+       while ((args[i++]=va_arg(ap,char *)));
+       execvp(path,args);
+       exit(1);
+    } else {
+       /* Error */
+       fatal_perror("sys_cmd(%s,%s,...)");
+    }
+
+    va_end(ap);
+    return rv;
+}
+
 /* Take a list of log closures and merge them */
 struct loglist {
     struct log_if *l;
@@ -515,19 +546,13 @@ static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context,
     item_t *item;
     dict_t *dict;
     bool_t lockdown=False;
+    uint32_t len=DEFAULT_BUFFER_SIZE;
     
     st=safe_malloc(sizeof(*st),"buffer_apply");
     st->cl.description="buffer";
     st->cl.type=CL_BUFFER;
     st->cl.apply=NULL;
     st->cl.interface=&st->ops;
-    st->ops.free=True;
-    st->ops.owner=NULL;
-    st->ops.flags=0;
-    st->ops.loc=loc;
-    st->ops.size=0;
-    st->ops.len=DEFAULT_BUFFER_SIZE;
-    st->ops.start=NULL;
 
     /* First argument, if present, is buffer length */
     item=list_elem(args,0);
@@ -536,11 +561,11 @@ static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context,
            cfgfatal(st->ops.loc,"buffer","first parameter must be a "
                     "number (buffer size)\n");
        }
-       st->ops.len=item->data.number;
-       if (st->ops.len<MIN_BUFFER_SIZE) {
+       len=item->data.number;
+       if (len<MIN_BUFFER_SIZE) {
            cfgfatal(st->ops.loc,"buffer","ludicrously small buffer size\n");
        }
-       if (st->ops.len>MAX_BUFFER_SIZE) {
+       if (len>MAX_BUFFER_SIZE) {
            cfgfatal(st->ops.loc,"buffer","ludicrously large buffer size\n");
        }
     }
@@ -556,7 +581,7 @@ static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context,
                                False);
     }
 
-    st->ops.base=safe_malloc(st->ops.len,"buffer");
+    buffer_new(&st->ops,len);
     if (lockdown) {
        Message(M_WARNING,"buffer: XXX lockdown\n");
     }