[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