[hamradio-commits] [ampr-ripd] 01/07: Imported Upstream version 1.14

Ana Custura ana.c-guest at moszumanska.debian.org
Fri Sep 23 14:27:08 UTC 2016


This is an automated email from the git hooks/post-receive script.

ana.c-guest pushed a commit to branch master
in repository ampr-ripd.

commit 570b0f8a7b24c94408f7d0486002a00b1e2e9019
Author: Ana C. Custura <ana at netstat.org.uk>
Date:   Fri Sep 23 12:05:35 2016 +0100

    Imported Upstream version 1.14
---
 COPYING      |  340 +++++++++++
 Makefile     |   41 ++
 ampr-ripd.1  |   84 +++
 ampr-ripd.c  | 1884 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ampr-run.sh  |   20 +
 find_pass.sh |   11 +
 manual.txt   |  116 ++++
 7 files changed, 2496 insertions(+)

diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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) year  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/Makefile b/Makefile
new file mode 100644
index 0000000..e461218
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,41 @@
+#
+# Makefile for ampr-ripd
+#
+
+BASEDIR = /usr
+SBINDIR = $(BASEDIR)/sbin
+MANDIR = $(BASEDIR)/man/man1
+SCACHEDIR = /var/lib/ampr-ripd
+
+# no need to run ampr-ripd as root
+OWN = daemon
+GRP = daemon
+
+CC = gcc
+
+#
+# Choose one of the followinf DOPTs if you need debug
+#
+
+# Full debug including Netlink
+#DOPT = -Wall -O2 -D HAVE_DEBUG -D NL_DEBUG
+
+# Full debug
+#DOPT = -Wall -O2 -D HAVE_DEBUG
+
+COPT = -Wall -O2
+LOPT =
+
+ampr-ripd:	ampr-ripd.c
+	$(CC) $(COPT) $(DOPT) $(LOPT) -o ampr-ripd ampr-ripd.c
+
+all:	ampr-ripd
+
+clean:
+	rm -f ampr-ripd
+
+install:	ampr-ripd
+	strip ampr-ripd
+	install -m 755 -o $(OWN) -g $(GRP) -d          $(SCACHEDIR)
+	install -m 755 -o $(OWN) -g $(GRP) ampr-ripd   $(SBINDIR)
+	install -m 644 -o $(OWN) -g $(GRP) ampr-ripd.1 $(MANDIR)
diff --git a/ampr-ripd.1 b/ampr-ripd.1
new file mode 100644
index 0000000..ae5e96d
--- /dev/null
+++ b/ampr-ripd.1
@@ -0,0 +1,84 @@
+.TH ampr-ripd "1" "September 2016" "ampr-ripd" "General Manual Commands"
+.SH NAME
+ampr-ripd \- routing daemon for AMPRNet gateways
+.SH SYNOPSIS
+.B ampr-ripd
+[\-?|\-h] [\-d] [\-v] [\-s] [\-r] [\-i <interface>] [\-a <ip|hostname|subnet>[,<ip|hostname|subnet>...]] [\-p <password>] [\-f <interface>] [\-e <ip>] [\-x <system command>]
+.br
+.SH DESCRIPTION
+AMPRnet is a RIPv4 Listener and route injector daemon used with AMPRnet gateways.
+.IP
+.SH OPTIONS
+.TP
+\-?, \fB\-h\fR
+Usage info
+.TP
+\fB\-d\fR
+Debug mode: no daemonization, verbose output
+.TP
+\fB\-v\fR
+More verbose debug output
+Using this option without debug leaves the console attached
+.TP
+\fB\-s\fR
+Save routes to \fI\,/var/lib/ampr\-ripd/encap.txt\/\fP (encap format),
+if this file exists, it will be loaded on startup regardless
+of this option
+.TP
+\fB\-r\fR
+Use raw socket instead of multicast
+.TP
+\fB\-i\fR <interface>
+Tunnel interface to use, defaults to tunl0
+.TP
+\fB\-t\fR <table>
+Routing table to use, defaults to 'main'
+.TP
+\fB\-a\fR
+<ip>[,<ip>...]    Comma separated list of IPs, hostnames or ampr subnets to be ignored.
+Subnets can be in full network/mask (e.g. 44.182.20.0/24) or encap (e.g. 44.182.20/24) format,
+but MUST match an entry in the RIP broadcast
+If a hostname is used, its IP will be re\-resolved 30 sec after every RIP broadcast.
+The List contains local interface IPs by default
+.TP
+\fB\-m\fR <metric>
+Metric to be used when setting routes.
+This is a numerical value from 0 to 255. Defaults to 0.
+.TP
+\fB\-w\fR <window>
+Sets the TCP window size to a given value (defaults to 840).
+This is needed since IP over AX.25 connections have small TCP window size.
+A value of 0 diables setting the window size (not recommended, default value should be ok)
+.TP
+\fB\-p\fR <password>
+RIPv2 password, defaults to the current valid password. Use only if the password should ever change
+.TP
+\fB\-f\fR <interface>
+Interface for RIP forwarding, defaults to none/disabled
+.TP
+\fB\-e\fR <ip>
+Forward destination IP, defaults to 224.0.0.9 if enabled
+.TP
+\fB\-x\fR <system command>
+Execute this system command after route set/change. If the command includes white spaces, use quotes.
+.TP
+.B Observation
+All routes are created with netlink protocol 44 for easy management
+.IP
+.SH SIGNALS
+.IP
+\- On signal SIG_HUP, ampr\-ripd will delete all set routes, but will remain active,
+so routes will be set again on next received RIPv2 set, and the saved encap file updated.
+.IP
+\- On signal SIG_TERM (exit), ampr\-ripd will delete all set routes and exit.
+.IP
+.SH DEBUG
+With the default debug level, using the \fB\-d\fR option,
+the daemon will stay in the foreground and allow you to find the RIPv2 password if needed
+(see the find_pass.sh shell script). To have full debug output, it has to be compiled with \fB\-D\fR HAVE_DEBUG or by uncommenting the right DOPT line.
+.SH AUTHOR
+Author: Marius Petrescu, YO2LOJ, <marius at yo2loj.ro>
+.SH KNOWN ISSUES
+When using other table than 'main', interogating the routes via netlink does not work properly.
+This means that on any update, the route is deleted and then recreated, even if it is already set correctly.
+This should be no problem since this happen only at startup on encap loading and on route change.
diff --git a/ampr-ripd.c b/ampr-ripd.c
new file mode 100644
index 0000000..4249dd8
--- /dev/null
+++ b/ampr-ripd.c
@@ -0,0 +1,1884 @@
+/*
+ * ampr-ripd.c - AMPR 44net RIPv2 Listner Version 1.14
+ *
+ * Author: Marius Petrescu, YO2LOJ, <marius at yo2loj.ro>
+ *
+ *
+ *
+ * Compile with: gcc -O2 -o ampr-ripd ampr-ripd.c
+ *
+ *
+ * Usage: ampr-ripd [-?|-h] [-d] [-v] [-s] [-r] [-i <interface>] [-t <table>] [-a <ip|hostname|subnet>[,<ip|hostname|subnet>...]] [-p <password>] [-m <metric>] [-w <window>] [-f <interface>] [-e <ip>] [-x <system command>]
+ *
+ * Options:
+ *          -?, -h                Usage info
+ *          -d                    Debug mode: no daemonization, verbose output
+ *          -v                    More verbose debug output
+ *                                Using this option without debug leaves the console attached
+ *          -s                    Save routes to /var/lib/ampr-ripd/encap.txt (encap format),
+ *                                If this file exists, it will be loaded on startup regardless
+ *                                of this option
+ *          -r                    Use raw socket instead of multicast
+ *          -i <interface>        Tunnel interface to use, defaults to 'tunl0'
+ *          -t <table>            Routing table to use, defaults to 'main'
+ *          -a  <ip>[,<ip>...]    Comma separated list of IPs/hostnames or encap style entries to be ignored
+ *                                (max. 10 hostnames or IPs, unlimited encap entries)
+ *                                The list contains local interface IPs by default
+ *          -p <password>         RIPv2 password, defaults to the current valid password
+ *          -m <metric>           Use given route metric to set routes, defaults to 0
+ *          -w <window>           Sets TCP window size to the given value
+ *                                A value of 0 skips window setting. Defaults to 840
+ *          -f <interface>        Interface for RIP forwarding, defaults to none/disabled
+ *          -e <ip>               Forward destination IP, defaults to 224.0.0.9 if enabled
+ *          -x <system command>   Execute this system command after route set/change
+ *
+ *
+ * Observation: All routes are created with protocol set to 44
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Version History
+ * ---------------
+ *    0.9    14.Apr.2013    Alpha release, based on Hessus's rip44d
+ *    1.0     1.Aug.2013    First functional version, no tables, no tcp window setting
+ *    1.1     1.Aug.2013    Fully functional version
+ *    1.2     3.Aug.2013    Added option for using raw sockets instead of multicast
+ *    1.3     7.Aug.2013    Minor bug fix, removed compiler warnings
+ *    1.4     8.Aug.2013    Possible buffer overflow fixed
+ *                          Reject metric 15 packets fixed
+ *    1.5    10.Aug.2013    Corrected a stupid netmask calculation error introduced in v1.4
+ *    1.6    10.Oct.2013    Changed multicast setup procedures to be interface specific (Tnx. Rob, PE1CHL)
+ *    1.7     8.Feb.2014    Added support for dynamic hostnames and ampr subnets in the ignore list
+ *    1.8    11.Feb.2014    Added option for route metric setting
+ *    1.9    13.Feb.2014    Added window size setting option and console detaching on daemon startup
+ *    1.10   14.Feb.2014    Small fixes on option and signal processing (Tnx. Demetre, SV1UY)
+ *                          Use daemon() instead of fork()
+ *                          Option -v without debug keeps the console attached
+ *    1.11   17.Feb.2014    Changed netlink route handling to overwrite/delete only routes written by ampr-ripd
+ *    1.12   16.Nov.2014    Added the execution of a system command after route setting/changing. This is done
+ *                          on startup with encap file present and 30 seconds after RIP update if encap changes
+ *                          (Tnx. Rob, PE1CHL for the idea)
+ *    1.13   20.Nov.2014    Ignore subnets for which the gateway is inside their own subnet
+ *                          Reconstruct forwarded RIP messages to be able to send them even on ampr-gw outages
+ *                          Forwarded RIP messages do not use authentication anymore
+ *                          Forwarded RIP messages are sent 30 seconds after a RIP update, otherwise every 29 seconds
+ *    1.14   21.Sep.2016    Password is included in the daemon. Only need to set should it ever change
+ *                          (OK from Brian Kantor - Tnx.)
+ *                          Added man page courtesy of Ana C. Custura and the DebianHams
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <asm/types.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <net/route.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <time.h>
+#include <ctype.h>
+
+#define AMPR_RIPD_VERSION	"1.14"
+
+#define RTSIZE		1000	/* maximum number of route entries */
+#define EXPTIME		600	/* route expiration in seconds */
+
+#define RTFILE		"/var/lib/ampr-ripd/encap.txt"	/* encap file */
+
+#define RTAB_FILE	"/etc/iproute2/rt_tables"	/* route tables */
+
+#define	BUFFERSIZE	8192
+#define MYIPSIZE	25	/* max number of local interface IPs */
+#define MAXIGNORE	10	/* max number of hosts in the ignore list */
+
+#define FALSE	0
+#define TRUE	1
+
+#define RIP_HDR_LEN		4
+#define RIP_ENTRY_LEN		(2+2+4*4)
+#define RIP_CMD_REQUEST		1
+#define RIP_CMD_RESPONSE	2
+#define RIP_AUTH_PASSWD		2
+#define RIP_AF_INET		2
+
+
+#define RTPROT_AMPR		44
+
+#define PERROR(s)				fprintf(stderr, "%s: %s\n", (s), strerror(errno))
+#define rip_pcmd(cmd)				((cmd==1)?("Request"):((cmd==2)?("Response"):("Unknown")))
+#define NLMSG_TAIL(nmsg)			((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+#define addattr32(n, maxlen, type, data) 	addattr_len(n, maxlen, type, &data, 4)
+#define rta_addattr32(rta, maxlen, type, data)	rta_addattr_len(rta, maxlen, type, &data, 4)
+
+/* uncomment if types not available */
+/*
+typedef unsigned char		uint8_t;
+typedef unsigned short		uint16_t;
+typedef unsigned int		uint32_t;
+typedef signed char		sint8_t;
+typedef signed short		sint16_t;
+typedef signed int		sint32_t;
+
+*/
+
+typedef enum
+{
+    ROUTE_ADD,
+    ROUTE_DEL,
+    ROUTE_GET
+} rt_actions;
+
+typedef struct __attribute__ ((__packed__))
+{
+    uint8_t command;
+    uint8_t version;
+    uint16_t zeros;
+} rip_header;
+
+
+typedef struct __attribute__ ((__packed__))
+{
+    uint16_t af;
+    uint16_t rtag;
+    uint32_t address;
+    uint32_t mask;
+    uint32_t nexthop;
+    uint32_t metric;
+} rip_entry;
+
+typedef struct __attribute__ ((__packed__))
+{
+    uint16_t auth;
+    uint16_t type;
+    uint8_t pass[16];
+} rip_auth;
+
+typedef struct
+{
+    uint32_t address;
+    uint32_t netmask;
+    uint32_t nexthop;
+    time_t timestamp;
+} route_entry;
+
+
+typedef struct
+{
+    rip_header header;
+    rip_entry entries[25];
+} rip_packet;
+
+
+static char *usage_string = "\nAMPR RIPv2 daemon " AMPR_RIPD_VERSION "by Marius, YO2LOJ\n\nUsage: ampr-ripd [-d] [-v] [-s] [-r] [-i <interface>]  [-t <table>] [-a <ip|hostname|subnet>[,<ip|hostname|subnet>...]] [-p <password>] [-m <metric>] [-w <window>] [-f <interface>] [-e <ip>] [-x <system command>]\n";
+
+
+int debug = FALSE;
+int verbose = FALSE;
+int save = FALSE;
+int raw = FALSE;
+char *tunif = "tunl0";
+unsigned int tunidx = 0;
+unsigned int tunaddr;
+char *ilist = NULL;
+uint32_t ignore_ip[MAXIGNORE];
+
+char *passwd = "pLaInTeXtpAsSwD";
+char *table = NULL;
+int nrtable;
+uint32_t rmetric = 0;
+uint32_t rwindow = 840;
+char *fwif = NULL;
+char *fwdest = "224.0.0.9";
+char *syscmd = NULL;
+
+int tunsd;
+int fwsd;
+int seq;
+int updated = FALSE;
+int update_encap = FALSE;
+int dns_ignore_lookup = FALSE;
+int encap_ignore = FALSE;
+
+route_entry routes[RTSIZE];
+
+uint32_t myips[MYIPSIZE];
+
+char *ipv4_htoa(uint32_t ip)
+{
+    static char buf[INET_ADDRSTRLEN];
+    sprintf(buf, "%d.%d.%d.%d", (ip & 0xff000000) >> 24, (ip & 0x00ff0000) >> 16, (ip & 0x0000ff00) >> 8, ip & 0x000000ff);
+    return buf;
+}
+
+char *ipv4_ntoa(uint32_t ip)
+{
+    unsigned int lip = ntohl(ip);
+    return ipv4_htoa(lip);
+}
+
+int32_t ns_resolv(const char *name)
+{
+    struct hostent *host;
+
+    host = gethostbyname(name);
+
+    if (host == NULL)
+    {
+	return 0;
+    }
+
+    if (host->h_addrtype != AF_INET)
+    {
+	return 0;
+    }
+
+    return ((struct in_addr) *((struct in_addr *) host->h_addr_list[0])).s_addr;
+}
+
+void ilist_resolve(void)
+{
+    int i = 0;
+    int j;
+    char buf[255];
+    char *plist = ilist;
+    char *nlist;
+    uint32_t ip;
+
+
+    if (ilist == NULL)
+    {
+	return;
+    }
+
+    do
+    {
+	
+	strncpy(buf, plist, 254);
+
+        nlist = strstr(buf, ",");
+
+	if (nlist != NULL)
+	{
+	    *nlist = 0;
+	}
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Ignoring host: %s", buf);
+#endif
+	ip = ns_resolv(buf);
+
+	if (ip != 0)
+	{
+	    for (j=0; j<i; j++)
+	    {
+		if (ignore_ip[j] == ip)
+		{
+		    /* already in list - clear */
+		    ip = 0;
+		}
+	    }
+	
+	    if (ip != 0)
+	    {
+		ignore_ip[i] = ip;
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, " address: %s\n", ipv4_ntoa(ip));
+#endif
+		if (strcmp(plist, ipv4_ntoa(ip)) != 0)
+		{
+		    dns_ignore_lookup = TRUE;
+		}
+		i++;
+	    }
+	    else
+	    {
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, " - already in list\n");
+#endif
+	    }
+	}
+	else
+	{
+	    if (strstr(buf, "44.") == buf)
+	    {
+		encap_ignore = TRUE;
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, " - ampr entry\n");
+#endif
+	    }
+#ifdef HAVE_DEBUG
+	    else
+	    {
+		if (debug) fprintf(stderr, " - invalid hostname\n");
+	    }
+#endif
+	}
+
+	plist = strstr(plist, ",");
+	if (plist != NULL) plist++;
+
+    } while ((i<MAXIGNORE) && (plist != NULL));
+
+#ifdef HAVE_DEBUG
+    if (debug) 
+    {
+	if (verbose) fprintf(stderr, "Total %d IPs in ignore lookup table.\n", i);
+	if (dns_ignore_lookup) fprintf(stderr, "Hostname usage found in ignore list - will do lookups after RIP update.\n");
+    }
+#endif
+
+    while (i<MAXIGNORE)
+    {
+	ignore_ip[i] = 0;
+	i++;
+    }
+}
+
+uint32_t getip(const char *dev)
+{
+    struct ifreq ifr;
+    int res;
+
+    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sockfd < 0) return 0;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    strcpy(ifr.ifr_name, dev);
+    res = ioctl(sockfd, SIOCGIFADDR, &ifr);
+    close(sockfd);
+    if (res < 0) return 0;
+    return ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
+}
+
+void set_multicast(int sockfd, const char *dev)
+{
+    struct ifreq ifr;
+
+    memset(&ifr, 0, sizeof(struct ifreq));
+    strcpy(ifr.ifr_name, dev);
+
+    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr)< 0)
+    {
+	return;
+    }
+
+    ifr.ifr_flags |= IFF_MULTICAST;
+
+    ioctl(sockfd, SIOCSIFFLAGS, &ifr);
+
+    return;
+}
+
+
+char *ipv4_ntoa_encap(uint32_t lip)
+{
+    static char buf[INET_ADDRSTRLEN];
+    char *p;
+    sprintf(buf, "%d.%d", (lip & 0xff000000) >> 24, (lip & 0x00ff0000) >> 16);
+    if ((((lip & 0x0000ff00) >> 8) != 0) || ((lip & 0x000000ff) != 0))
+    {
+	p = &buf[strlen(buf)];
+	sprintf(p, ".%d", (lip & 0x0000ff00) >> 8);
+	if ((lip & 0x000000ff) != 0)
+	{
+	    p = &buf[strlen(buf)];
+	    sprintf(p, ".%d", lip & 0x000000ff);
+	}
+    }
+    return buf;
+}
+
+char *idx_encap(int idx)
+{
+    static char *buf;
+    uint32_t lip = ntohl(routes[idx].address);
+    buf=ipv4_ntoa_encap(lip);
+    return buf;
+}
+
+void set_rt_table(char *arg)
+{
+    FILE *rtf;
+    char buffer[255];
+    char sbuffer[255];
+    char *p;
+    int i;
+
+    if (NULL == arg)
+    {
+	nrtable =  RT_TABLE_MAIN;
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Using routing table 'main' (%d).\n", nrtable);
+#endif
+    }
+    else if (strcmp("default", arg) == 0)
+    {
+	nrtable =  RT_TABLE_DEFAULT;
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Using routing table 'default' (%d).\n", nrtable);
+#endif
+    }
+    else if (strcmp("main", arg) == 0)
+    {
+	nrtable =  RT_TABLE_MAIN;
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Using routing table 'main' (%d).\n", nrtable);
+#endif
+    }
+    else if (strcmp("local", arg) == 0)
+    {
+	nrtable =  RT_TABLE_LOCAL;
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Using routing table 'local' (%d).\n", nrtable);
+#endif
+    }
+    else
+    {
+	/* check for a number */
+	for (i=0; i<strlen(arg); i++)
+	{
+	    if (!isdigit(arg[i]))
+		break;
+	}
+
+	if (i==strlen(arg)) /* we are all digits */
+	{
+	    if (1 != sscanf(arg, "%d", &nrtable))
+	    {
+		/* fallback */
+		nrtable = RT_TABLE_MAIN;
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Can not find routing table '%s'. Assuming table 'main' (%d)", table, nrtable);
+#endif
+	    }
+#ifdef HAVE_DEBUG
+	    if (debug) fprintf(stderr, "Using routing table (%d).\n", nrtable);
+#endif
+	}
+	else /* we have a table name  */
+	{
+	    rtf = fopen(RTAB_FILE, "r");
+	    if (NULL == rtf)
+	    {
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Can not open routing table file '%s'. Assuming table main (254)\n", RTAB_FILE);
+#endif
+		nrtable = RT_TABLE_MAIN;
+	    }
+
+	    while (fgets(buffer, 255, rtf) != NULL)
+	    {
+		if ((buffer[0]!='#') && (p = strstr(buffer, table)) != NULL)
+		{
+		    if (2 == sscanf(buffer, "%d %s", &nrtable, (char *)&sbuffer))
+		    {
+			if (0 == strcmp(table, sbuffer))
+			{
+#ifdef HAVE_DEBUG
+			    if (debug) fprintf(stderr, "Using routing table '%s' (%d).\n", table, nrtable);
+#endif
+			    return;
+			}
+		    }
+		}
+		p = NULL;
+		continue;
+	    }
+	    nrtable = RT_TABLE_MAIN;
+#ifdef HAVE_DEBUG
+	    if (debug) fprintf(stderr, "Can not find routing table %s. Assuming table 'main' (%d)", table, nrtable);
+#endif
+	    fclose (rtf);
+	}
+    }
+}
+
+void detect_myips(void)
+{
+    int i, j;
+    uint32_t ipaddr;
+
+    struct if_nameindex *names;
+
+    for (i=0; i<MYIPSIZE; i++) myips[i] = 0;
+
+    names = if_nameindex();
+
+    if (NULL == names)
+    {
+	return;
+    }
+
+    i = 0;
+    while ((names[i].if_index != 0) && (names[i].if_name != NULL) && (i<MYIPSIZE))
+    {
+	ipaddr = getip(names[i].if_name);
+
+#ifdef HAVE_DEBUG
+	if (debug && verbose) fprintf(stderr, "Interface detected: %s, IP: %s\n", names[i].if_name, ipv4_ntoa(ipaddr));
+#endif
+
+	if (strcmp(names[i].if_name, tunif) == 0)
+	{
+	    tunidx = names[i].if_index;
+#ifdef HAVE_DEBUG
+	    if (debug && verbose) fprintf(stderr, "Assigned tunnel interface index: %u\n", tunidx);
+#endif
+	}
+
+	/* check if address not already there */
+	for (j=0; j<MYIPSIZE; j++)
+	{
+	    if ((myips[j] == ipaddr) || (0 == myips[j])) break;
+	}
+	if (MYIPSIZE != j) myips[j] = ipaddr;
+
+	i++;
+    }
+
+    if_freenameindex(names);
+
+#ifdef HAVE_DEBUG
+    if (debug && verbose)
+    {
+	fprintf(stderr, "Local IPs:\n");
+        for (i=0; i<MYIPSIZE; i++)
+	{
+	    if (0 == myips[i]) break;
+	    fprintf(stderr, "   %s\n", ipv4_ntoa(myips[i]));
+	}
+    }
+#endif
+}
+
+int check_ignore(uint32_t ip)
+{
+	int i;
+	
+	/* check for a local interface match */
+	for (i=0; i<MYIPSIZE; i++)
+	{
+	    if (0 == myips[i]) break;
+	    if (ip == myips[i]) return TRUE;
+	}
+
+	/* check for a local interface match */
+	for (i=0; i<MAXIGNORE; i++)
+	{
+	    if (0 == ignore_ip[i]) break;
+	    if (ip == ignore_ip[i]) return TRUE;
+	}
+
+	/* valid IP */
+	return FALSE;
+};
+
+int check_ignore_encap(uint32_t ip, uint32_t mask)
+{
+    char *plist = ilist;
+    char *nlist;
+    char buf[255];
+    char encb[INET_ADDRSTRLEN + 3];
+    char nb[INET_ADDRSTRLEN + 3];
+
+    sprintf(encb, "%s/%d", ipv4_ntoa_encap(ntohl(ip)), mask);
+    sprintf(nb, "%s/%d", ipv4_ntoa(ip), mask);
+
+    if (ilist == NULL)
+    {
+	return FALSE;
+    }
+
+    do
+    {
+	strncpy(buf, plist, 254);
+
+        nlist = strstr(buf, ",");
+
+	if (nlist != NULL)
+	{
+	    *nlist = 0;
+	}
+
+	if (strcmp(buf, encb) == 0)
+	{
+	    return TRUE;
+	}
+
+	if (strcmp(buf, nb) == 0)
+	{
+	    return TRUE;
+	}
+
+	plist = strstr(plist, ",");
+	if (plist != NULL) plist++;
+
+    } while (plist != NULL);
+
+    /* valid subnet */
+    return FALSE;
+}
+
+void list_add(uint32_t address, uint32_t netmask, uint32_t nexthop)
+{
+    int i;
+
+    /* find a free entry */
+    for (i=0; i<RTSIZE; i++)
+    {
+	if (0 == routes[i].timestamp)
+	{
+	    routes[i].address = address;
+	    routes[i].netmask = netmask;
+	    routes[i].nexthop = nexthop;
+	    routes[i].timestamp = time(NULL);
+	    break;
+	}
+    }
+
+#ifdef HAVE_DEBUG
+    if (RTSIZE == i)
+    {
+	if (debug) fprintf(stderr, "Can not find an unused route entry.\n");
+    }
+#endif
+}
+
+int list_count(void)
+{
+    int count = 0;
+    int i;
+
+    for (i=0; i<RTSIZE; i++)
+    {
+	if (0 != routes[i].timestamp)
+	{
+	    count++;
+	}
+    }
+    return count;
+}
+
+int list_find(uint32_t address, uint32_t netmask)
+{
+    int i;
+
+    for (i=0; i<RTSIZE; i++)
+    {
+	if ((routes[i].address == address) && (routes[i].netmask == netmask))
+	{
+	    break;
+	}
+    }
+
+    if (RTSIZE == i)
+    {
+	return -1;
+    }
+    else
+    {
+	return i;
+    }
+}
+
+void list_update(uint32_t address, uint32_t netmask, uint32_t nexthop)
+{
+    int entry;
+    entry = list_find(address, netmask);
+    if (-1 != entry)
+    {
+	if (routes[entry].nexthop != nexthop)
+	{
+	    routes[entry].nexthop = nexthop;
+	    updated = TRUE;
+	}
+	routes[entry].timestamp = time(NULL);
+    }
+    else
+    {
+	list_add(address, netmask, nexthop);
+	updated = TRUE;
+    }
+}
+
+void list_remove(int idx)
+{
+	routes[idx].address = 0;
+	routes[idx].netmask = 0;
+	routes[idx].nexthop = 0;
+	routes[idx].timestamp = 0;
+
+}
+
+void list_clear(void)
+{
+    int i;
+    for (i=0; i<RTSIZE; i++)
+    {
+	list_remove(i);
+    }
+}
+
+void save_encap(void)
+{
+	int i;
+	FILE *efd;
+	time_t clock;
+
+	if ((FALSE == updated) || (FALSE == save))
+	{
+#ifdef HAVE_DEBUG
+	    if (debug && verbose) fprintf(stderr, "Saving to encap file not needed.\n");
+#endif
+	}
+	else
+	{
+	    efd = fopen(RTFILE, "w+");
+	    if (NULL == efd)
+	    {
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Can not open encap file for writing: %s\n", RTFILE);
+#endif
+	    }
+	    else
+	    {
+
+		clock = time(NULL);
+
+		fprintf(efd, "#\n");
+		fprintf(efd, "# encap.txt file - saved by ampr-ripd (UTC) %s", asctime(gmtime(&clock)));
+		fprintf(efd, "#\n");
+
+		for (i=0; i<RTSIZE; i++)
+		{
+		    if (0 != routes[i].timestamp)
+		    {
+			fprintf(efd, "route addprivate %s", idx_encap(i));
+			fprintf(efd, "/%d encap ", routes[i].netmask);
+			fprintf(efd, "%s\n", ipv4_ntoa(routes[i].nexthop));
+		    }
+		}
+
+		fprintf(efd, "# --EOF--\n");
+
+		fclose(efd);
+	    }
+	}
+
+	if ((NULL != syscmd) && (TRUE == updated))
+	{
+	    i = system(syscmd);
+	    if ((0 != i) && debug)
+	    {
+		fprintf(stderr, "Error executing \"%s\"\n", syscmd);
+	    }
+	}
+
+	updated = FALSE;
+}
+
+void load_encap(void)
+{
+	int i;
+	int count = 0;
+	FILE *efd;
+	char buffer[255];
+	char *p;
+	uint32_t b1, b2, b3, b4, nr;
+	uint32_t ipaddr;
+	uint32_t netmask;
+	uint32_t nexthop;
+
+	efd = fopen(RTFILE, "r");
+	if (NULL == efd)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Can not open encap file for reading: %s\n", RTFILE);
+#endif
+		return;
+	}
+
+	while (fgets(buffer, 255, efd) != NULL)
+	{
+		if ((buffer[0]!='#') && ((p = strstr(buffer, "addprivate ")) != NULL))
+		{
+		    p = &p[strlen("addprivate ")];
+		    b1 = b2 = b3 = b4 = 0;
+		    netmask = 0;
+		    ipaddr = 0;
+		    nr =sscanf(p, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);
+		    if (nr < 2) continue;
+		    ipaddr = (b1 << 24) | (b2 << 16);
+		    if (nr > 2) ipaddr |= b3 << 8;
+		    if (nr > 3) ipaddr |= b4;
+		    p = strstr(p, "/"); p = &p[1];
+		    if (sscanf(p, "%d", &netmask) != 1) continue;
+		    p = strstr(p, "encap ");
+		    if (p == NULL) continue;
+		    p = &p[strlen("encap ")];
+		    nr =sscanf(p, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);
+		    if (nr < 4) continue;
+		    nexthop = (b1 << 24) | (b2 << 16) | b3 << 8 | b4;
+		
+		    /* find a free entry */
+		    for (i=0; i<RTSIZE; i++)
+		    {
+			if (0 == routes[i].timestamp)
+			{
+			    routes[i].address = htonl(ipaddr);
+			    routes[i].netmask = netmask;
+			    routes[i].nexthop = htonl(nexthop);
+			    routes[i].timestamp = 1; /* expire at first update */
+			    break;
+			}
+		    }
+
+#ifdef HAVE_DEBUG
+		    if (RTSIZE == i)
+		    {
+			if (debug) fprintf(stderr, "Can not find an unused route entry.\n");
+		    }
+#endif
+
+		    count++;
+		}
+	}
+
+#ifdef HAVE_DEBUG
+	if (debug && verbose) fprintf(stderr, "Loaded %d entries from %s\n", count, RTFILE);
+#endif
+
+	fclose(efd);
+
+	if (count) updated = TRUE;
+
+}
+
+int addattr_len(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
+{
+    int len = RTA_LENGTH(alen);
+    struct rtattr *rta;
+
+    if ((NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
+    {
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Max allowed length exceeded during NLMSG assembly.\n");
+#endif
+	return -1;
+    }
+    rta = NLMSG_TAIL(n);
+    rta->rta_type = type;
+    rta->rta_len = len;
+    memcpy(RTA_DATA(rta), data, len);
+    n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+    return 0;
+}
+
+int rta_addattr_len(struct rtattr *rta, int maxlen, int type, const void *data, int alen)
+{
+    struct rtattr *subrta;
+    int len = RTA_LENGTH(alen);
+    if ((RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen)
+    {
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Max allowed length exceeded during sub-RTA assembly.\n");
+#endif
+	return -1;
+    }
+
+    subrta = (struct rtattr *)(((void *)rta) + RTA_ALIGN(rta->rta_len));
+    subrta->rta_type = type;
+    subrta->rta_len = len;
+    memcpy(RTA_DATA(subrta), data, alen);
+    rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+    return 0;
+}
+
+#ifdef HAVE_DEBUG
+#ifdef NL_DEBUG
+void nl_debug(void *msg, int len)
+{
+    struct rtattr *rtattr;
+    struct nlmsghdr *rh;
+    struct rtmsg *rm;
+    int i;
+    unsigned char *c;
+
+    if (debug && verbose)
+    {
+	for (rh = (struct nlmsghdr *)msg; NLMSG_OK(rh, len); rh = NLMSG_NEXT(rh, len))
+	{
+	
+	    if (NLMSG_ERROR == rh->nlmsg_type)
+	    {
+		fprintf(stderr, "NLMSG: error\n");
+	    }
+	    else if (NLMSG_DONE == rh->nlmsg_type)
+	    {
+		fprintf(stderr, "NLMSG: done\n");
+	    }
+	    else
+	    {
+		if ((RTM_NEWROUTE != rh->nlmsg_type) && (RTM_DELROUTE != rh->nlmsg_type) && (RTM_GETROUTE != rh->nlmsg_type))
+		{
+		    fprintf(stderr, "NLMSG: %d\n", rh->nlmsg_type);
+		
+		    for (i=0; i<((struct nlmsghdr *)msg)->nlmsg_len; i++)
+		    {
+			c = (unsigned char *)&msg;
+			fprintf(stderr, "%u ", c[i]);
+		    }
+		    fprintf(stderr, "\n");
+		}
+		else
+		{
+		    if (RTM_NEWROUTE == rh->nlmsg_type)
+		    {
+			c = (unsigned char *)"request new route/route info (24)";
+		    }
+		    else if (RTM_DELROUTE == rh->nlmsg_type)
+		    {
+			c = (unsigned char *)"delete route (25)";
+		    }
+		    else /* RTM_GETROUTE */
+		    {
+			c = (unsigned char *)"get route (26)";
+		    }
+
+		    fprintf(stderr, "NLMSG: %s\n", c);
+		    rm = NLMSG_DATA(rh);
+		    for (rtattr = (struct rtattr *)RTM_RTA(rm); RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len))
+		    {
+			fprintf(stderr, "RTA type: %d (%d bytes): ", rtattr->rta_type, rtattr->rta_len);
+			for(i=0; i<(rtattr->rta_len - sizeof(struct rtattr)); i++)
+			{
+			    c = (unsigned char *)RTA_DATA(rtattr);
+			    fprintf(stderr, "%u ", c[i]);
+			}
+			fprintf(stderr, "\n");
+		    }
+		}
+	    }
+	}
+    }
+}
+#endif
+#endif
+
+uint32_t route_func(rt_actions action, uint32_t address, uint32_t netmask, uint32_t nexthop)
+{
+
+    int nlsd;
+    int len;
+
+    char nlrxbuf[4096];
+    char mxbuf[256];
+
+    struct {
+	struct nlmsghdr hdr;
+	struct rtmsg    rtm;
+	char buf[1024];
+    } req;
+
+    struct rtattr *mxrta = (void *)mxbuf;
+
+    struct sockaddr_nl sa;
+    struct rtattr *rtattr;
+    struct nlmsghdr *rh;
+    struct rtmsg *rm;
+
+    uint32_t result = 0;
+
+    mxrta->rta_type = RTA_METRICS;
+    mxrta->rta_len = RTA_LENGTH(0);
+
+    memset(&req, 0, sizeof(req));
+
+    memset(&sa, 0, sizeof(struct sockaddr_nl));
+    sa.nl_family = AF_NETLINK;
+    sa.nl_pid = getpid();
+    sa.nl_groups = 0;
+
+    req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+    req.hdr.nlmsg_flags = NLM_F_REQUEST;
+    req.hdr.nlmsg_seq = ++seq;
+    req.hdr.nlmsg_pid = getpid();
+    req.rtm.rtm_family = AF_INET;
+    req.rtm.rtm_dst_len = netmask;
+    req.rtm.rtm_protocol = RTPROT_AMPR;
+
+    if (NULL == table)
+    {
+        req.rtm.rtm_table = RT_TABLE_MAIN;
+    }
+    else
+    {
+        req.rtm.rtm_table = nrtable;
+    }
+
+    if (ROUTE_DEL == action)
+    {
+        req.rtm.rtm_scope = RT_SCOPE_NOWHERE;
+        req.rtm.rtm_type = RTN_UNICAST;
+        req.hdr.nlmsg_type = RTM_DELROUTE;
+        req.hdr.nlmsg_flags |= NLM_F_CREATE;
+        result = address;
+    }
+    else if (ROUTE_ADD == action)
+    {
+	req.rtm.rtm_flags |= RTNH_F_ONLINK;
+	req.rtm.rtm_type = RTN_UNICAST;
+	req.hdr.nlmsg_type = RTM_NEWROUTE;
+	req.hdr.nlmsg_flags |= NLM_F_CREATE;
+	result = nexthop;
+    }
+    else
+    {
+	req.hdr.nlmsg_type = RTM_GETROUTE;
+	req.rtm.rtm_scope = RT_SCOPE_UNIVERSE;
+    }
+
+    addattr32(&req.hdr, sizeof(req), RTA_DST, address);
+
+    if (ROUTE_ADD == action)
+    {
+	if (0 != nexthop) addattr32(&req.hdr, sizeof(req), RTA_GATEWAY, nexthop); /* gateway */
+	addattr32(&req.hdr, sizeof(req), RTA_OIF, tunidx); /* dev */
+	if (rmetric>0)
+	{
+	    addattr32(&req.hdr, sizeof(req), RTA_PRIORITY, rmetric); /* metrics */
+	}
+	if (rwindow>0)
+	{
+	    rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, rwindow);
+	    addattr_len(&req.hdr, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
+	}
+    }
+
+    if ((nlsd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
+    {
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Can not open netlink socket.\n");
+#endif
+	return 0;
+    }
+
+    if (bind(nlsd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+    {
+#ifdef HAVE_DEBUG
+        if (debug) fprintf(stderr, "Can not bind to netlink socket.\n");
+#endif
+	return 0;
+    }
+#ifdef HAVE_DEBUG
+#ifdef NL_DEBUG
+    if (debug && verbose) fprintf(stderr, "NL sending request.\n");
+    nl_debug(&req, req.hdr.nlmsg_len);
+#endif
+#endif
+    if (send(nlsd, &req, req.hdr.nlmsg_len, 0) < 0)
+    {
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Can not talk to rtnetlink.\n");
+#endif
+    }
+
+    if ((len = recv(nlsd, nlrxbuf, sizeof(nlrxbuf), MSG_DONTWAIT|MSG_PEEK)) > 0)
+    {
+#ifdef HAVE_DEBUG
+#ifdef NL_DEBUG
+	if (debug && verbose) fprintf(stderr, "NL response received.\n");
+	nl_debug(nlrxbuf, len);
+#endif
+#endif
+	if (ROUTE_GET == action)
+	{
+	    /* parse response for ROUTE_GET */
+	    for (rh = (struct nlmsghdr *)nlrxbuf; NLMSG_OK(rh, len); rh = NLMSG_NEXT(rh, len))
+	    {
+		if (rh->nlmsg_type == 24) /* route info resp */
+		{
+		    rm = NLMSG_DATA(rh);
+		    for (rtattr = (struct rtattr *)RTM_RTA(rm); RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len))
+		    {
+			if (RTA_GATEWAY == rtattr->rta_type)
+			{
+			    result = *((uint32_t *)RTA_DATA(rtattr));
+			}
+		    }
+		}
+		else if (NLMSG_ERROR == rh->nlmsg_type)
+		{
+		    result = 0;
+		}
+	    }
+	}
+    }
+    close(nlsd);
+    return result;
+}
+
+void route_update(uint32_t address, uint32_t netmask, uint32_t nexthop)
+{
+	if (route_func(ROUTE_GET, address, netmask, 0) != nexthop)
+	{
+	    route_func(ROUTE_DEL, address, netmask, 0); /* fails if route does not exist - no problem */
+	    if (route_func(ROUTE_ADD, address, netmask, nexthop) == 0)
+	    {
+#ifdef HAVE_DEBUG
+		if (debug)
+		{
+		    fprintf(stderr, "Failed to set route %s/%d via ", ipv4_ntoa(address), netmask);
+		    fprintf(stderr, "%s on dev %s. ", ipv4_ntoa(nexthop), tunif);
+		}
+#endif
+	    }
+	}
+}
+
+void route_delete_all(void)
+{
+	int i;
+
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Clearing routes (%d).\n", list_count());
+#endif
+
+	for(i=0; i<RTSIZE; i++)
+	{
+		if (0 != routes[i].timestamp)
+		{
+			route_func(ROUTE_DEL, routes[i].address, routes[i].netmask, 0);
+		}
+	}
+
+}
+
+void route_set_all(void)
+{
+	int i;
+	
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Setting routes (%d).\n", list_count());
+#endif
+
+	for(i=0; i<RTSIZE; i++)
+	{
+		if (0 != routes[i].timestamp)
+		{
+			route_update(routes[i].address, routes[i].netmask, routes[i].nexthop);
+		}
+	}
+
+	if ((NULL != syscmd) && updated)
+	{
+	    i = system(syscmd);
+	    if ((0 != i) && debug)
+	    {
+		fprintf(stderr, "Error executing \"%s\"\n", syscmd);
+	    }
+	}
+
+	updated = FALSE;
+}
+
+int process_auth(char *buf, int len)
+{
+	rip_auth *auth = (rip_auth *)buf;
+
+	if (auth->auth != 0xFFFF)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Password auth requested but no password found in first RIPv2 message.\n");
+#endif
+		return -1;
+	}
+	if (ntohs(auth->type) != RIP_AUTH_PASSWD)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Unsupported authentication type %d.\n", ntohs(auth->type));
+#endif
+		return -1;
+	}
+
+	if (strcmp((char *)auth->pass, passwd) != 0)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Invalid password.\n");
+#endif
+		return -1;
+	}
+
+	if (ntohs(auth->type) == RIP_AUTH_PASSWD)
+	{
+		if (debug) fprintf(stderr, "Simple password: %s\n", auth->pass);
+	}
+
+	return 0;
+}
+
+void process_entry(char *buf)
+{
+	rip_entry *rip = (rip_entry *)buf;
+
+	
+	if (ntohs(rip->af) != RIP_AF_INET)
+	{
+#ifdef HAVE_DEBUG
+		if (debug && verbose) fprintf(stderr, "Unsupported address family %d.\n", ntohs(rip->af));
+#endif
+		return;
+	}
+
+	unsigned int mask = 1;
+	unsigned int netmask = 0;
+	int i;
+
+	for (i=0; i<32; i++)
+	{
+	    if (rip->mask & mask)
+	    {
+		netmask++;
+	    }
+	    mask <<= 1;
+	}
+
+#ifdef HAVE_DEBUG
+	if (debug && verbose)
+	{
+		fprintf(stderr, "Entry: address %s/%d ", ipv4_ntoa(rip->address), netmask);
+		fprintf(stderr, "nexthop %s ", ipv4_ntoa(rip->nexthop));
+		fprintf(stderr, "metric %d", ntohl(rip->metric));
+	}
+#endif
+
+	/* drop 44.0.0.1 */
+	if (rip->address == inet_addr("44.0.0.1"))
+	{
+#ifdef HAVE_DEBUG
+	    if (debug && verbose) fprintf(stderr, " - rejected\n");
+#endif
+	    return;
+	}
+
+	/* validate and update the route */
+
+	/* drop routes with gw in their own subnet */
+	if ((rip->address << (32 - netmask)) == (rip->nexthop << (32 - netmask)))
+	{
+#ifdef HAVE_DEBUG
+	    if (debug && verbose) fprintf(stderr, " - rejected\n");
+#endif
+	    return;
+	}
+
+	/* remove if unreachable and in list */
+	if (ntohl(rip->metric) > 14)
+	{
+#ifdef HAVE_DEBUG
+		if (debug && verbose) fprintf(stderr, " - unreacheable");
+#endif
+		if ((i = list_find(rip->address, netmask)) != -1)
+		{
+			route_func(ROUTE_DEL, rip->address, netmask, 0);
+			list_remove(i);
+#ifdef HAVE_DEBUG
+			if (debug && verbose) fprintf(stderr, ", removed from list");
+#endif
+		}
+#ifdef HAVE_DEBUG
+		if (debug && verbose) fprintf(stderr, ".\n");
+#endif
+		return;
+	}
+
+	/* check if in ignore list */
+	if (check_ignore(rip->nexthop) || check_ignore_encap(rip->address, netmask))
+	{
+#ifdef HAVE_DEBUG
+		if (debug && verbose) fprintf(stderr, " - in ignore list, rejected\n");
+#endif
+		return;
+	}
+
+#ifdef HAVE_DEBUG
+	if (debug && verbose) fprintf(stderr, "\n");
+#endif
+
+	/* update routes */
+	route_update(rip->address, netmask, rip->nexthop);
+	list_update(rip->address, netmask, rip->nexthop);
+}
+
+
+int process_message(char *buf, int len)
+{
+	rip_header *hdr;
+
+	if (len < RIP_HDR_LEN + RIP_ENTRY_LEN)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "RIP packet to short: %d bytes", len);
+#endif
+		return -1;
+	}
+	if (len > RIP_HDR_LEN + RIP_ENTRY_LEN * 25)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "RIP packet to long: %d bytes", len);
+#endif
+		return -1;
+	}
+	if ((len - RIP_HDR_LEN)%RIP_ENTRY_LEN != 0)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "RIP invalid packet length: %d bytes", len);
+#endif
+		return -1;
+	}
+
+	/* packet seems plausible, process header */
+
+	hdr = (rip_header *)buf;
+
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "RIP len %d header version %d, Command %d (%s)\n", len, hdr->version, hdr->command, rip_pcmd(hdr->command));
+#endif
+
+	if (hdr->command != RIP_CMD_RESPONSE)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Ignored non-response packet\n");
+#endif
+		return -1;
+	}
+
+	if (hdr->version != 2)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Ignored RIP version %d packet (only accept version 2).\n", hdr->version);
+#endif
+		return -1;
+	}
+
+	if (hdr->zeros)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "Ignored packet: zero bytes are not zero.\n");
+#endif
+		return -1;
+	}
+
+	/* header is valid, process content */
+
+	buf += RIP_HDR_LEN;
+	len -= RIP_HDR_LEN;
+
+	/* check password if defined */
+
+	if (-1 == process_auth(buf, len))
+	{
+		return -1;
+	}
+
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Simple password authentication successful.\n");
+#endif
+	
+	buf += RIP_ENTRY_LEN;
+	len -= RIP_ENTRY_LEN;
+
+	/* simple auth ok */
+
+	if (len == 0)
+	{
+#ifdef HAVE_DEBUG
+		if (debug) fprintf(stderr, "No routing entries in this packet.\n");
+#endif
+		return -1;
+	}
+
+	/* we have some entries */
+
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Processing RIPv2 packet, %d entries ", len/RIP_ENTRY_LEN);
+	if (debug && verbose) fprintf(stderr, "\n");
+#endif
+
+	while (len >= RIP_ENTRY_LEN)
+	{
+		process_entry(buf);
+		buf += RIP_ENTRY_LEN;
+		len -= RIP_ENTRY_LEN;
+	}
+
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "(total %d entries).\n", list_count());
+#endif
+
+	/* schedule a route expire check in 30 sec - we do this only if we have route reception */
+	/* else we will keep the routes because there are no updates sources available!         */
+	update_encap = TRUE;
+	alarm(30);
+
+	return 0;
+}
+
+static void on_term(int sig)
+{
+#ifdef HAVE_DEBUG
+	if (debug && verbose) fprintf(stderr, "SIGTERM/SIGKILL received.\n");
+#endif
+	close(fwsd);
+	close(tunsd);
+	route_delete_all();
+	exit(0); 
+}
+
+static void on_alarm(int sig)
+{
+	int i;
+	int count = 0;
+
+	struct sockaddr_in sin;
+	rip_packet rp;
+	int rip_nr;
+	int route_nr;
+	int size;
+
+	if (TRUE == update_encap)
+	{
+	    update_encap = FALSE;
+
+#ifdef HAVE_DEBUG
+	    if (debug)
+	    {
+		fprintf(stderr, "SIGALRM received.\n");
+		fprintf(stderr, "Checking for expired routes.\n");
+	    }
+#endif
+
+	    /* recheck for dynamic ignore list entries if hostnames are in use */
+	    if (dns_ignore_lookup)
+	    {
+		ilist_resolve();
+	    }
+
+	    /* check route timestamp and remove expired routes */
+	    for(i=0; i<RTSIZE; i++)
+	    {
+		if ((0 != routes[i].timestamp) && ((routes[i].timestamp + EXPTIME) < time(NULL)))
+		{
+			route_func(ROUTE_DEL, routes[i].address, routes[i].netmask, 0);
+			list_remove(i);
+			count++;
+			updated = TRUE;
+		}
+	    }
+
+#ifdef HAVE_DEBUG
+	    if (debug)
+	    {
+		fprintf(stderr, "Routes expired: %d.\n", count);
+		fprintf(stderr, "Saving routes to disk.\n");
+	    }
+#endif
+
+	    save_encap();
+
+#ifdef HAVE_DEBUG
+	    if (debug) fprintf(stderr, "(total %d entries).\n", list_count());
+#endif
+	}
+
+	count = list_count();
+
+	if ((NULL != fwif) && (count > 0))
+	{
+#ifdef HAVE_DEBUG
+	    if (debug) fprintf(stderr, "Sending local RIP update.\n");
+#endif
+	    rip_nr = 0;
+	    route_nr = 0;
+
+	    memset((char *)&sin, 0, sizeof(sin));
+	    sin.sin_family = PF_INET;
+	    sin.sin_addr.s_addr = inet_addr(fwdest); 
+	    sin.sin_port = htons(IPPORT_ROUTESERVER);
+
+	    memset(&rp, 0, sizeof(rip_packet));
+	    rp.header.version = 2;
+	    rp.header.command = RIP_CMD_RESPONSE;
+
+	    while ((rip_nr < count) && (route_nr < RTSIZE))
+	    {
+		size = 0;
+		for (i = 0; i < 25; i++)
+		{
+		    while ((0 == routes[route_nr].address) && (route_nr < RTSIZE)) route_nr++;
+		    if (route_nr == RTSIZE)
+		    {
+			break;
+		    }
+
+		    if  (rip_nr < count)
+		    {
+			rp.entries[i].af = htons(RIP_AF_INET);
+			rp.entries[i].rtag = htons(44);
+			rp.entries[i].address = routes[route_nr].address;
+			rp.entries[i].mask = htonl(0xFFFFFFFFl << (32 - routes[route_nr].netmask));
+			rp.entries[i].nexthop = routes[route_nr].nexthop;
+			rp.entries[i].metric = htonl(2);
+			rip_nr ++;
+			route_nr++;
+			size++;
+		    }
+		    else
+		    {
+			break;
+		    }
+		}
+
+		sendto(fwsd, &rp, sizeof(rip_header) + size * sizeof(rip_entry), 0, (struct sockaddr *)&sin, sizeof(sin));
+
+	    }
+
+	}
+
+	/* resend local RIP data every 29 sec - this will prevent overlapping at 5 min with the AMPR RIP update */
+	alarm(29);
+}
+
+static void on_hup(int sig)
+{
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "SIGHUP received!\n");
+#endif
+	route_delete_all();
+	list_clear();
+	updated = TRUE;
+}
+
+int main(int argc, char **argv)
+{
+	int p;
+
+	struct sockaddr_in sin;
+	struct group_req group;
+	
+	char databuf[BUFFERSIZE];
+	char *pload;
+	int len, plen;
+	int lval;
+
+	while ((p = getopt(argc, argv, "dvsrh?i:a:p:t:m:w:f:e:x:")) != -1)
+	{
+		switch (p)
+		{
+		case 'd':
+			debug = TRUE;
+			break;
+		case 'v':
+			verbose = TRUE;
+			break;
+		case 's':
+			save = TRUE;
+			break;
+		case 'r':
+			raw = TRUE;
+			break;
+		case 'i':
+			tunif = optarg;
+			break;
+		case 'a':
+			ilist = optarg;
+			break;
+		case 'p':
+			passwd = optarg;
+			break;
+		case 't':
+			table = optarg;
+			break;
+		case 'm':
+			if (sscanf(optarg, "%d", &lval)==1)
+			{
+			    rmetric = (uint32_t) lval;
+			}
+			break;
+		case 'w':
+			if (sscanf(optarg, "%d", &lval)==1)
+			{
+			    rwindow = (uint32_t) lval;
+			}
+			break;
+		case 'f':
+			fwif = optarg;
+			break;
+		case 'e':
+			fwdest = optarg;
+			break;
+		case 'x':
+			syscmd = optarg;
+			break;
+		case ':':
+		case 'h':
+		case '?':
+			fprintf(stderr, "%s", usage_string);
+			return 1;
+		}
+	}
+
+	if (debug && verbose)
+	{
+		fprintf(stderr, "Using metric %d for routes.\n", rmetric);
+		fprintf(stderr, "Using TCP window %d for routes.\n", rwindow);
+		if (NULL != syscmd) fprintf(stderr, "Executing system command \"%s\" on encap load/save\n", syscmd);
+#ifdef HAVE_DEBUG
+		if (NULL !=ilist) fprintf(stderr, "Ignore list: %s\n", ilist);
+#endif
+	}
+
+	ilist_resolve();
+
+	set_rt_table(table);
+
+	list_clear();
+	load_encap();
+
+	seq = time(NULL);
+
+#ifdef HAVE_DEBUG
+	if (debug && verbose)
+	{
+		fprintf(stderr, "Max list size: %d entries\n", RTSIZE);
+	}
+#endif
+
+	tunaddr = getip(tunif);
+
+#ifdef HAVE_DEBUG
+	if (debug) fprintf(stderr, "Detected tunnel interface address: %s\n", ipv4_ntoa(tunaddr));
+#endif
+
+	detect_myips();
+
+	route_set_all();
+
+	if (TRUE == raw)
+	{
+	    /* create multicast listen socket on tunnel */
+
+#ifdef HAVE_DEBUG
+	    if (debug) fprintf(stderr, "Creating RIP UDP listening socket.\n");
+#endif
+
+	    if ((tunsd = socket(PF_INET, SOCK_RAW, 4)) < 0)
+	    {
+		PERROR("Raw socket");
+		return 1;
+	    }
+	}
+	else
+	{
+	
+	    /* create multicast listen socket on tunnel */
+
+#ifdef HAVE_DEBUG
+	    if (debug) fprintf(stderr, "Creating RIP UDP listening socket.\n");
+#endif
+
+	    if ((tunsd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
+	    {
+		PERROR("Tunnel socket");
+		return 1;
+	    }
+
+#ifdef HAVE_DEBUG
+	    if (debug && verbose) fprintf(stderr, "Setting up multicast interface.\n");
+#endif
+
+	    int reuse = 1;
+	    if (setsockopt(tunsd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
+	    {
+		PERROR("Tunnel socket: Setting SO_REUSEADDR");
+		close(tunsd);
+		return 1;
+	    }
+
+	    if (setsockopt(tunsd, SOL_SOCKET, SO_BINDTODEVICE, tunif, strlen(tunif)) < 0)
+	    {
+		PERROR("Tunnel socket: Setting SO_BINDTODEVICE");
+		close(tunsd);
+		return 1;
+	    }
+
+	    set_multicast(tunsd, tunif);
+
+	    memset((char *)&sin, 0, sizeof(sin));
+	    sin.sin_family = PF_INET;
+	    sin.sin_addr.s_addr = INADDR_ANY; /* mandatory INADDR_ANY for multicast */
+	    sin.sin_port = htons(IPPORT_ROUTESERVER);
+
+	    if (bind(tunsd, (struct sockaddr *)&sin, sizeof(sin)))
+	    {
+		PERROR("Tunnel socket: Bind");
+		close(tunsd);
+		return 1;
+	    }
+
+	    /* disable loopback */
+	    int loop = 0;
+	    if (setsockopt(tunsd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop, sizeof(loop)) < 0)
+	    {
+		PERROR("Tunnel socket: Disable loopback");
+		close(tunsd);
+		return 1;
+	    }
+
+	    /* join multicast group 224.0.0.9 */
+	    memset((char *)&group, 0, sizeof(group));
+	    memset((char *)&sin, 0, sizeof(sin));
+	    sin.sin_family = AF_INET;
+	    sin.sin_addr.s_addr = inet_addr("224.0.0.9");
+	    memcpy(&group.gr_group, &sin, sizeof(sin));
+	    group.gr_interface = tunidx;
+
+	    if (setsockopt(tunsd, IPPROTO_IP, MCAST_JOIN_GROUP, (char *)&group, sizeof(group)) < 0)
+	    {
+		PERROR("Tunnel socket: join multicast");
+		close(tunsd);
+		return 1;
+	    }
+	}
+
+	if (NULL != fwif)
+	{
+		/* create the forward socket */
+#ifdef HAVE_DEBUG
+		if (debug && verbose) fprintf(stderr, "Setting up forwarding interface.\n");
+#endif
+		if ((fwsd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
+		{
+			PERROR("Forward socket");
+			close(tunsd);
+			return 1;
+		}
+
+		if (setsockopt(fwsd, SOL_SOCKET, SO_BINDTODEVICE, fwif, strlen(fwif)) < 0)
+		{
+			PERROR("Tunnel socket: Setting SO_BINDTODEVICE");
+			close(fwsd);
+			close(tunsd);
+			return 1;
+		}
+
+		memset((char *)&sin, 0, sizeof(sin));
+		sin.sin_family = PF_INET;
+		sin.sin_addr.s_addr = INADDR_ANY;
+		sin.sin_port = htons(IPPORT_ROUTESERVER);
+		
+		if (bind(fwsd, (struct sockaddr *)&sin, sizeof(sin)))
+		{
+			PERROR("Forward socket: Bind");
+			close(fwsd);
+			close(tunsd);
+			return 1;
+		}
+	}
+
+	/* networking up and running */
+
+	if (FALSE == debug)
+	{
+		/* try to become a daemon */
+		
+		if (daemon(0,verbose)<0)
+		{
+		    PERROR("Can not become a daemon");
+		}
+	}
+
+	signal(SIGTERM, on_term);
+	signal(SIGKILL, on_term);
+	signal(SIGHUP, on_hup);
+	signal(SIGALRM, on_alarm);
+
+	alarm(30);
+
+	/* daemon or debug */
+
+	if (debug) fprintf(stderr, "Waiting for RIPv2 broadcasts...\n");
+
+
+	while (1)
+	{
+		if ((len = read(tunsd, databuf, BUFFERSIZE)) < 0)
+		{
+			if (debug) fprintf(stderr, "Socket read error.\n");
+		}
+		else
+		{
+			if (TRUE == raw)
+			{
+			    if (len >= 48 + (RIP_HDR_LEN + RIP_ENTRY_LEN))
+			    {
+				struct iphdr *iph = (struct iphdr *)(databuf + 20);
+				struct udphdr *udh = (struct udphdr *)(databuf + 40);
+			
+				if ((iph->daddr == inet_addr("224.0.0.9")) &&
+				    (iph->saddr == inet_addr("44.0.0.1")) &&
+				    (udh->dest == htons(IPPORT_ROUTESERVER)) &&
+				    (udh->source == htons(IPPORT_ROUTESERVER)))
+				{
+				    pload = &databuf[48];
+				    plen = len - 48;
+				}
+				else
+				{
+				    continue;
+				}
+			    }
+			    else
+			    {
+				continue;
+			    }
+			}
+			else
+			{
+			    pload = databuf;
+			    plen = len;
+			}
+			
+			process_message(pload, plen);
+			
+		}
+	}
+
+	/* we never reach this */
+	return 0; 
+}
diff --git a/ampr-run.sh b/ampr-run.sh
new file mode 100755
index 0000000..db7bd55
--- /dev/null
+++ b/ampr-run.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+#
+# Example of how to run ampr-ripd
+#
+# In this case:
+# - received routes are saved to /var/lib/ampr-ripd/encap.txt
+# - the tunnel interface is called ampr0
+# - you have to run the program in debug mode (-d) to find the password...
+# - the ip 193.0.0.1 is excluded from the list (the public ip, if not directly connected to a interface,
+#   replace with your own IP if you need this or just delete the parameter)
+# - all routes are created using metric 50 (allows for other ampr routes with smaller metric to take precedence,
+#   e.g. routes aquired via other routing protocols, may be dropped if not needed)
+# - all received RIPv2 multicasts are forwarded to interface eth0 as multicasts (drop this if not needed)...
+# - all routes set by ampr-ripd are saved to /var/lib/ampr-ripd/routes (example of external system command)
+#
+# IF YOUR SYSTEM DOES NOT SUPPORT MULTICAST, ADD THE '-r' OPTION
+#
+
+/usr/sbin/ampr-ripd -s -i ampr0 -m 50 -a 193.0.0.1 -f eth0 -x "ip route | grep 'proto 44' >/var/lib/ampr-ripd/routes"
diff --git a/find_pass.sh b/find_pass.sh
new file mode 100755
index 0000000..6488cb6
--- /dev/null
+++ b/find_pass.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+#
+# Running ampr-ripd without any options except debug and the interface name
+# allows you to find your RIPv2 password if it ever changes from the default.
+# You need to adapt the interface name to your setup.
+#
+# IF YOUR SYSTEM DOES NOT SUPPORT MULTICAST ADD THE '-r' OPTION
+#
+
+./ampr-ripd -d -i ampr0
diff --git a/manual.txt b/manual.txt
new file mode 100644
index 0000000..7222308
--- /dev/null
+++ b/manual.txt
@@ -0,0 +1,116 @@
+ ***************************************************************
+ *                                                             *
+ *  ampr-ripd.c - AMPR 44net RIPv2 Listner and Route Injector  *
+ *                                                             *
+ ***************************************************************
+
+ Author: Marius Petrescu, YO2LOJ, <marius at yo2loj.ro>
+
+
+ Usage: ampr-ripd [-?|-h] [-d] [-v] [-s] [-r] [-i <interface>] [-a <ip|hostname|subnet>[,<ip|hostname|subnet>...]] [-p <password>] [-f <interface>] [-e <ip>] [-x <system command>]
+
+ Options:
+          -?, -h                Usage info
+          -d                    Debug mode: no daemonization, verbose output
+          -v                    More verbose debug output
+                                Using this option without debug leaves the console attached
+          -s                    Save routes to /var/lib/ampr-ripd/encap.txt (encap format),
+                                if this file exists, it will be loaded on startup regardless
+                                of this option
+          -r                    Use raw socket instead of multicast
+          -i <interface>        Tunnel interface to use, defaults to tunl0
+          -t <table>            Routing table to use, defaults to 'main'
+          -a  <ip>[,<ip>...]    Comma separated list of IPs, hostnames or ampr subnets to be ignored.
+                                Subnets can be in full network/mask (e.g. 44.182.20.0/24) or encap (e.g. 44.182.20/24) format,
+                                but MUST match an entry in the RIP broadcast
+                                If a hostname is used, its IP will be re-resolved 30 sec after every RIP broadcast.
+                                The List contains local interface IPs by default
+          -m <metric>           Metric to be used when setting routes.
+                                This is a numerical value from 0 to 255. Defaults to 0.
+          -w <window>           Sets the TCP window size to a given value (defaults to 840).
+                                This is needed since IP over AX.25 connections have small TCP window size.
+                                A value of 0 diables setting the window size (not recommended, default value should be ok)
+          -p <password>         RIPv2 password, defaults to the current valid password
+          -f <interface>        Interface for RIP forwarding, defaults to none/disabled
+          -e <ip>               Forward destination IP, defaults to 224.0.0.9 if enabled
+          -x <system command>   Execute this system command after route set/change. If the command includes white spaces, use quotes.
+
+ Observation: All routes are created with protocol 44 for easy management
+
+
+ Signal handling:
+
+   - On signal SIG_HUP, ampr-ripd will delete all set routes, but will remain active,
+     so routes will be set again on next received RIPv2 set, and the saved encap file updated.
+   - On exit (SIG_TERM), ampr-ripd will delete all set routes and exit.
+
+ Debug:
+
+   - As set up in the makefile, wiyh the default debug level, using the -d option,
+     the daemon will stay in the forground and allow you to find the RIPv2 password
+     (see the find_pass.sh shell script).
+   - To have full debug output, it has to be compiled with -D HAVE_DEBUG  or by
+     uncommenting the right DOPT line.
+
+
+ ************************************************************************
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.            *
+ ************************************************************************
+
+ * Version History
+ * --------------------------------------------------------------------
+ *    0.9     14.Apr.2013      Alpha version, based on Hessus's rip44d
+ *    1.0      1.Aug.2013      First functional version, no tables, no TCP window setting
+ *    1.1      1.Aug.2013      Fully functional version
+ *    1.2      3.Aug.2013      Added option for using raw sockets instead of multicast
+ *    1.3      7.Aug.2013      Minor bug fix, removed compiler warnings
+ *    1.4      8.Aug,2013      Possible buffer overflow fixed
+ *                             Reject metrics 15 packets fixed
+ *    1.5     10.Aug.2013      Corrected a stupid netmask calculation error introduced in v1.4
+ *    1.6     10.Oct.2013      Changed multicast setup procedures to be interface specific (Tnx. Rob, PE1CHL)
+ *    1.7      8.Feb.2014      Added support for dynamic hostnames and ampr subnets in the exclude list
+ *    1.8     11.Feb.2014      Added option for route metric setting
+ *    1.9     13.Feb.2014      Added window size setting option and console detaching on daemon startup
+ *    1.10    14.Feb.2014      Small fixes on option and signal processing (Tnx. Demetre, SV1UY))
+ *                             Using daemon() instead of fork().
+ *                             Option -v without debug keeps console attached
+ *    1.11    17.Feb.2014      Changed netlink route handling to overwrite/delete only routes written by ampr-ripd
+ *    1.12    16.Nov.2014      Added the execution of a system command after route setting/change. This is done
+ *                             on startup with encap file present and 30 seconds after RIP update if encap changes.
+ *                             (Tnx. Rob, PE1CHL for the idea)
+ *    1.13    20.Nov.2014      Ignore subnets for which the gateway is inside their own subnet
+ *                             Reconstruct forwarded RIP messages to be able to send them even on ampr-gw outages
+ *                             Forwarded RIP messages do not use authentication anymore
+ *                             Forwarded RIP messages are sent 30 seconds after RIP update, otherwise every 29 seconds
+ *    1.14    21.Sep.2016      Password is included in the daemon. Only need to set it sould it ever change
+ *                             (OK from Brian Kantor - Tnx.)
+ *                             Added man page courtesy of Ana C. Custura and the DebianHams
+
+ INSTALLATION
+ ------------
+ -Compile with: gcc -O2 -o ampr-ripd ampr-ripd.c or use the make script
+ -Copy the file ampr-ripd to a proper location, e.g. /usr/sbin
+ -Create the folder /var/lib/ampr-ripd
+ -Launch with proper parameters as described above
+Or:
+ -Just do a 'make install' in the source folder,
+
+
+ KNOWN ISSUES
+ ------------
+ - when using other table than 'main', interogating the routes via netlink does not work properly.
+   This means that on any update, the route is deleted and then recreated, even if it is already set correctly.
+   This should be no problem since this happen only at startup on encap loading and on route change.
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/ampr-ripd.git



More information about the pkg-hamradio-commits mailing list