From 4efd681a66c15bc6f81eefc69396669e165e5e0f Mon Sep 17 00:00:00 2001 From: Stephen Early Date: Sun, 23 Sep 2001 16:28:00 +0100 Subject: [PATCH] Import release 0.04 --- COPYING | 340 +++++++++++++++ INSTALL | 9 + Makefile.in | 4 +- TODO | 7 +- config.h.bot | 23 -- config.h.in | 45 +- configure | 320 +++------------ configure.in | 5 +- foo.pid | 1 + linux/if_tun.h | 92 +++++ netlink.c | 1069 +++++++++++++++++++++++++++++++++++++++--------- secnet.c | 2 +- secnet.h | 9 +- site.c | 22 +- testconfig | 7 +- testconfigz | 6 +- util.c | 47 ++- 17 files changed, 1446 insertions(+), 562 deletions(-) create mode 100644 COPYING create mode 100644 foo.pid create mode 100644 linux/if_tun.h diff --git a/COPYING b/COPYING new file mode 100644 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) 19yy + + 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. + + , 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 --- 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 diff --git a/Makefile.in b/Makefile.in index 27b5636..8ec05ce 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 --- 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 diff --git a/config.h.bot b/config.h.bot index adb0c7b..ff6ecda 100644 --- a/config.h.bot +++ b/config.h.bot @@ -6,27 +6,4 @@ #include #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 */ diff --git a/config.h.in b/config.h.in index a3b3cef..5d31bab 100644 --- a/config.h.in +++ b/config.h.in @@ -26,21 +26,6 @@ #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 doesn't define. */ -#undef pid_t - -/* Define to `unsigned' if doesn't define. */ -#undef size_t - /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS @@ -48,14 +33,17 @@ byte first (like Motorola and SPARC, unlike Intel and VAX). */ #undef WORDS_BIGENDIAN +/* Define if you have the 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. */ @@ -64,27 +52,4 @@ #include #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 */ diff --git a/configure b/configure index 7a08124..4153c02 100755 --- 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 +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 <&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 < -#if STDC_HEADERS -#include -#include -#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 < -#if STDC_HEADERS -#include -#include -#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 < -/* 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 < -/* 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 <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 < #include @@ -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 < #include @@ -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 <&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 <&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 <&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 <&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 diff --git a/configure.in b/configure.in index 6c85df9..ff74f20 100644 --- a/configure.in +++ b/configure.in @@ -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 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 index 0000000..fd610f3 --- /dev/null +++ b/linux/if_tun.h @@ -0,0 +1,92 @@ +/* + * Universal TUN/TAP device driver. + * Copyright (C) 1999-2000 Maxim Krasnyansky + * + * 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 */ diff --git a/netlink.c b/netlink.c index 7db3559..0b1ebe4 100644 --- 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 @@ -10,25 +11,35 @@ 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 #include #include #include +#include +#include "config.h" #include "secnet.h" #include "util.h" +#ifdef HAVE_LINUX_IF_H +#include +#include +#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 , 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; ipending_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; inetworks->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; inetworks->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); } diff --git a/secnet.c b/secnet.c index 0df6e09..b0bb4b3 100644 --- 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); diff --git a/secnet.h b/secnet.h index ce5f9d6..db3bee2 100644 --- 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 --- 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); diff --git a/testconfig b/testconfig index 4541199..35b3776 100644 --- a/testconfig +++ b/testconfig @@ -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"; diff --git a/testconfigz b/testconfigz index f2a66a6..59027f8 100644 --- a/testconfigz +++ b/testconfigz @@ -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 --- a/util.c +++ b/util.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #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.lendata.number; + if (lenops.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"); } -- 2.30.2