pax_global_header00006660000000000000000000000064145500370400014510gustar00rootroot0000000000000052 comment=610efbc28a17e17395bc24b1b25b93f73a563d00 lsm-1.0.21/000077500000000000000000000000001455003704000123645ustar00rootroot00000000000000lsm-1.0.21/LICENSE000066400000000000000000000432541455003704000134010ustar00rootroot00000000000000 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 Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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. , 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 Lesser General Public License instead of this License. lsm-1.0.21/Makefile000066400000000000000000000034651455003704000140340ustar00rootroot00000000000000# # (C) 2009-2011 Mika Ilmaranta # # License: GPLv2 # VERSION ?= $(lastword $(shell grep ^Version: foolsm.spec)) PROGS = foolsm PKG = foolsm CC = gcc override CFLAGS += -Wall -O2 -DFOOLSM_VERSION=\"$(VERSION)\" #override CFLAGS += -D NO_PLUGIN_EXPORT #override CFLAGS += -D NO_PLUGIN_EXPORT_MUNIN #override CFLAGS += -D NO_PLUGIN_EXPORT_STATUS PREFIX ?= /usr/local DESTDIR ?= BINDIR ?= $(PREFIX)/sbin ifeq ($(PREFIX), /usr) ETCDIR ?= /etc/foolsm else ETCDIR ?= $(PREFIX)/etc/foolsm endif DOCDIR ?= $(PREFIX)/share/doc/foolsm EXAMPLEDIR ?= $(DOCDIR)/examples SCRIPTDIR ?= $(PREFIX)/libexec/foolsm DOCFILES = README foolsm.conf.sample default_script.sample rsyslog-foolsm.conf.sample SCRIPTS = shorewall_script shorewall6_script default_script group_script override CFLAGS += -D ETCDIR=\"$(ETCDIR)\" override CFLAGS += -D SCRIPTDIR=\"$(SCRIPTDIR)\" .PHONY: all clean distclean tar rpm all: $(PROGS) foolsm: foolsm.o icmp_t.o icmp6_t.o config.o globals.o cksum.o forkexec.o signal_handler.o timecalc.o plugin_export.o save_statuses.o pidfile.o cmdline.o usage.o clean distclean: rm -rf *~ .*~ *.o $(PROGS) debugfiles.list debuglinks.list debugsources.list *.orig tar: distclean tar zcvf ../$(PKG)-$(VERSION).tar.gz \ --transform=s,.,$(PKG)-$(VERSION), \ --show-transformed-name \ --exclude .git \ --exclude .gitignore \ . rpm: tar cp ../$(PKG)-$(VERSION).tar.gz ~/rpmbuild/SOURCES cp $(PKG).spec ~/rpmbuild/SPECS rpmbuild -ba ~/rpmbuild/SPECS/$(PKG).spec install: all install -d $(DESTDIR)$(EXAMPLEDIR) install -d $(DESTDIR)$(SCRIPTDIR) install -d $(DESTDIR)/var/lib/foolsm install -D -m0644 foolsm.conf $(DESTDIR)$(ETCDIR)/foolsm.conf install -D -m0755 foolsm $(DESTDIR)$(BINDIR)/foolsm install -m0644 $(DOCFILES) $(DESTDIR)$(EXAMPLEDIR) install -m0755 $(SCRIPTS) $(DESTDIR)$(SCRIPTDIR) # lsm-1.0.21/README000066400000000000000000000044161455003704000132510ustar00rootroot00000000000000README ====== License: GPLv2 You should be able to find GNU Public License from www.gnu.org. Foolsm is a Link Status Monitor which can be used to monitor for example a Linux router/firewall connectivity and if you happen to have multiple connections it can change routing when an up/down event happens by utilizing external script. When a SIGUSR1 is received current connection states are syslogged. This package is highly influenced by fping and iputils arping. Many thanks for their efforts. TODO ==== - Add "odd" icmp packet handling (may not be necessary) - You can't set source ip for ping packets. It's always autodiscovered. - Could the arping connection targets use only one socket ... - IPv6 support. - Should be able to check ENODEV somehow before first sendto. Now exits with error code 2 after daemon which init-script can't handle. * This is now changed so that ENODEV causes LOG_ERR with each ping but no exit and behave just as the packet was sent. So for ENODEV you should get a down -event just as if there was packet loss. - The source is kind of ugly ... DECISION MAKING =============== As of foolsm v0.27, the detection algorithm works like this: Foolsm keeps track of 1) the result of the most recent pings (up to 100) 2) the number of consecutive lost pings. 3) the number of consecutive received pings. Let A == the number of recently lost pings Let B == the number of consecutive lost pings Let C == the number of consecutive returned pings If connection (or group) is UP AND ( (A >= max_packet_loss) OR (B >= max_successive_pkts_lost) then change the connection (or group) to down. Else If connection (or group) is DOWN AND ( (A <= min_packet_loss) AND (C > min_successive_pkts_rcved) ) then change the connection (or group) to up. Note: Foolsm assumes each connection starts in an UNKNOWN state by default. DEPENDENCIES ============ default_script uses /bin/mail that's the only reason to depend on mailx-package. INSTALLING ========== There is now Makefile target install for those of you not using rpms. I use only rpm based distros so that may not be in sync with foolsm.spec which does all what I need. AUTHORS ======= Mika Ilmaranta See foolsm.spec's changelog section for patch submitters. lsm-1.0.21/cksum.c000066400000000000000000000010411455003704000136460ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #include "cksum.h" int in_cksum(u_short *p, int n) { register u_short answer; register long sum = 0; u_short odd_byte = 0; while(n > 1) { sum += *p++; n -= 2; } /* mop up an odd byte, if necessary */ if(n == 1){ *(u_char*)(&odd_byte) = *(u_char*)p; sum += odd_byte; } sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* ones-complement, truncate */ return(answer); } /* EOF */ lsm-1.0.21/cksum.h000066400000000000000000000002761455003704000136640ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #ifndef __CKSUM_H__ #define __CKSUM_H__ #include int in_cksum(u_short *p, int n); #endif /* EOF */ lsm-1.0.21/cmdline.c000066400000000000000000000016151455003704000141460ustar00rootroot00000000000000/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #include #include #include #include "globals.h" #include "usage.h" #include "cmdline.h" void cmdline_parse(int argc, char *argv[]) { static struct option long_options[] = { { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { "config", 1, 0, 'c' }, { "pidfile", 1, 0, 'p' }, { "no-fork", 0, 0, 'f' }, { 0, 0, 0, 0 }, }; set_prog(argv[0]); for(;;) { int i = getopt_long(argc, argv, "hvc:p:f", long_options, NULL); if(i == -1) break; switch(i) { case 'h': case 'v': usage_and_exit(); case 'c': set_configfile(optarg); break; case 'p': set_pidfile(optarg); break; case 'f': set_nodaemon(1); break; default: usage_and_exit(); } } if(optind < argc) usage_and_exit(); } /* EOF */ lsm-1.0.21/cmdline.h000066400000000000000000000002641455003704000141520ustar00rootroot00000000000000/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #ifndef __CMDLINE_H__ #define __CMDLINE_H__ void cmdline_parse(int argc, char *argv[]); #endif /* EOF */ lsm-1.0.21/config.c000066400000000000000000000676321455003704000140130ustar00rootroot00000000000000/* (C) 2009-2019 Mika Ilmaranta License: GPLv2 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "defs.h" #define DEFAULT_SCRIPT_FILE SCRIPTDIR "/default_script" static CONFIG defaults; static int errors = 0; GLOBAL cfg; static void read_one_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg); static int find_all_configs(char* fn, int mustexist, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg); static void reassign(char **dst, char *src); static void release(char **dst); static int eqcmp(char *str, char *pat); static int check_addrs(CONFIG *cur); static void reassign(char **dst, char *src) { if(*dst) free(*dst); *dst = strdup(src); } static void release(char **dst) { if(*dst) free(*dst); *dst = NULL; } static int eqcmp(char *str, char *pat) { int i; i = strlen(pat); if(!strncmp(str, pat, i) && str[i] == '=') return 0; return 1; } int reload_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) { free_config(first, last, firstg, lastg); init_config(); return(read_config(fn, first, last, firstg, lastg)); } void free_config(CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) { CONFIG *cur, *prev; GROUPS *curg, *prevg; GROUP_MEMBERS *curgm, *prevgm; cur = (*first); while(cur) { if(cur->name && cur->name != defaults.name) release(&cur->name); if(cur->sourceip && cur->sourceip != defaults.sourceip) release(&cur->sourceip); if(cur->srcinfo) freeaddrinfo(cur->srcinfo); if(cur->checkip && cur->checkip != defaults.checkip) release(&cur->checkip); if(cur->dstinfo) freeaddrinfo(cur->dstinfo); if(cur->eventscript && cur->eventscript != defaults.eventscript) release(&cur->eventscript); if(cur->notifyscript && cur->notifyscript != defaults.notifyscript) release(&cur->notifyscript); if(cur->warn_email && cur->warn_email != defaults.warn_email) release(&cur->warn_email); if(cur->device && cur->device != defaults.device) release(&cur->device); if(cur->queue && cur->queue != defaults.queue) release(&cur->queue); if(cur->long_down_email && cur->long_down_email != defaults.long_down_email) release(&cur->long_down_email); if(cur->long_down_notifyscript && cur->long_down_notifyscript != defaults.long_down_notifyscript) release(&cur->long_down_notifyscript); if(cur->long_down_eventscript && cur->long_down_eventscript != defaults.long_down_eventscript) release(&cur->long_down_eventscript); prev = cur; cur = cur->next; free(prev); } *first = NULL; *last = NULL; curg = (*firstg); while(curg) { curgm = curg->fgm; while(curgm) { free(curgm->name); prevgm = curgm; curgm = curgm->next; free(prevgm); } curg->fgm = NULL; curg->lgm = NULL; if(curg->name && curg->name != defaults.name) release(&curg->name); if(curg->eventscript && curg->eventscript != defaults.eventscript) release(&curg->eventscript); if(curg->notifyscript && curg->notifyscript != defaults.notifyscript) release(&curg->notifyscript); if(curg->warn_email && curg->warn_email != defaults.warn_email) release(&curg->warn_email); if(curg->device && curg->device != defaults.device) release(&curg->device); if(curg->queue && curg->queue != defaults.queue) release(&curg->queue); prevg = curg; curg = curg->next; free(prevg); } *firstg = NULL; *lastg = NULL; if(defaults.name) release(&defaults.name); if(defaults.checkip) release(&defaults.checkip); if(defaults.eventscript) release(&defaults.eventscript); if(defaults.notifyscript) release(&defaults.notifyscript); if(defaults.warn_email) release(&defaults.warn_email); if(defaults.sourceip) release(&defaults.sourceip); if(defaults.device) release(&defaults.device); if(defaults.queue) release(&defaults.queue); if(defaults.long_down_email) release(&defaults.long_down_email); if(defaults.long_down_notifyscript) release(&defaults.long_down_notifyscript); if(defaults.long_down_eventscript) release(&defaults.long_down_eventscript); } void init_config(void) { /* zero cfg and defaults */ memset(&cfg, 0, sizeof(cfg)); memset(&defaults, 0, sizeof(defaults)); /* initialize to sane value */ cfg.debug = 8; defaults.name = strdup("defaults"); defaults.checkip = strdup("127.0.0.1"); defaults.eventscript = NULL; defaults.notifyscript = strdup(DEFAULT_SCRIPT_FILE); defaults.max_packet_loss = 15; defaults.max_successive_pkts_lost = 7; defaults.min_packet_loss = 5; defaults.min_successive_pkts_rcvd = 10; defaults.interval_ms = 1000; defaults.timeout_ms = 1000; defaults.warn_email = strdup("root"); defaults.check_arp = 0; defaults.sourceip = NULL; defaults.ttl = 0; /* assume default unknown state for connections unless user has stated otherwise later in config */ defaults.status = UNKNOWN; /* no exec queue by default */ defaults.queue = NULL; defaults.long_down_email = NULL; /* by default don't execute notify script on unkown to up event */ defaults.unknown_up_notify = 0; /* by default no accelerated startup */ defaults.startup_acceleration = 0; /* by default no startup burst */ defaults.startup_burst_pkts = 0; defaults.startup_burst_interval = MIN_PERHOST_INTERVAL; } static int find_all_configs(char* fn, int mustexist, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) { struct dirent **namelist; char dir[PATH_MAX - NAME_MAX - 1], pattern[128], *p, s[PATH_MAX]; int n, i, found; /* Split fn to dir/pattern */ strcpy(dir, fn); if((p = strrchr(dir, '/')) == NULL) { strcpy(dir, "."); strcpy(pattern, fn); } else { *p = 0; strcpy(pattern, p + 1); } /* Find list of all files */ n = scandir(dir, &namelist, 0, alphasort); if (n < 0) { if (mustexist == 0) return(0); syslog(LOG_ERR, "%s: can't read directory \"%s\"", __FUNCTION__, dir); return(-1); } /* See if name matches pattern */ found = 0; for (i = 0; i < n; i++) { if (fnmatch(pattern, namelist[i]->d_name, 0) == 0 && fnmatch("*~", namelist[i]->d_name, 0) != 0) { snprintf(s, PATH_MAX, "%s/%s", dir, namelist[i]->d_name); read_one_config(s, first, last, firstg, lastg); found++; } free(namelist[i]); } free(namelist); /* Fail if no matches found */ if (found == 0) { if (mustexist == 0) return(0); syslog(LOG_ERR, "%s: no config files found for \"%s\"", __FUNCTION__, fn); return(-1); } return(0); } int read_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) { CONFIG *cur = NULL; GROUPS *curg = NULL; GROUP_MEMBERS *curgm = NULL; errors = 0; read_one_config(fn, first, last, firstg, lastg); for(curg = *firstg; curg; curg = curg->next) { for(curgm = curg->fgm; curgm; curgm = curgm->next) { int found = 0; for(cur = *first; cur; cur = cur->next) { if(!strcmp(cur->name, curgm->name)) { curgm->cfg_ptr = cur; found = 1; break; } } if(!found) { syslog(LOG_ERR, "%s: %s: connection group member \"%s\" not found", __FILE__, __FUNCTION__, curgm->name); errors++; } } } /* some parameter sanity checking */ for(cur = *first; cur; cur = cur->next) { if(strlen(cur->checkip) == 0) { syslog(LOG_ERR, "WARNING: connection \"%s\" has no checkip parameter set", cur->name); errors++; } else { if(check_addrs(cur) < 0) { errors++; } } if(cur->max_packet_loss <= cur->min_packet_loss) { syslog(LOG_ERR, "WARNING: connection \"%s\" max_packet_loss (%d) <= min_packet_loss (%d). that would cause flip-flop effect", cur->name, cur->max_packet_loss, cur->min_packet_loss); errors++; } } if(errors) return(-1); return(0); } static void read_one_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) { CONFIG *cur = NULL; GROUPS *curg = NULL; GROUP_MEMBERS *curgm = NULL; FILE *fp; char buf[BUFSIZ]; int mode = 0; int line = 1; if((fp = fopen(fn, "r")) == 0) { syslog(LOG_ERR, "%s: can't open config file \"%s\"", __FUNCTION__, fn); return; } while(fgets(buf, BUFSIZ, fp)) { char *p = NULL; if(*buf && buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; /* strip lf */ if((p = strchr(buf, '#')) != NULL) *p = '\0'; /* strip comment */ while((p = strchr(buf, '\t')) != NULL) *p = ' '; /* tabs -> spaces */ while(*buf == ' ') memmove(buf, buf + 1, strlen(buf)); /* strip leading space */ while((p = strstr(buf, " ")) != NULL) memmove(p, p + 1, strlen(p)); /* strip multi white space */ while((p = strstr(buf, " =")) != NULL) memmove(p, p + 1, strlen(p)); /* strip spaces before = */ while((p = strstr(buf, "= ")) != NULL) memmove(p + 1, p + 2, strlen(p + 1)); /* strip spaces after = */ while(*buf && buf[strlen(buf) - 1] == ' ') buf[strlen(buf) - 1] = '\0'; /* strip tailing space */ if(!*buf) continue; if(mode) { if (!strcmp(buf, "}")) { mode=0; continue; } switch(mode) { case 1: /* defaults */ if(!eqcmp(buf, "name")) reassign(&defaults.name, strchr(buf, '=') + 1); else if(!eqcmp(buf, "checkip")) reassign(&defaults.checkip, strchr(buf, '=') + 1); else if(!eqcmp(buf, "eventscript")) reassign(&defaults.eventscript, strchr(buf, '=') + 1); else if(!eqcmp(buf, "notifyscript")) reassign(&defaults.notifyscript, strchr(buf, '=') + 1); else if(!eqcmp(buf, "unknown_up_notify")) defaults.unknown_up_notify = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "max_packet_loss")) defaults.max_packet_loss = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "max_successive_pkts_lost")) defaults.max_successive_pkts_lost = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "min_packet_loss")) defaults.min_packet_loss = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "min_successive_pkts_rcvd")) defaults.min_successive_pkts_rcvd = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "interval_ms")) defaults.interval_ms = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "timeout_ms")) defaults.timeout_ms = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "warn_email")) reassign(&defaults.warn_email, strchr(buf, '=') + 1); else if(!eqcmp(buf, "check_arp")) defaults.check_arp = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "sourceip")) reassign(&defaults.sourceip, strchr(buf, '=') + 1); else if(!eqcmp(buf, "device")) reassign(&defaults.device, strchr(buf, '=') + 1); else if(!eqcmp(buf, "ttl")) defaults.ttl = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "status")) defaults.status = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "queue")) reassign(&defaults.queue, strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_time")) defaults.long_down_time = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_email")) reassign(&defaults.long_down_email, strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_notifyscript")) reassign(&defaults.long_down_notifyscript, strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_eventscript")) reassign(&defaults.long_down_eventscript, strchr(buf, '=') + 1); else if(!eqcmp(buf, "startup_acceleration")) defaults.startup_acceleration = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "startup_burst_pkts")) defaults.startup_burst_pkts = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "startup_burst_interval")) defaults.startup_burst_interval = atoi(strchr(buf, '=') + 1); else { syslog(LOG_ERR, "%s: %s: unrecognised " "default config option on " "line %d \"%s\"", __FILE__, __FUNCTION__, line, buf); errors++; } break; case 2: /* connection */ if(!cur) { syslog(LOG_ERR, "read_config: cur == NULL"); break; } if(!eqcmp(buf, "name")) cur->name = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "checkip")) cur->checkip = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "eventscript")) cur->eventscript = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "notifyscript")) cur->notifyscript = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "unknown_up_notify")) cur->unknown_up_notify = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "max_packet_loss")) cur->max_packet_loss = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "max_successive_pkts_lost")) cur->max_successive_pkts_lost = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "min_packet_loss")) cur->min_packet_loss = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "min_successive_pkts_rcvd")) cur->min_successive_pkts_rcvd = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "interval_ms")) cur->interval_ms = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "timeout_ms")) cur->timeout_ms = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "warn_email")) cur->warn_email = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "check_arp")) cur->check_arp = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "sourceip")) cur->sourceip = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "device")) cur->device = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "ttl")) cur->ttl = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "status")) cur->status = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "queue")) cur->queue = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_time")) cur->long_down_time = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_email")) cur->long_down_email = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_notifyscript")) cur->long_down_notifyscript = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "long_down_eventscript")) cur->long_down_eventscript = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "startup_acceleration")) cur->startup_acceleration = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "startup_burst_pkts")) cur->startup_burst_pkts = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "startup_burst_interval")) cur->startup_burst_interval = atoi(strchr(buf, '=') + 1); else { syslog(LOG_ERR, "%s: %s: unrecognised connection config option on line %d \"%s\"", __FILE__, __FUNCTION__, line, buf); errors++; } break; case 3: /* group */ if(!eqcmp(buf, "name")) curg->name = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "eventscript")) curg->eventscript = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "notifyscript")) curg->notifyscript = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "unknown_up_notify")) curg->unknown_up_notify = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "warn_email")) curg->warn_email = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "logic")) curg->logic = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "device")) curg->device = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "status")) curg->status = atoi(strchr(buf, '=') + 1); else if(!eqcmp(buf, "queue")) curg->queue = strdup(strchr(buf, '=') + 1); else if(!eqcmp(buf, "member-connection")) { if((curgm = (GROUP_MEMBERS *)malloc(sizeof(GROUP_MEMBERS))) == NULL) { syslog(LOG_ERR, "%s: %s: can't malloc for group member", __FILE__, __FUNCTION__); fclose(fp); return; } curgm->name = strdup(strchr(buf, '=') + 1); curgm->cfg_ptr = NULL; if(curg->lgm) { /* insert as last */ curgm->next = NULL; curgm->prev = curg->lgm; curg->lgm->next = curgm; curg->lgm = curgm; } else { /* empty member list */ curgm->next = NULL; curgm->prev = NULL; curg->fgm = curgm; curg->lgm = curgm; } } else { syslog(LOG_ERR, "%s: %s: unrecognised group config option on line %d \"%s\"", __FILE__, __FUNCTION__, line, buf); errors++; } break; default: syslog(LOG_ERR, "%s: %s: switch(mode) hit default: should never happen. mode was %d", __FILE__, __FUNCTION__, mode); errors++; break; } } else { /* global config */ if(!eqcmp(buf, "debug")) cfg.debug = atoi(strchr(buf, '=') + 1); /* per connection configs */ else if(!strcmp(buf, "defaults {")) mode=1; else if(!strcmp(buf, "connection {")) { mode=2; if((cur = malloc(sizeof(CONFIG))) == NULL) { syslog(LOG_ERR, "%s: %s: can't malloc for config", __FILE__, __FUNCTION__); return; } if(*last) { /* not first */ (*last)->next = cur; cur->prev = *last; cur->next = NULL; *last = cur; } else { *first = cur; *last = cur; cur->prev = NULL; cur->next = NULL; } /* fill in defaults */ if(defaults.name) { cur->name = defaults.name; cur->sourceip = defaults.sourceip; cur->srcinfo = NULL; cur->checkip = defaults.checkip; cur->dstinfo = NULL; cur->eventscript = defaults.eventscript; cur->notifyscript = defaults.notifyscript; cur->unknown_up_notify = defaults.unknown_up_notify; cur->max_packet_loss = defaults.max_packet_loss; cur->max_successive_pkts_lost = defaults.max_successive_pkts_lost; cur->min_packet_loss = defaults.min_packet_loss; cur->min_successive_pkts_rcvd = defaults.min_successive_pkts_rcvd; cur->interval_ms = defaults.interval_ms; cur->timeout_ms = defaults.timeout_ms; cur->warn_email = defaults.warn_email; cur->check_arp = defaults.check_arp; cur->device = defaults.device; cur->ttl = defaults.ttl; cur->status = defaults.status; cur->queue = defaults.queue; cur->long_down_time = defaults.long_down_time; cur->long_down_email = defaults.long_down_email; cur->long_down_notifyscript = defaults.long_down_notifyscript; cur->long_down_eventscript = defaults.long_down_eventscript; cur->startup_acceleration = defaults.startup_acceleration; cur->startup_burst_pkts = defaults.startup_burst_pkts; cur->startup_burst_interval = defaults.startup_burst_interval; } else syslog(LOG_ERR, "%s: %s: defaults not set", __FILE__, __FUNCTION__); } else if(!strcmp(buf, "group {")) { mode = 3; if((curg = (GROUPS *)malloc(sizeof(GROUPS))) == NULL) { syslog(LOG_ERR, "read_config: can't malloc for group"); return; } /* apply sane defaults for group */ curg->name = defaults.name; curg->eventscript = defaults.eventscript; curg->notifyscript = defaults.notifyscript; curg->unknown_up_notify = defaults.unknown_up_notify; curg->warn_email = defaults.warn_email; curg->logic = 0; /* default group logic or */ curg->device = defaults.device; curg->status = defaults.status; curg->queue = defaults.queue; curg->fgm = NULL; curg->lgm = NULL; if(*lastg) { /* not first group */ (*lastg)->next = curg; curg->prev = *lastg; curg->next = NULL; *lastg = curg; } else { *firstg = curg; *lastg = curg; curg->prev = NULL; curg->next = NULL; } } else if(!strncmp(buf, "include ", 8)) { if(find_all_configs(strchr(buf, ' ') + 1, 1, first, last, firstg, lastg) != 0) { syslog(LOG_ERR, "%s: %s: failed to process included config file on line %d \"%s\"", __FILE__, __FUNCTION__, line, strchr(buf, ' ') + 1); errors++; } continue; } else if(!strncmp(buf, "-include ", 9)) { if(find_all_configs(strchr(buf, ' ') + 1, 0, first, last, firstg, lastg) != 0) { syslog(LOG_ERR, "%s: %s: failed to process included config file on line %d \"%s\"", __FILE__, __FUNCTION__, line, strchr(buf, ' ') + 1); errors++; } continue; } else { syslog(LOG_ERR, "%s: %s: unrecognised global config option in file \"%s\" on line %d \"%s\"", __FILE__, __FUNCTION__, fn, line, buf); errors++; } } line++; } if(mode != 0) { syslog(LOG_ERR, "%s: %s: missing closing bracket at the end of config file \"%s\"", __FILE__, __FUNCTION__, fn); errors++; } fclose(fp); } void dump_config(CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) { CONFIG *cur; GROUPS *curg; GROUP_MEMBERS *curgm; syslog(LOG_INFO, "cfg.debug = \"%d\"", cfg.debug); for(cur = *first; cur; cur = cur->next) { syslog(LOG_INFO, "cur->name = \"%s\"", cur->name); syslog(LOG_INFO, "cur->sourceip = \"%s\"", cur->sourceip); #if defined(DEBUG) if(cur->srcinfo) { char sbuf[INET6_ADDRSTRLEN]; syslog(LOG_INFO, "cur->srcinfo = \"%s\"", inet_ntop(cur->srcinfo->ai_family, &cur->srcinfo->ai_addr, sbuf, INET6_ADDRSTRLEN)); } #endif syslog(LOG_INFO, "cur->checkip = \"%s\"", cur->checkip); #if defined(DEBUG) if(cur->dstinfo) { char sbuf[INET6_ADDRSTRLEN]; syslog(LOG_INFO, "cur->dstinfo = \"%s\"", inet_ntop(cur->dstinfo->ai_family, &cur->dstinfo->ai_addr, sbuf, INET6_ADDRSTRLEN)); } #endif syslog(LOG_INFO, "cur->eventscript = \"%s\"", cur->eventscript); syslog(LOG_INFO, "cur->notifyscript = \"%s\"", cur->notifyscript); syslog(LOG_INFO, "cur->unknown_up_notify = \"%d\"", cur->unknown_up_notify); syslog(LOG_INFO, "cur->max_packet_loss = \"%d\"", cur->max_packet_loss); syslog(LOG_INFO, "cur->max_successive_pkts_lost = \"%d\"", cur->max_successive_pkts_lost); syslog(LOG_INFO, "cur->min_packet_loss = \"%d\"", cur->min_packet_loss); syslog(LOG_INFO, "cur->min_successive_pkts_rcvd = \"%d\"", cur->min_successive_pkts_rcvd); syslog(LOG_INFO, "cur->interval_ms = \"%d\"", cur->interval_ms); syslog(LOG_INFO, "cur->timeout_ms = \"%d\"", cur->timeout_ms); syslog(LOG_INFO, "cur->warn_email = \"%s\"", cur->warn_email); syslog(LOG_INFO, "cur->check_arp = \"%d\"", cur->check_arp); syslog(LOG_INFO, "cur->device = \"%s\"", cur->device); syslog(LOG_INFO, "cur->ttl = \"%d\"", cur->ttl); syslog(LOG_INFO, "cur->status = \"%d\"", cur->status); syslog(LOG_INFO, "cur->startup_acceleration = \"%d\"", cur->startup_acceleration); syslog(LOG_INFO, "cur->startup_burst_pkts = \"%d\"", cur->startup_burst_pkts); syslog(LOG_INFO, "cur->startup_burst_interval = \"%d\"", cur->startup_burst_interval); } for(curg = *firstg; curg; curg = curg->next) { syslog(LOG_INFO, "curg->name = \"%s\"", curg->name); syslog(LOG_INFO, "curg->eventscript = \"%s\"", curg->eventscript); syslog(LOG_INFO, "curg->notifyscript = \"%s\"", curg->notifyscript); syslog(LOG_INFO, "curg->unknown_up_notify = \"%d\"", curg->unknown_up_notify); syslog(LOG_INFO, "curg->warn_email = \"%s\"", curg->warn_email); syslog(LOG_INFO, "curg->device = \"%s\"", curg->device); syslog(LOG_INFO, "curg->logic = \"%s\"", curg->logic == 0 ? "OR" : "AND"); for(curgm = curg->fgm; curgm; curgm = curgm->next) { syslog(LOG_INFO, "curgm->name = \"%s\"", curgm->name); } } } static int check_addrs(CONFIG *cur) { struct in6_addr serveraddr; struct addrinfo hints; #if defined(DEBUG) struct addrinfo *rp; #endif int rc; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if((rc = inet_pton(AF_INET, cur->checkip, &serveraddr)) == 1) { /* valid v4 addr */ hints.ai_family = AF_INET; hints.ai_flags |= AI_NUMERICHOST; } else if((rc = inet_pton(AF_INET6, cur->checkip, &serveraddr)) == 1) { /* valid v6 addr */ hints.ai_family = AF_INET6; hints.ai_flags |= AI_NUMERICHOST; } if((rc = getaddrinfo(cur->checkip, "1025", &hints, &(cur->dstinfo))) != 0) { syslog(LOG_ERR, "WARNING: connection \"%s\" checkip is invalid %s, %s", cur->name, cur->checkip, gai_strerror(rc)); return(-1); } #if defined(DEBUG) for(rp = cur->dstinfo; rp; rp = rp->ai_next) { unsigned char *s; char buf[BUFSIZ]; char sbuf[INET6_ADDRSTRLEN]; int i; memset(buf, 0, BUFSIZ); s = (unsigned char *)rp; strcat(buf, "hex dump:"); for(i = 0; i < sizeof(struct addrinfo); i++) sprintf(buf + strlen(buf), " %2x", s[i]); syslog(LOG_INFO, "%s: %s: dst %s", __FILE__, __FUNCTION__, buf); memset(buf, 0, BUFSIZ); s = (unsigned char *)rp; strcat(buf, "dec dump:"); for(i = 0; i < sizeof(struct addrinfo); i++) sprintf(buf + strlen(buf), " %3d", s[i]); syslog(LOG_INFO, "%s: %s: dst %s", __FILE__, __FUNCTION__, buf); syslog(LOG_INFO, "%s: %s: dst %s = %s", __FILE__, __FUNCTION__, cur->checkip, inet_ntop(rp->ai_family, &rp->ai_addr, sbuf, INET6_ADDRSTRLEN)); } #endif if(cur->dstinfo->ai_family == AF_INET6 && cur->check_arp) { syslog(LOG_ERR, "WARNING: connection \"%s\" ipv6 and arping are not compatible", cur->name); return(-1); } if(!cur->sourceip || !*cur->sourceip) return(0); /* sourceip is not mandatory */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if((rc = inet_pton(AF_INET, cur->sourceip, &serveraddr)) == 1) { /* valid v4 addr */ hints.ai_family = AF_INET; hints.ai_flags |= AI_NUMERICHOST; } else if((rc = inet_pton(AF_INET6, cur->sourceip, &serveraddr)) == 1) { /* valid v6 addr */ hints.ai_family = AF_INET6; hints.ai_flags |= AI_NUMERICHOST; } if((rc = getaddrinfo(cur->sourceip, "1025", &hints, &(cur->srcinfo))) != 0) { syslog(LOG_ERR, "WARNING: connection \"%s\" sourceip is invalid %s, %s", cur->name, cur->sourceip, gai_strerror(rc)); return(-1); } #if defined(DEBUG) for(rp = cur->srcinfo; rp; rp = rp->ai_next) { unsigned char *s; char buf[BUFSIZ]; char sbuf[INET6_ADDRSTRLEN]; int i; memset(buf, 0, BUFSIZ); s = (unsigned char *)rp; strcat(buf, "hex dump:"); for(i = 0; i < sizeof(struct addrinfo); i++) sprintf(buf + strlen(buf), " %2x", s[i]); syslog(LOG_INFO, "%s: %s: src %s", __FILE__, __FUNCTION__, buf); memset(buf, 0, BUFSIZ); s = (unsigned char *)rp; strcat(buf, "dec dump:"); for(i = 0; i < sizeof(struct addrinfo); i++) sprintf(buf + strlen(buf), " %3d", s[i]); syslog(LOG_INFO, "%s: %s: src %s", __FILE__, __FUNCTION__, buf); syslog(LOG_INFO, "%s: %s: src %s = %s", __FILE__, __FUNCTION__, cur->checkip, inet_ntop(rp->ai_family, &rp->ai_addr, sbuf, INET6_ADDRSTRLEN)); } #endif if(cur->srcinfo->ai_family != cur->dstinfo->ai_family) { syslog(LOG_ERR, "WARNING: connection \"%s\" sourceip and checkip have unmatching protocol families", cur->name); return(-1); } return(0); } /* EOF */ lsm-1.0.21/config.h000066400000000000000000000033511455003704000140040ustar00rootroot00000000000000/* (C) 2009-2019 Mika Ilmaranta License: GPLv2 */ #ifndef __CONFIG_H__ #define __CONFIG_H__ #include #include #include typedef enum status { DOWN = 0, UP = 1, UNKNOWN = 2, LONG_DOWN = 3 } STATUS; typedef struct config { struct config *prev, *next; char *name; char *sourceip; struct addrinfo *srcinfo; char *checkip; struct addrinfo *dstinfo; char *eventscript; int unknown_up_notify; char *notifyscript; int max_packet_loss; int max_successive_pkts_lost; int min_packet_loss; int min_successive_pkts_rcvd; int interval_ms; int timeout_ms; char *warn_email; int long_down_time; char *long_down_email; char *long_down_notifyscript; char *long_down_eventscript; int check_arp; char *device; int ttl; STATUS status; char *queue; int startup_acceleration; int startup_burst_pkts; int startup_burst_interval; void *data; } CONFIG; typedef struct group_members { struct group_members *prev, *next; char *name; CONFIG *cfg_ptr; } GROUP_MEMBERS; typedef struct groups { struct groups *prev, *next; char *name; char *eventscript; char *notifyscript; int unknown_up_notify; char *warn_email; int logic; /* or = 0, and = 1 */ char *device; STATUS status; char *queue; GROUP_MEMBERS *fgm, *lgm; } GROUPS; typedef struct global { int debug; } GLOBAL; extern GLOBAL cfg; void init_config(void); int read_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg); int reload_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg); void dump_config(CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg); void free_config(CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg); #endif /* EOF */ lsm-1.0.21/debian/000077500000000000000000000000001455003704000136065ustar00rootroot00000000000000lsm-1.0.21/debian/README.Debian000066400000000000000000000003741455003704000156530ustar00rootroot00000000000000foolsm for Debian ---------------- This package only supports systemd. If you don't use systemd, you'll have to invoke foolsm directly or create a sysV script to manage it. -- Roberto Suárez Soto Tue, 18 Jul 2017 08:30:49 +0000 lsm-1.0.21/debian/changelog000066400000000000000000000005061455003704000154610ustar00rootroot00000000000000foolsm (1.0.12-1) unstable; urgency=medium * Update Debian packaging for 1.0.12. -- Roberto Suárez Soto Fri, 20 Sep 2019 09:55:32 +0000 foolsm (1.0.10-1) unstable; urgency=medium * Initial packaging for Debian/Ubuntu. -- Roberto Suárez Soto Tue, 18 Jul 2017 08:30:49 +0000 lsm-1.0.21/debian/compat000066400000000000000000000000021455003704000150040ustar00rootroot000000000000009 lsm-1.0.21/debian/control000066400000000000000000000007651455003704000152210ustar00rootroot00000000000000Source: foolsm Section: net Priority: optional Maintainer: Roberto Suárez Soto Build-Depends: debhelper (>=9), dh-systemd Standards-Version: 3.9.6 Homepage: https://lsm.foobar.fi/ Package: foolsm Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: Foolsm is the Foobar Link Status Monitor Foolsm can ping multiple targets and when up or down event happens it will execute user configured external script so it can be used as poor man's routing protocol. lsm-1.0.21/debian/copyright000066400000000000000000000017141455003704000155440ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: foolsm Source: https://lsm.foobar.fi/ Files: * Copyright: 2009-2017 Mika Ilmaranta License: GPL-2 This package 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 package 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, see . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". lsm-1.0.21/debian/foolsm.init000066400000000000000000000035311455003704000157740ustar00rootroot00000000000000#!/bin/sh # # /etc/init.d/foolsm # # This shellscript takes care of starting and stopping foolsm. # # chkconfig: - 79 31 # description: Foolsm, Link Status Monitor # ### BEGIN INIT INFO # Provides: foolsm # Required-Start: $network $syslog # Required-Stop: # Default-Stop: 0 1 6 # Short-Description: Foolsm - link status monitor # Description: Foolsm is the link status monitor # Foolsm can ping multiple targets and when up or down event happens # it will execute user configured external script so it can be used # as poor man's routing protocol. ### END INIT INFO # Source function library. . /etc/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 CONFIGFILE="/etc/foolsm/foolsm.conf" PIDFILE="/var/run/foolsm.pid" [ -f /etc/sysconfig/foolsm ] && . /etc/sysconfig/foolsm [ -x /usr/sbin/foolsm ] || exit 0 RETVAL=0 start() { echo -n $"Starting foolsm: " daemon --pidfile=${PIDFILE} /usr/sbin/foolsm --config $CONFIGFILE --pidfile $PIDFILE RETVAL=$? /bin/usleep 10000 echo [ $RETVAL = 0 ] && touch /var/lock/subsys/foolsm return $RETVAL } stop() { echo -n $"Stopping foolsm: " killproc /usr/sbin/foolsm RETVAL=$? echo [ $RETVAL = 0 ] && rm -f /var/lock/subsys/foolsm return $RETVAL } restart() { stop start } reload() { echo -n $"Reloading foolsm: " killproc -p ${PIDFILE} /usr/sbin/foolsm -HUP RETVAL=$? echo return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; condrestart) [ -f /var/lock/subsys/foolsm ] && restart ;; status) status -p ${PIDFILE} foolsm RETVAL=$? ;; *) echo "Usage: foolsm {start|stop|restart|condrestart|status}" RETVAL=2 esac exit $RETVAL lsm-1.0.21/debian/foolsm.service000066400000000000000000000005561455003704000164750ustar00rootroot00000000000000[Unit] Description=Foolsm is the link status monitor Documentation=https://lsm.foobar.fi/ Wants=network-online.target After=network-online.target shorewall.service shorewall6.service [Service] Type=simple ExecStart=/usr/sbin/foolsm --config /etc/foolsm/foolsm.conf --no-fork ExecReload=/bin/kill -HUP $MAINPID Restart=on-abort [Install] WantedBy=multi-user.target lsm-1.0.21/debian/rules000066400000000000000000000013061455003704000146630ustar00rootroot00000000000000#!/usr/bin/make -f # See debhelper(7) (uncomment to enable) # output every command that modifies files on the build system. #export DH_VERBOSE = 1 # see FEATURE AREAS in dpkg-buildflags(1) #export DEB_BUILD_MAINT_OPTIONS = hardening=+all # see ENVIRONMENT in dpkg-buildflags(1) # package maintainers to append CFLAGS #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic # package maintainers to append LDFLAGS #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed export PREFIX=/usr %: dh $@ --with=systemd # dh_make generated override targets # This is example for Cmake (See https://bugs.debian.org/641051 ) #override_dh_auto_configure: # dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) lsm-1.0.21/debian/source/000077500000000000000000000000001455003704000151065ustar00rootroot00000000000000lsm-1.0.21/debian/source/format000066400000000000000000000000141455003704000163140ustar00rootroot000000000000003.0 (quilt) lsm-1.0.21/default_script000066400000000000000000000032031455003704000153150ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2009-2015 Mika Ilmaranta # Copyright (C) 2015-2021 Tuomo Soini # # License: GPLv2 # # # default event handling script # STATE=${1} NAME=${2} CHECKIP=${3} DEVICE=${4} WARN_EMAIL=${5} REPLIED=${6} WAITING=${7} TIMEOUT=${8} REPLY_LATE=${9} CONS_RCVD=${10} CONS_WAIT=${11} CONS_MISS=${12} AVG_RTT=${13} SRCIP=${14} PREVSTATE=${15} TIMESTAMP=${16} MIN_RTT=${17} MAX_RTT=${18} if [ -z "${WARN_EMAIL}" ] ; then exit 0 fi DATE=$(date +'%Y-%m-%d %H:%M:%S %Z' --date=@${TIMESTAMP}) HOSTNAME=$(hostname) cat < License: GPLv2 */ #ifndef __DEFS_H__ #define __DEFS_H__ #ifndef TRUE #define TRUE (1) #define FALSE (0) #endif #define MIN_PERHOST_INTERVAL (20000L) /* 20ms in between sends minimum */ #define DEFAULT_SELECT_WAIT (10000L) /* wait at least 10ms for incoming packet */ #define FOLLOWED_PKTS (100) /* THIS ABSOLUTELY CAN'T EXCEED 0xffff (65535 decimal) OR THINGS BREAK */ #define SEQ_LIMITER ((0x10000 / FOLLOWED_PKTS) * FOLLOWED_PKTS) #define min(x, y) ((x)<(y) ? (x) : (y)) #define PLUGIN_EXPORT_DIR "/var/lib/foolsm" #endif /* EOF */ lsm-1.0.21/foolsm.c000066400000000000000000001730131455003704000140340ustar00rootroot00000000000000/* (C) 2009-2019 Mika Ilmaranta License: GPLv2 */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icmp_t.h" #include "icmp6_t.h" #include "config.h" #include "cksum.h" #include "globals.h" #include "signal_handler.h" #include "forkexec.h" #include "timecalc.h" #include "foolsm.h" #ifndef NO_PLUGIN_EXPORT #include "plugin_export.h" #endif #include "save_statuses.h" #include "pidfile.h" #include "cmdline.h" #include "usage.h" typedef struct ping_data { unsigned short id; /* target id */ long ping_count; /* counts up to -c count or 1 */ struct timeval ping_ts; /* time sent */ } PING_DATA; static void update_stats(CONFIG *first); static void dump_statuses(CONFIG *first); static void decide(CONFIG *first); static void groups_decide(GROUPS *firstg); static int wait_for_replies(CONFIG **ctable); static int ping_send(CONFIG *cur); static int ping_rcv(CONFIG *first, char *buf, int len, struct sockaddr_in6 *saddr, unsigned int *slen, long usec, CONFIG **arp); static int event_script_check(const char *path); static int open_arp_sock(CONFIG *cur); static int open_icmp_sock(CONFIG *cur); static int probe_src_ip_addr(CONFIG *cur); static void init_config_data(CONFIG *first, CONFIG *last, CONFIG ***ctable); static void free_config_data(CONFIG *first); #if defined(DEBUG) static void dump_pkt(const void *buf, size_t len); #endif static int num_hosts = 0; /* Main */ int main(int argc, char *argv[]) { TARGET *t = NULL; CONFIG *first = NULL, *last = NULL, *cur; GROUPS *firstg = NULL, *lastg = NULL; CONFIG **ctable = NULL; struct timeval last_sent_time = {0, 0}; int start = 0; openlog("foolsm", LOG_PID, LOG_DAEMON); cmdline_parse(argc, argv); init_config(); if(read_config(get_configfile(), &first, &last, &firstg, &lastg)) { usage_and_exit(); } if(cfg.debug >= 9) syslog(LOG_INFO, "my ident is %d\n", get_ident()); if(!first) { syslog(LOG_ERR, "no targets found in config file"); exit(1); } if(cfg.debug >= 9) dump_config(&first, &last, &firstg, &lastg); /* check pid file */ if(pidfile_open() != 0) exit(1); /* detach from controlling terminal if nodaemon global is not set */ if(get_nodaemon() == 0) { if(daemon(1, 0)) { syslog(LOG_ERR, "daemon failed while trying to detach"); return(1); } } if(pidfile_update() != 0) exit(1); set_ident(getpid() & 0xFFFF); #ifndef NO_PLUGIN_EXPORT plugin_export_init(); #endif init_config_data(first, last, &ctable); signal(SIGINT, signal_handler); signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGHUP, signal_handler); /* Create the handler for child signals. This will clean up any forked child after an event has occured. */ create_sigchld_hdl(); struct timeval last_decision = {0, 0}; /* the main loop */ while(get_cont()) { struct timeval tv = {0, 0}; if(get_reload_cfg()) { save_statuses(first); /* reload config */ free(ctable); free_config_data(first); if(reload_config(get_configfile(), &first, &last, &firstg, &lastg)) { syslog(LOG_ERR, "reload config failed"); exit(2); } init_config_data(first, last, &ctable); restore_statuses(first); set_reload_cfg(0); } for(cur = first; cur; cur = cur->next) { struct timeval current_time = {0, 0}; if(start) while(wait_for_replies(ctable)); if(gettimeofday(¤t_time, NULL) == -1) { syslog(LOG_INFO, "gettimeofday failed \"%s\"", strerror(errno)); sleep(1); continue; } t = cur->data; if(timeval_diff_cmp(¤t_time, &last_sent_time, TIMEVAL_DIFF_CMP_LT, MIN_PERHOST_INTERVAL / 1000000L, MIN_PERHOST_INTERVAL % 1000000L)) continue; if(cur->startup_burst_pkts && t->used <= cur->startup_burst_pkts && !timeval_diff_cmp(¤t_time, &(t->last_send_time), TIMEVAL_DIFF_CMP_LT, (cur->startup_burst_interval * 1000) / 1000000L, (cur->startup_burst_interval * 1000) % 1000000L)); else if(timeval_diff_cmp(¤t_time, &(t->last_send_time), TIMEVAL_DIFF_CMP_LT, (cur->interval_ms * 1000) / 1000000L, (cur->interval_ms * 1000) % 1000000L)) continue; if(cur->check_arp) { open_arp_sock(cur); } else { open_icmp_sock(cur); } if(ping_send(cur)) { if(cfg.debug >= 9) syslog(LOG_INFO, "ping_send failed to %s", cur->name); } else { gettimeofday(&last_sent_time, NULL); start = 1; } } gettimeofday(&tv, NULL); if(timeval_diff_cmp(&tv, &last_decision, TIMEVAL_DIFF_CMP_GT, 1, 0)) { /* make decisions at 1s intervals */ gettimeofday(&last_decision, NULL); update_stats(first); decide(first); dump_statuses(first); groups_decide(firstg); #if defined(DEBUG) exec_queue_dump(); #endif exec_queue_process(); #ifndef NO_PLUGIN_EXPORT plugin_export(first); #endif } } /* while cont */ /* if we wrote pid file then close and remove it */ pidfile_close(); free(ctable); free_config_data(first); free_config(&first, &last, &firstg, &lastg); exec_queue_free(); closelog(); return(0); } static void free_config_data(CONFIG *first) { CONFIG *cur; for(cur = first; cur; cur = cur->next) { TARGET *t; t = cur->data; if(t->sock != -1) close(t->sock); free(t); } } static void update_stats(CONFIG *first) { struct timeval current_time = {0, 0}; CONFIG *cur; gettimeofday(¤t_time, NULL); for(cur = first; cur; cur = cur->next) { TARGET *t; int i, seq, ind; long rtt = 0; t = cur->data; t->timeout = 0; t->replied = 0; t->waiting = 0; t->reply_late = 0; t->consecutive_waiting = 0; t->consecutive_missing = 0; t->consecutive_rcvd = 0; t->min_rtt = 0; t->max_rtt = 0; /* check consecutive pkts */ seq = t->seq % FOLLOWED_PKTS; for(i = (seq - 2); i > (seq - 2) - FOLLOWED_PKTS; i--) { ind = (i >= 0) ? i : i + FOLLOWED_PKTS; if(!t->sentpkts[ind].flags.used) break; if(t->sentpkts[ind].flags.waiting) t->consecutive_waiting++; else break; } for(i = (seq - 2); i > (seq - 2) - FOLLOWED_PKTS; i--) { ind = (i >= 0) ? i : i + FOLLOWED_PKTS; if(!t->sentpkts[ind].flags.used) break; if(t->sentpkts[ind].flags.timeout || t->sentpkts[ind].flags.waiting) t->consecutive_missing++; else break; } for(i = (seq - 2); i > (seq - 2) - FOLLOWED_PKTS; i--) { ind = (i >= 0) ? i : i + FOLLOWED_PKTS; if(!t->sentpkts[ind].flags.used) break; if(t->sentpkts[ind].flags.replied && !t->sentpkts[ind].flags.timeout) t->consecutive_rcvd++; else break; } /* count pkt states */ for(i = 0; i < FOLLOWED_PKTS; i++) { if(!t->sentpkts[i].flags.used) continue; if(timeval_diff_cmp(¤t_time, &t->sentpkts[i].sent_time, TIMEVAL_DIFF_CMP_GT, (cur->timeout_ms * 1000) / 1000000L, (cur->timeout_ms * 1000) % 1000000L) && t->sentpkts[i].flags.waiting) { t->sentpkts[i].flags.timeout = 1; } if(t->sentpkts[i].flags.replied && t->sentpkts[i].flags.timeout) t->reply_late++; if(t->sentpkts[i].flags.replied) { t->replied++; rtt += t->sentpkts[i].rtt; /* count rtt sum in usec from replied pkts rtt which is in usec */ if(t->min_rtt == 0 || t->min_rtt > t->sentpkts[i].rtt) t->min_rtt = t->sentpkts[i].rtt; if(t->max_rtt < t->sentpkts[i].rtt) t->max_rtt = t->sentpkts[i].rtt; } if(t->sentpkts[i].flags.timeout) t->timeout++; if(t->sentpkts[i].flags.waiting) t->waiting++; } /* avg_rtt in usec */ t->avg_rtt = rtt / (t->replied ? t->replied : 1); /* update loss max info */ if(t->timeout > t->timeout_max) t->timeout_max = t->timeout; if(t->consecutive_missing > t->consecutive_missing_max) t->consecutive_missing_max = t->consecutive_missing; if(cfg.debug >= 9) syslog(LOG_INFO, "name = %s, replied = %d, waiting = %d, timeout = %d, late reply = %d, cons rcvd = %d, cons wait = %d, cons miss = %d, min_rtt = %.3f, avg_rtt = %.3f, max_rtt = %.03f, seq = %d, status = %s", cur->name, t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->min_rtt / 1000.0, t->avg_rtt / 1000.0, t->max_rtt / 1000.0, t->seq, get_status_str(t->status)); } } static void dump_statuses(CONFIG *first) { CONFIG *cur; for(cur = first; cur; cur = cur->next) { TARGET *t; t = cur->data; if((t->status == DOWN || t->status == LONG_DOWN) && t->downseq == (t->seq % FOLLOWED_PKTS) && t->seq != t->downseqreported && !t->status_change) syslog(LOG_INFO, "link %s still down", cur->name); /* dump is controlled by SIGUSR1 and then we should show all statuses anyway */ if(get_dump() || t->status_change || ((t->status == DOWN || t->status == LONG_DOWN) && t->downseq == (t->seq % FOLLOWED_PKTS) && t->seq != t->downseqreported && !t->status_change)) { if(cfg.debug >= 6) syslog(LOG_INFO, "name = %s, replied = %d, waiting = %d, timeout = %d, timeout max = %d, late reply = %d, cons rcvd = %d, cons wait = %d, cons miss = %d, cons miss max = %d, min_rtt = %.3f, avg_rtt = %.3f, max_rtt = %.03f, seq = %d, status = %s", cur->name, t->replied, t->waiting, t->timeout, t->timeout_max, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->consecutive_missing_max, t->min_rtt / 1000.0, t->avg_rtt / 1000.0, t->max_rtt / 1000.0, t->seq, get_status_str(t->status)); if(cfg.debug >= 7) { /* 100 should be enough for the comments and such, but I don't care to count */ char buf[FOLLOWED_PKTS + 100]; int i, seq; seq = t->seq % FOLLOWED_PKTS; sprintf(buf, "seq "); for(i = 0; i < FOLLOWED_PKTS; i++) { if(i == seq) strcat(buf, "*"); else strcat(buf, " "); } syslog(LOG_INFO, "%s", buf); sprintf(buf, "used "); for(i = 0; i < FOLLOWED_PKTS; i++) { sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.used); } syslog(LOG_INFO, "%s", buf); sprintf(buf, "wait "); for(i = 0; i < FOLLOWED_PKTS; i++) { sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.waiting); } syslog(LOG_INFO, "%s", buf); sprintf(buf, "replied "); for(i = 0; i < FOLLOWED_PKTS; i++) { sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.replied); } syslog(LOG_INFO, "%s", buf); sprintf(buf, "timeout "); for(i = 0; i < FOLLOWED_PKTS; i++) { sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.timeout); } syslog(LOG_INFO, "%s", buf); sprintf(buf, "error "); for(i = 0; i < FOLLOWED_PKTS; i++) { sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.error); } syslog(LOG_INFO, "%s", buf); if(t->status == UP && t->status_change) { t->timeout_max = 0; t->consecutive_missing_max = 0; } } t->downseqreported = t->seq; } } if(get_dump()) set_dump(0); /* if we just dumped then don't dump next time. flags don't change that frequently */ } static void decide(CONFIG *first) { struct timeval current_time = {0, 0}; CONFIG *cur; gettimeofday(¤t_time, NULL); for(cur = first; cur; cur = cur->next) { TARGET *t; STATUS prevstatus; t = cur->data; /* reset any previous connection status_change state */ t->status_change = 0; prevstatus = t->status; /* up or unknown */ if(t->status == UP || t->status == UNKNOWN) { if(t->timeout >= cur->max_packet_loss || t->consecutive_missing >= cur->max_successive_pkts_lost) { /* change to down state */ t->status_change = 1; t->status = DOWN; #if !defined(NO_PLUGIN_EXPORT) && !defined(NO_PLUGIN_EXPORT_STATUS) plugin_export_status(first); #endif if(cfg.debug >= 8) syslog(LOG_INFO, "link %s down event", cur->name); if(event_script_check(cur->eventscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->eventscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->warn_email ? cur->warn_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), current_time.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); if(cur->queue && *cur->queue) { exec_queue_add(cur->queue, argv, envp); } else { forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if(event_script_check(cur->notifyscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->notifyscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->warn_email ? cur->warn_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), current_time.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } if(gettimeofday(&t->down_timestamp, NULL) == -1) { syslog(LOG_INFO, "gettimeofday failed \"%s\"", strerror(errno)); } t->downseq = t->seq % FOLLOWED_PKTS; t->downseqreported = 0; } } /* has it been down long? */ if(t->status == DOWN && cur->long_down_time) { if(timeval_diff_cmp(¤t_time, &t->down_timestamp, TIMEVAL_DIFF_CMP_GT, cur->long_down_time, 0)) { /* special, LONG_DOWN is considered DOWN thus no status_change */ t->status = LONG_DOWN; if(cfg.debug >= 8) syslog(LOG_INFO, "link %s long down event", cur->name); if(event_script_check(cur->long_down_eventscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->long_down_eventscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->long_down_email ? cur->long_down_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), t->down_timestamp.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); if(cur->queue && *cur->queue) { exec_queue_add(cur->queue, argv, envp); } else { forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if(event_script_check(cur->long_down_notifyscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->long_down_notifyscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->long_down_email ? cur->long_down_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), t->down_timestamp.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } } /* down, long down or unknown */ if(t->status == DOWN || t->status == LONG_DOWN || t->status == UNKNOWN) { if((cur->startup_acceleration && t->consecutive_rcvd >= cur->startup_acceleration && (t->consecutive_rcvd + 1) >= t->used) || (t->timeout <= cur->min_packet_loss && t->consecutive_rcvd >= cur->min_successive_pkts_rcvd)) { t->status_change = 1; t->status = UP; #if !defined(NO_PLUGIN_EXPORT) && !defined(NO_PLUGIN_EXPORT_STATUS) plugin_export_status(first); #endif /* report long_down to up */ if(prevstatus == LONG_DOWN) { if(event_script_check(cur->long_down_eventscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->long_down_eventscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->long_down_email ? cur->long_down_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), current_time.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); if(cur->queue && *cur->queue) { exec_queue_add(cur->queue, argv, envp); } else { forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if(event_script_check(cur->long_down_notifyscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->long_down_notifyscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->long_down_email ? cur->long_down_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), current_time.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } /* change to up state */ if(cfg.debug >= 8) syslog(LOG_INFO, "link %s up event", cur->name); if(event_script_check(cur->eventscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->eventscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->warn_email ? cur->warn_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), current_time.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); if(cur->queue && *cur->queue) { exec_queue_add(cur->queue, argv, envp); } else { forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if((cur->unknown_up_notify || t->status != UNKNOWN) && event_script_check(cur->notifyscript)) { char sbuf[INET6_ADDRSTRLEN]; char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", cur->notifyscript, get_status_str(t->status), cur->name, cur->checkip, cur->device ? cur->device : "", cur->warn_email ? cur->warn_email : "", t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt, cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN), get_status_str(prevstatus), current_time.tv_sec, t->min_rtt, t->max_rtt); envp = exec_queue_envp(); forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } } } } static void groups_decide(GROUPS *firstg){ GROUPS *curg; GROUP_MEMBERS *curgm; TARGET *t; STATUS prevstatus; struct timeval current_time = {0, 0}; gettimeofday(¤t_time, NULL); curg = firstg; while(curg) { prevstatus = curg->status; curg->status = curg->logic; curgm = curg->fgm; while(curgm) { if(!curgm->cfg_ptr) break; t = curgm->cfg_ptr->data; /* if any one group member is in unknown status, group is in unknown status */ if(t->status == UNKNOWN) { curg->status = UNKNOWN; break; } if(!curg->logic) { curg->status |= (t->status == DOWN || t->status == LONG_DOWN) ? DOWN : t->status; } else { curg->status &= (t->status == DOWN || t->status == LONG_DOWN) ? DOWN : t->status; } curgm = curgm->next; } if(curg->status != prevstatus) { if(curg->status == UP) { /* group up event */ if(cfg.debug >= 8) syslog(LOG_INFO, "group %s up event", curg->name); if(event_script_check(curg->eventscript)) { char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", curg->eventscript, get_status_str(curg->status), curg->name, "", curg->device ? curg->device : "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec, 0, 0); envp = exec_queue_envp(); if(curg->queue && *curg->queue) { exec_queue_add(curg->queue, argv, envp); } else { forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if((curg->unknown_up_notify || prevstatus != UNKNOWN) && event_script_check(curg->notifyscript)) { char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", curg->notifyscript, get_status_str(curg->status), curg->name, "", curg->device ? curg->device : "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec, 0, 0); envp = exec_queue_envp(); forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if(curg->status == DOWN) { /* group down event */ if(cfg.debug >= 8) syslog(LOG_INFO, "group %s down event", curg->name); if(event_script_check(curg->eventscript)) { char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", curg->eventscript, get_status_str(curg->status), curg->name, "", curg->device ? curg->device : "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec, 0, 0); envp = exec_queue_envp(); if(curg->queue && *curg->queue) { exec_queue_add(curg->queue, argv, envp); } else { forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } if(event_script_check(curg->notifyscript)) { char **argv; char **envp; argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d %d %d", curg->notifyscript, get_status_str(curg->status), curg->name, "", curg->device ? curg->device : "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec, 0, 0); envp = exec_queue_envp(); forkexec(argv, envp); exec_queue_argv_free(argv); exec_queue_envp_free(envp); } } } curg = curg->next; } } static int wait_for_replies(CONFIG **ctable) { struct ip *ip; int hlen = 0; struct icmp *icp; struct icmp6_hdr *icp6; PING_DATA *pdp; int this_count; struct timeval sent_time = {0, 0}; struct timeval current_time = {0, 0}; long time_diff; char buf[BUFSIZ]; union { struct sockaddr_in6 saddr6; struct sockaddr_in saddr; struct sockaddr_ll FROM; } from_addr; unsigned int slen; int result; TARGET *t; int seq; CONFIG *arp; slen = sizeof(from_addr); result = ping_rcv(ctable[0], buf, BUFSIZ, (struct sockaddr_in6 *)&from_addr, &slen, DEFAULT_SELECT_WAIT, &arp); if(result <= 0) { return(0); } gettimeofday(¤t_time, NULL); if(arp) { struct sockaddr_ll *FROM = &from_addr.FROM; TARGET *t = arp->data; struct arphdr *ah = (struct arphdr*)buf; unsigned char *p = (unsigned char *)(ah+1); struct in_addr src_ip, dst_ip; int ind; /* Filter out wild packets */ if(FROM->sll_pkttype != PACKET_HOST && FROM->sll_pkttype != PACKET_BROADCAST && FROM->sll_pkttype != PACKET_MULTICAST) return(1); /* Only these types are recognised */ #if 0 if(ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) return(1); #else if(ah->ar_op != htons(ARPOP_REPLY)) return(1); #endif /* ARPHRD check and this darned FDDI hack here :-( */ if(ah->ar_hrd != htons(FROM->sll_hatype) && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) return(1); /* Protocol must be IP. */ if(ah->ar_pro != htons(ETH_P_IP)) return(1); if(ah->ar_pln != 4) return(1); if(ah->ar_hln != t->me.sll_halen) return(1); #if defined(DEBUG) for(ind = 0; ind < result; ind ++) { fprintf(stderr, "%02x", (unsigned char)buf[ind]); if(!((ind + 1) % 2)) fprintf(stderr, " "); if(!((ind + 1) % 32)) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); for(ind = 0; ind < result; ind ++) { fprintf(stderr, "%3u", (unsigned char)buf[ind]); if(!((ind + 1) % 32)) fprintf(stderr, "\n"); else fprintf(stderr, ","); } fprintf(stderr, "\n"); #endif if(result < sizeof(*ah) + 2*(4 + ah->ar_hln)) return(1); memcpy(&src_ip, p+ah->ar_hln, 4); memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4); if(src_ip.s_addr != t->dst.s_addr) return(1); if(t->src.s_addr != dst_ip.s_addr) return(1); if(memcmp(p+ah->ar_hln+4, &t->me.sll_addr, ah->ar_hln)) return(1); /* update packet log here */ /* there are no sequence numbers in arp replies so just mark seq - 1 replied */ ind = ((t->seq - 1) >= 0 ? (t->seq - 1) : (FOLLOWED_PKTS + (t->seq - 1))) % FOLLOWED_PKTS; t->sentpkts[ind].flags.replied = 1; t->sentpkts[ind].flags.waiting = 0; t->sentpkts[ind].replied_time = current_time; t->sentpkts[ind].rtt = timeval_diff(¤t_time, &t->sentpkts[ind].sent_time); return(1); } #if defined(DEBUG) if(cfg.debug >= 9) { char sbuf[INET6_ADDRSTRLEN]; syslog(LOG_INFO, "not arp: family = %d, AF_INET = %d, AF_INET6 = %d, inet_ntop addr = %s, inet_ntoa addr = %s", from_addr.saddr6.sin6_family, AF_INET, AF_INET6, inet_ntop(from_addr.saddr6.sin6_family, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN), inet_ntoa(from_addr.saddr.sin_addr)); } #endif switch(from_addr.saddr6.sin6_family) { case AF_INET: #if defined(DEBUG) syslog(LOG_INFO, "%s: %s: AF_INET reply", __FILE__, __FUNCTION__); #endif ip = (struct ip *)buf; hlen = ip->ip_hl << 2; icp = (struct icmp *)(buf + hlen); if(icp->icmp_type == ICMP_ECHO) { return(1); } if(icp->icmp_type == ICMP_ECHOREPLY) { if(icp->icmp_id != get_ident()) { /* fprintf(stderr, "icmp_id = %d funny, got reply from %s to something else ...\n", icp->icmp_id, inet_ntoa(saddr.sin_addr)); */ return(1); } if(result < sizeof(struct icmp) + sizeof(PING_DATA)) { /* fprintf(stderr, "too short ping reply\n"); */ return(1); } pdp = (PING_DATA *)(buf + hlen + sizeof(struct icmp)); this_count = pdp->ping_count; sent_time = pdp->ping_ts; time_diff = timeval_diff(¤t_time, &sent_time); if(pdp->id >= num_hosts) { #if defined(DEBUG) syslog(LOG_INFO, "out of range: pdp->id = %d >= num_hosts = %d from %s", pdp->id, num_hosts, inet_ntoa(from_addr.saddr.sin_addr)); dump_pkt(buf, sizeof(struct ip) + sizeof(struct icmp) + sizeof(PING_DATA)); set_dump(1); #endif return(1); } t = ctable[pdp->id]->data; if(memcmp(&ip->ip_src, &t->dst, sizeof(struct in_addr) != 0)) { return(1); } seq = icp->icmp_seq % FOLLOWED_PKTS; if(t->sentpkts[seq].seq == icp->icmp_seq) { t->sentpkts[seq].flags.replied = 1; t->sentpkts[seq].flags.waiting = 0; t->sentpkts[seq].replied_time = current_time; t->sentpkts[seq].rtt = timeval_diff(¤t_time, &t->sentpkts[seq].sent_time); } else if(cfg.debug >= 9) syslog(LOG_INFO, "sentpkts seq != icmp_seq"); if(cfg.debug >= 9) syslog(LOG_INFO, "received seq = %d from %s, id = %d, num_sent = %d, target id = %u, time_diff = %ld", icp->icmp_seq, inet_ntoa(from_addr.saddr.sin_addr), icp->icmp_id, this_count, pdp->id, time_diff); return(1); } else { struct icmpmsg *msg; msg = stricmp(icp->icmp_type, icp->icmp_code); if(cfg.debug >= 9) syslog(LOG_INFO, "got odd reply from %s, icmp_type = %d %s, icmp_code = %d %s", inet_ntoa(from_addr.saddr.sin_addr), icp->icmp_type, msg->type_msg, icp->icmp_code, msg->code_msg); return(1); } break; case AF_INET6: #if defined(DEBUG) syslog(LOG_INFO, "%s: %s: AF_INET6 reply", __FILE__, __FUNCTION__); #endif icp6 = (struct icmp6_hdr *)buf; if(icp6->icmp6_type == ICMP6_ECHO_REQUEST) { return(1); } if (icp6->icmp6_type == ICMP6_ECHO_REPLY) { /* v6 reply */ char sbuf[INET6_ADDRSTRLEN]; #if defined(DEBUG) dump_pkt(buf, sizeof(struct icmp6_hdr) + sizeof(PING_DATA)); #endif /* syslog(LOG_INFO, "sizeof struct icmp6_hdr = %ld\n", sizeof(struct icmp6_hdr)); */ if(icp6->icmp6_id != get_ident()) { return(1); } if(result < sizeof(struct icmp6_hdr) + sizeof(PING_DATA)) { return(1); } /* pdp = (PING_DATA *)(buf + hlen + sizeof(struct icmp6_hdr)); */ /* pdp = (PING_DATA *)(buf + sizeof(struct icmp6_hdr)); */ pdp = (PING_DATA *)(buf + sizeof(struct icmp6_hdr)); this_count = pdp->ping_count; sent_time = pdp->ping_ts; time_diff = timeval_diff(¤t_time, &sent_time); #if defined(DEBUG) syslog(LOG_INFO, "%s: %s: this_count = %d, sent_time = %ld,%ld, pdp->id = %d", __FILE__, __FUNCTION__, this_count, sent_time.tv_sec, sent_time.tv_usec, pdp->id); #endif if(pdp->id >= num_hosts) { #if defined(DEBUG) syslog(LOG_INFO, "out of range: pdp->id = %d >= num_hosts = %d from %s", pdp->id, num_hosts, inet_ntop(AF_INET6, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN)); dump_pkt(buf, sizeof(struct icmp6_hdr) + sizeof(PING_DATA)); set_dump(1); #endif return(1); } t = ctable[pdp->id]->data; if(memcmp(&from_addr.saddr6.sin6_addr, &t->dst6, sizeof(struct in6_addr)) != 0) { return(1); } seq = ntohs(icp6->icmp6_seq) % FOLLOWED_PKTS; if(t->sentpkts[seq].seq == ntohs(icp6->icmp6_seq)) { t->sentpkts[seq].flags.replied = 1; t->sentpkts[seq].flags.waiting = 0; t->sentpkts[seq].replied_time = current_time; t->sentpkts[seq].rtt = timeval_diff(¤t_time, &t->sentpkts[seq].sent_time); } else if (cfg.debug >= 9) syslog(LOG_INFO, "sentpkts seq != icmp_seq"); if(cfg.debug >= 9) syslog(LOG_INFO, "received seq = %d from %s, id = %d, num_sent = %d, target id = %u, time_diff = %ld", ntohs(icp6->icmp6_seq), inet_ntop(AF_INET6, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN), icp6->icmp6_id, this_count, pdp->id, time_diff); return(1); } else { char sbuf[INET6_ADDRSTRLEN]; struct icmp6msg *msg; msg = stricmp6(icp6->icmp6_type, icp6->icmp6_code); if(cfg.debug >= 9) syslog(LOG_INFO, "got odd reply from %s, icmp_type = %d %s, icmp_code = %d %s", inet_ntop(from_addr.saddr6.sin6_family, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN), icp6->icmp6_type, msg->type_msg, icp6->icmp6_code, msg->code_msg); return(1); } break; default: syslog(LOG_INFO, "%s: %s: unknown family reply", __FILE__, __FUNCTION__); break; } return(1); } static int ping_rcv(CONFIG *first, char *buf, int len, struct sockaddr_in6 *saddr, unsigned int *slen, long usec, CONFIG **arp) { int nfound, n; fd_set readset; struct timeval to = {0, 0}; int max; CONFIG *cur; TARGET *t; int cnt_targets = 0; FD_ZERO(&readset); max = 0; for(cur = first; cur; cur = cur->next) { t = cur->data; if(t->sock == -1) continue; if(t->sock > max) max = t->sock; FD_SET(t->sock, &readset); cnt_targets++; } /* no point in calling select if we didn't find any open sockets. so sleep and return ... */ if(cnt_targets == 0) { sleep(1); return(0); } to.tv_sec = usec / 1000000; to.tv_usec = (usec - (to.tv_sec * 1000000)); #if defined(DEBUG) printf("to.tv_sec = %ld, to.tv_usec = %ld\n", to.tv_sec, to.tv_usec); #endif nfound = select(max + 1, &readset, NULL, NULL, &to); if(nfound < 0) { if(errno != EINTR) syslog(LOG_INFO, "select failed \"%s\"", strerror(errno)); return(0); } if(nfound == 0) return(-1); for(cur = first; cur; cur = cur->next) { t = cur->data; if(t->sock == -1) continue; if(FD_ISSET(t->sock, &readset)) { if(!cur->check_arp) { *arp = (CONFIG *)NULL; } else { *arp = cur; } n = recvfrom(t->sock, buf, len, 0, (struct sockaddr *)saddr, slen); if(n < 0) { if(cfg.debug >= 9) syslog(LOG_INFO, "recvfrom failed with connection %s \"%s\", n = %d, errno = %d", cur->name, strerror(errno), n, errno); close(t->sock); t->sock = -1; return(0); } return(n); } } return(0); } static int ping_send(CONFIG *cur) { char buf[BUFSIZ]; struct icmp *icp; PING_DATA *pdp; TARGET *t; int n; int ping_pkt_size; t = cur->data; gettimeofday(&t->last_send_time, NULL); if(cur->check_arp) { int err; unsigned char buf[256]; struct arphdr *ah = (struct arphdr*)buf; unsigned char *p = (unsigned char *)(ah+1); if(cur->dstinfo->ai_family == AF_INET6) { syslog(LOG_ERR, "%s: %s: ipv6 arping not supported", __FILE__, __FUNCTION__); return(-1); } ah->ar_hrd = htons(t->me.sll_hatype); if(ah->ar_hrd == htons(ARPHRD_FDDI)) ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = t->me.sll_halen; ah->ar_pln = 4; ah->ar_op = htons(ARPOP_REQUEST); memcpy(p, &t->me.sll_addr, ah->ar_hln); p += t->me.sll_halen; memcpy(p, &t->src, 4); p += 4; memcpy(p, &t->he.sll_addr, ah->ar_hln); p += ah->ar_hln; memcpy(p, &t->dst, 4); p += 4; #if defined(DEBUG) { int ind; for(ind = 0; ind < p - buf; ind ++) { fprintf(stderr, "%02x", (unsigned char)buf[ind]); if(!((ind + 1) % 2)) fprintf(stderr, " "); if(!((ind + 1) % 32)) fprintf(stderr, "\n"); } fprintf(stderr, "\n"); for(ind = 0; ind < p - buf; ind ++) { fprintf(stderr, "%3u", (unsigned char)buf[ind]); if(!((ind + 1) % 32)) fprintf(stderr, "\n"); else fprintf(stderr, ","); } fprintf(stderr, "\n"); } #endif if(t->sock != -1) { err = sendto(t->sock, buf, p - buf, 0, (struct sockaddr*)&t->he, sizeof(t->he)); if(err < 0) { if(cfg.debug >= 9) syslog(LOG_ERR, "arping sendto failed to %s on %s reason \"%s\"", cur->name, cur->device, strerror(errno)); close(t->sock); t->sock = -1; } } else { if(cfg.debug >= 9) syslog(LOG_INFO, "arping sendto socket not open for %s", cur->name); err = -1; } { /* we don't care what the error was just advance with seq */ int seq; seq = t->seq % FOLLOWED_PKTS; t->sentpkts[seq].seq = t->seq; t->sentpkts[seq].sent_time = t->last_send_time; t->sentpkts[seq].flags.replied = 0; t->sentpkts[seq].flags.timeout = 0; t->sentpkts[seq].flags.waiting = 1; if(t->sentpkts[seq].flags.used == 0) t->used++; t->sentpkts[seq].flags.used = 1; t->sentpkts[seq].flags.error = (err == -1) ? 1 : 0; t->seq = (t->seq + 1) % SEQ_LIMITER; /* limit seq so that consecutive missing and received pkt counting doesn't get confused when seq "overflows" */ t->num_sent++; } if(err == (p - buf)) { return(0); } return(err); } if(cur->dstinfo->ai_family == AF_INET6) { struct icmp6_hdr *icp6; ping_pkt_size = sizeof(struct icmp6_hdr) + sizeof(PING_DATA); memset(buf, 0, ping_pkt_size); icp6 = (struct icmp6_hdr *)buf; icp6->icmp6_type = ICMP6_ECHO_REQUEST; icp6->icmp6_code = 0; icp6->icmp6_seq = htons(t->seq); /* I saw a tcpdump suggesting that there is something wrong with seq thus htons() */ icp6->icmp6_id = get_ident(); pdp = (PING_DATA *)(buf + sizeof(struct icmp6_hdr)); pdp->ping_count = t->num_sent; pdp->ping_ts = t->last_send_time; pdp->id = t->id; icp6->icmp6_cksum = 0; /* the ipv6 stack calculates the checksum for us */ if(t->sock != -1) { if(cfg.debug >= 9) syslog(LOG_INFO, "cmsglen = %d", t->cmsglen); if(t->cmsglen == 0) { n = sendto(t->sock, buf, ping_pkt_size, 0, (struct sockaddr *)&t->dst_addr6, sizeof(t->dst_addr6)); if(n < 0) { if(errno == ENODEV) { if(cfg.debug >= 9) syslog(LOG_ERR, "connection %s no such device %s \"%s\"", cur->name, cur->device, strerror(errno)); } else if (cfg.debug >= 9) syslog(LOG_ERR, "ping6 sendto failed to %s on %s reason \"%s\"", cur->name, cur->device, strerror(errno)); if(t->sock != -1) { close(t->sock); t->sock = -1; } } } else { struct msghdr mhdr; struct iovec iov; int confirm = 0; iov.iov_len = ping_pkt_size; iov.iov_base = buf; mhdr.msg_name = &t->dst_addr6; mhdr.msg_namelen = sizeof(struct sockaddr_in6); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = t->cmsgbuf; mhdr.msg_controllen = t->cmsglen; n = sendmsg(t->sock, &mhdr, confirm); if(cfg.debug >= 9 && n < 0) syslog(LOG_INFO, "sendmsg failed for %s %s", cur->name, strerror(errno)); if(n < 0) { close(t->sock); t->sock = -1; } } } else { if(cfg.debug >= 9) syslog(LOG_INFO, "ping sendto socket not open for %s", cur->name); n = -1; } { /* we don't care what the error was just advance with seq */ int seq; seq = t->seq % FOLLOWED_PKTS; #if defined(DEBUG) fprintf(stderr, "ping_send seq = %d to %s, num_sent = %ld, %ld, pkt_size = %d\n", t->seq, inet_ntoa(t->dst_addr.sin_addr), t->num_sent, pdp->ping_count, ping_pkt_size); #endif t->sentpkts[seq].seq = t->seq; t->sentpkts[seq].sent_time = t->last_send_time; t->sentpkts[seq].flags.replied = 0; t->sentpkts[seq].flags.timeout = 0; t->sentpkts[seq].flags.waiting = 1; if(t->sentpkts[seq].flags.used == 0) t->used++; t->sentpkts[seq].flags.used = 1; t->sentpkts[seq].flags.error = (n < 1) ? 1 : 0; t->seq = (t->seq + 1) % SEQ_LIMITER; /* limit seq so that consecutive missing and received pkt counting doesn't get confused when seq "overflows" */ t->num_sent++; } if(n == ping_pkt_size) { return(0); } return(n); } /* send a ping packet */ ping_pkt_size = sizeof(struct icmp) + sizeof(PING_DATA); memset(buf, 0, ping_pkt_size); icp = (struct icmp *)buf; icp->icmp_type = ICMP_ECHO; icp->icmp_code = 0; icp->icmp_cksum = 0; icp->icmp_seq = t->seq; icp->icmp_id = get_ident(); pdp = (PING_DATA *)(buf + sizeof(struct icmp)); pdp->ping_count = t->num_sent; pdp->ping_ts = t->last_send_time; pdp->id = t->id; icp->icmp_cksum = in_cksum((u_short *)icp, ping_pkt_size); if(t->sock != -1) { n = sendto(t->sock, buf, ping_pkt_size, 0, (struct sockaddr *)&t->dst_addr, sizeof(struct sockaddr)); if(n < 0) { if(errno == ENODEV) { if(cfg.debug >= 9) syslog(LOG_ERR, "connection %s no such device %s \"%s\"", cur->name, cur->device, strerror(errno)); /* exit(2); */ /* commented out. handle this situation like the packet had been sent. see below. */ } else if(cfg.debug >= 9) syslog(LOG_ERR, "ping sendto failed to %s on %s reason \"%s\"", cur->name, cur->device, strerror(errno)); if(t->sock != -1) { close(t->sock); t->sock = -1; } } } else { if(cfg.debug >= 9) syslog(LOG_INFO, "ping sendto socket not open for %s", cur->name); n = -1; } { /* we don't care what the error was just advance with seq */ int seq; seq = t->seq % FOLLOWED_PKTS; #if defined(DEBUG) fprintf(stderr, "ping_send seq = %d to %s, num_sent = %ld, %ld, pkt_size = %d\n", t->seq, inet_ntoa(t->dst_addr.sin_addr), t->num_sent, pdp->ping_count, ping_pkt_size); #endif t->sentpkts[seq].seq = t->seq; t->sentpkts[seq].sent_time = t->last_send_time; t->sentpkts[seq].flags.replied = 0; t->sentpkts[seq].flags.timeout = 0; t->sentpkts[seq].flags.waiting = 1; if(t->sentpkts[seq].flags.used == 0) t->used++; t->sentpkts[seq].flags.used = 1; t->sentpkts[seq].flags.error = (n < 1) ? 1 : 0; t->seq = (t->seq + 1) % SEQ_LIMITER; /* limit seq so that consecutive missing and received pkt counting doesn't get confused when seq "overflows" */ t->num_sent++; } if(n == ping_pkt_size) { return(0); } return(n); } static int event_script_check(const char *path) { struct stat statbuf; if(!path) { if(cfg.debug >= 9) syslog(LOG_ERR, "NULL pointer event script"); return(0); } if(!*path) { if(cfg.debug >= 9) syslog(LOG_ERR, "null string event script"); return(0); } /* check that the script is owner executable */ if(stat(path, &statbuf) == -1) { syslog(LOG_ERR, "failed to stat event script \"%s\" reason \"%s\"", path, strerror(errno)); return(0); } if((statbuf.st_mode & S_IXUSR) == 0) { syslog(LOG_ERR, "event script \"%s\" is not executable by owner, please check permissions", path); return(0); } return(1); } static void init_config_data(CONFIG *first, CONFIG *last, CONFIG ***ctable) { int i; CONFIG *cur; TARGET *t = NULL; /* initialize config->data */ for(cur = first, num_hosts = 0; cur; cur = cur->next, num_hosts++) { u_int ipaddress; if((t = malloc(sizeof(TARGET))) == NULL) { syslog(LOG_ERR, "main: initializing targets failed to malloc"); exit(1); } memset(t, 0, sizeof(TARGET)); cur->data = t; /* protocol family independent init */ t->seq = 0; t->downseq = 0; t->downseqreported = 0; t->last_send_time.tv_sec = 0; t->last_send_time.tv_usec = 0; t->num_sent = 0; t->timeout_max = 0; t->consecutive_missing_max = 0; t->used = 0; memset(t->cmsgbuf, 0, sizeof(t->cmsgbuf)); t->cmsglen = 0; t->id = num_hosts; /* get initial connection state assumption from config */ t->status = cur->status; t->sock = -1; if(cur->dstinfo->ai_family == AF_INET6) { /* ipv6 init */ if(cur->srcinfo) { if(inet_pton(AF_INET6, cur->sourceip, &t->src6) != 1) { syslog(LOG_ERR, "%s: %s: src6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name); } t->src_addr6.sin6_family = cur->srcinfo->ai_family; if(inet_pton(AF_INET6, cur->sourceip, &t->src_addr6.sin6_addr) != 1) { syslog(LOG_ERR, "%s: %s: src6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name); } } if(inet_pton(AF_INET6, cur->checkip, &t->dst6) != 1) { syslog(LOG_ERR, "%s: %s: dst6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name); } t->dst_addr6.sin6_family = cur->dstinfo->ai_family; if(inet_pton(AF_INET6, cur->checkip, &t->dst_addr6.sin6_addr) != 1) { syslog(LOG_ERR, "%s: %s: dst6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name); } } else { /* ipv4 init */ ipaddress = inet_addr(cur->checkip); t->dst_addr.sin_family = AF_INET; t->dst_addr.sin_addr = *((struct in_addr *)&ipaddress); t->dst = *((struct in_addr *)&ipaddress); if(cur->srcinfo) { if(inet_pton(AF_INET, cur->sourceip, &t->src) != 1) { syslog(LOG_ERR, "%s: %s: src inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name); } } } } if(((*ctable) = (CONFIG **)malloc(sizeof(CONFIG *) * num_hosts)) == NULL) { syslog(LOG_ERR, "main: can't malloc for ctable"); exit(1); } /* create pointer table */ for(cur = first, i = 0; cur; cur = cur->next, i++) { (*ctable)[i] = cur; } } static int open_arp_sock(CONFIG *cur) { int ifindex = 0; TARGET *t = (TARGET *)cur->data; if(t->sock != -1) return(0); if(cur->dstinfo->ai_family == AF_INET6) { syslog(LOG_ERR, "%s: %s: protocol family is ipv6?", __FILE__, __FUNCTION__); return(1); } t->sock = socket(PF_PACKET, SOCK_DGRAM, 0); if(t->sock < 0) { syslog(LOG_ERR, "could not open socket for %s arp ping \"%s\"", cur->name, strerror(errno)); t->sock = -1; return(1); } if(fcntl(t->sock, F_SETFD, FD_CLOEXEC) == -1) { syslog(LOG_ERR, "failed to set close on exec on socket %s reason \"%s\"", cur->name, strerror(errno)); } if(cur->device && *cur->device) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, cur->device, IFNAMSIZ-1); if(ioctl(t->sock, SIOCGIFINDEX, &ifr) < 0) { syslog(LOG_ERR, "unknown iface \"%s\"", cur->device); close(t->sock); t->sock = -1; return(2); } ifindex = ifr.ifr_ifindex; if(ioctl(t->sock, SIOCGIFFLAGS, (char*)&ifr)) { syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } if(!(ifr.ifr_flags&IFF_UP)) { syslog(LOG_ERR, "Interface \"%s\" is down", cur->device); close(t->sock); t->sock = -1; return(2); } if(ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) { syslog(LOG_ERR, "Interface \"%s\" is not ARPable", cur->device); close(t->sock); t->sock = -1; return(2); } } if(inet_aton(cur->checkip, &t->dst) != 1) { struct hostent *hp; hp = gethostbyname2(cur->checkip, AF_INET); if(!hp) { syslog(LOG_ERR, "unknown host %s\n", cur->checkip); close(t->sock); t->sock = -1; return(2); } memcpy(&t->dst, hp->h_addr, 4); } if(cur->sourceip && *cur->sourceip) if(inet_aton(cur->sourceip, &t->src) != 1) { syslog(LOG_ERR, "invalid source %s\n", cur->sourceip); close(t->sock); t->sock = -1; return(2); } if(probe_src_ip_addr(cur) != 0) { close(t->sock); t->sock = -1; return(2); } t->me.sll_family = AF_PACKET; t->me.sll_ifindex = ifindex; t->me.sll_protocol = htons(ETH_P_ARP); if(bind(t->sock, (struct sockaddr*)&t->me, sizeof(t->me)) == -1) { syslog(LOG_ERR, "bind \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } { int alen = sizeof(t->me); if(getsockname(t->sock, (struct sockaddr*)&t->me, (socklen_t*)&alen) == -1) { syslog(LOG_ERR, "getsockname \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } } if(t->me.sll_halen == 0) { syslog(LOG_ERR, "Interface \"%s\" is not ARPable (no ll address)", cur->device); close(t->sock); t->sock = -1; return(2); } t->he = t->me; memset(t->he.sll_addr, -1, min(t->he.sll_halen, sizeof t->he.sll_addr)); #if 0 printf("ARPING %s ", inet_ntoa(t->dst)); printf("from %s %s\n", inet_ntoa(t->src), cur->device ? : ""); #endif if(!t->src.s_addr) { syslog(LOG_ERR, "no source address for %s", cur->name); close(t->sock); t->sock = -1; return(2); } if(cur->ttl) { int ittl = cur->ttl; if(setsockopt(t->sock, IPPROTO_IP, IP_MULTICAST_TTL, &cur->ttl, 1) == -1) { syslog(LOG_ERR, "can't set multicast time-to-live \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } if(setsockopt(t->sock, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) == -1) { syslog(LOG_ERR, "can't set unicast time-to-live \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } } return(0); } static int open_icmp_sock(CONFIG *cur) { TARGET *t = (TARGET *)cur->data; struct protoent *proto; int pf = cur->dstinfo->ai_family; if(t->sock != -1) return(0); if(pf == AF_INET6) { if((proto = getprotobyname("ipv6-icmp")) == NULL) { syslog(LOG_ERR, "no ipv6-icmp proto found"); return(1); } } else { if((proto = getprotobyname("icmp")) == NULL) { syslog(LOG_ERR, "no icmp proto found"); return(1); } } t->sock = socket(pf, SOCK_RAW, proto->p_proto); if(t->sock < 0) { syslog(LOG_ERR, "could not open socket for ping target \"%s\" reason \"%s\"\n", cur->name, strerror(errno)); t->sock = -1; return(1); } if(fcntl(t->sock, F_SETFD, FD_CLOEXEC) == -1) { syslog(LOG_ERR, "failed to set close on exec on socket %s reason \"%s\"", cur->name, strerror(errno)); } if(pf == AF_INET6) { int opton = 1; #ifdef IPV6_RECVHOPOPTS if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RECVHOPOPTS)"); close(t->sock); t->sock = -1; return(2); } #else /* old adv. API */ if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_HOPOPTS, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_HOPOPTS)"); close(t->sock); t->sock = -1; return(s); } #endif #ifdef IPV6_RECVDSTOPTS if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RECVDSTOPTS)"); close(t->sock); t->sock = -1; return(2); } #else /* old adv. API */ if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_DSTOPTS, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_DSTOPTS)"); close(t->sock); t->sock = -1; return(2); } #endif #ifdef IPV6_RECVRTHDRDSTOPTS if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RECVRTHDRDSTOPTS)"); close(t->sock); t->sock = -1; return(2); } #endif #ifdef IPV6_RECVRTHDR if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RECVRTHDR)"); close(t->sock); t->sock = -1; return(2); } #else /* old adv. API */ if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RTHDR, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RTHDR)"); close(t->sock); t->sock = -1; return(2); } #endif #ifndef USE_SIN6_SCOPE_ID #ifdef IPV6_RECVPKTINFO if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO)"); close(t->sock); t->sock = -1; return(2); } #else /* old adv. API */ if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_PKTINFO, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_PKTINFO)"); close(t->sock); t->sock = -1; return(2); } #endif #endif /* USE_SIN6_SCOPE_ID */ #ifdef IPV6_RECVHOPLIMIT if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT)"); close(t->sock); t->sock = -1; return(2); } #else /* old adv. API */ if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(IPV6_HOPLIMIT)"); close(t->sock); t->sock = -1; return(2); } #endif #ifdef IPV6_CHECKSUM #ifndef SOL_RAW #define SOL_RAW IPPROTO_IPV6 #endif opton = 2; if(setsockopt(t->sock, SOL_RAW, IPV6_CHECKSUM, &opton, sizeof(opton))) { syslog(LOG_ERR, "setsockopt(SOL_RAW,IPV6_CHECKSUM)"); close(t->sock); t->sock = -1; return(2); } #endif } if(pf == AF_INET6) { int hold = 1; ICMP6_FILTER_SETBLOCKALL(&t->filter); if (setsockopt(t->sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) { syslog(LOG_INFO, "WARNING: your kernel is veeery old. No problems."); ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &t->filter); ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &t->filter); ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &t->filter); ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &t->filter); } ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &t->filter); if(setsockopt(t->sock, IPPROTO_ICMPV6, ICMP6_FILTER, &t->filter, sizeof(struct icmp6_filter)) < 0) { syslog(LOG_ERR, "setsockopt(ICMP6_FILTER)"); return(2); } } if(cur->ttl) { if(pf == AF_INET6) { if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &cur->ttl, sizeof(cur->ttl)) == -1) { syslog(LOG_ERR, "can't set multicast hop limit \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &cur->ttl, sizeof(cur->ttl)) == -1) { syslog(LOG_ERR, "can't set unicast hop limit \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } } else if(pf == AF_INET) { /* AF_INET */ int ittl = cur->ttl; if(setsockopt(t->sock, IPPROTO_IP, IP_MULTICAST_TTL, &cur->ttl, 1) == -1) { syslog(LOG_ERR, "can't set multicast time-to-live \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } if(setsockopt(t->sock, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) == -1) { syslog(LOG_ERR, "can't set unicast time-to-live \"%s\"", strerror(errno)); close(t->sock); t->sock = -1; return(2); } } } if(pf == AF_INET && cur->device && *cur->device) { if(setsockopt(t->sock, SOL_SOCKET, SO_BINDTODEVICE, cur->device, strlen(cur->device) + 1) == -1) { syslog(LOG_INFO, "failed to bind to ping interface device \"%s\", \"%s\"", cur->device, strerror(errno)); close(t->sock); t->sock = -1; return(2); } } #if defined(DEBUG) if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: probing for src ip for %s", __FILE__, __FUNCTION__, cur->name); #endif if(probe_src_ip_addr(cur) != 0) { close(t->sock); t->sock = -1; return(2); } #if defined(DEBUG) if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: probing for src ip for %s done", __FILE__, __FUNCTION__, cur->name); #endif if(cur->sourceip && *cur->sourceip) { if(cur->srcinfo->ai_family == AF_INET) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(cur->sourceip); if(bind(t->sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { syslog(LOG_ERR, "ping can't bind \"%s\"", strerror(errno)); return(1); } } else { struct sockaddr_in6 addr; #if defined(DEBUG) if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: setting v6 src addr", __FILE__, __FUNCTION__); #endif memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; if(inet_pton(AF_INET6, cur->sourceip, &addr.sin6_addr) != 1) { syslog(LOG_ERR, "ping6 failed to convert connection %s address %s", cur->name, cur->sourceip); return(1); } if(bind(t->sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { syslog(LOG_ERR, "ping6 can't bind %s to %s, \"%s\"", cur->name, cur->sourceip, strerror(errno)); return(1); } #if defined(DEBUG) if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: setting v6 src addr done", __FILE__, __FUNCTION__); #endif } } if(pf == AF_INET6 && cur->device && *cur->device) { struct ifreq ifr; struct cmsghdr *cmsg; struct in6_pktinfo *ipi; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, cur->device, IFNAMSIZ-1); if(ioctl(t->sock, SIOCGIFINDEX, &ifr) < 0) { syslog(LOG_ERR, "ping6 unknown iface %s", cur->device); return(2); } memset(&t->cmsgbuf, 0, sizeof(t->cmsgbuf)); t->cmsglen = 0; cmsg = (struct cmsghdr *)t->cmsgbuf; t->cmsglen += CMSG_SPACE(sizeof(*ipi)); cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi)); cmsg->cmsg_level = SOL_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg); memset(ipi, 0, sizeof(*ipi)); ipi->ipi6_ifindex = ifr.ifr_ifindex; } return(0); } static int probe_src_ip_addr(CONFIG *cur) { /* probe for src ip address */ TARGET *t = (TARGET *)cur->data; int probe_fd; int pf = cur->dstinfo->ai_family; probe_fd = socket(pf, SOCK_DGRAM, 0); if(probe_fd < 0) { syslog(LOG_ERR, "ping probe socket for %s failed \"%s\"", cur->name, strerror(errno)); return(2); } if(fcntl(t->sock, F_SETFD, FD_CLOEXEC) == -1) { syslog(LOG_ERR, "ping probe failed to set close on exec on probe socket for %s reason \"%s\"", cur->name, strerror(errno)); } if(cur->device && *cur->device) { if(setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, cur->device, strlen(cur->device) + 1) == -1) syslog(LOG_INFO, "WARNING: ping probe interface \"%s\" is ignored for %s reason \"%s\"", cur->device, cur->name, strerror(errno)); } if(pf == AF_INET) { struct sockaddr_in saddr; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if(t->src.s_addr) { saddr.sin_addr = t->src; if(bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { syslog(LOG_ERR, "ping probe bind failed for %s \"%s\"", cur->name, strerror(errno)); close(probe_fd); /* earlier probed src addr is not usable, wipe it */ memset(&t->src, 0, sizeof(t->src)); return(2); } } else { int on = 1; int alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = t->dst; if(setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1) syslog(LOG_INFO, "WARNING: ping probe setsockopt(SO_DONTROUTE) \"%s\"", strerror(errno)); if(connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { syslog(LOG_ERR, "ping probe connect for %s failed \"%s\"", cur->name, strerror(errno)); close(probe_fd); return(2); } if(getsockname(probe_fd, (struct sockaddr*)&saddr, (socklen_t*)&alen) == -1) { syslog(LOG_ERR, "ping probe getsockname for %s failed \"%s\"", cur->name, strerror(errno)); close(probe_fd); return(2); } t->src = saddr.sin_addr; } } else if (pf == AF_INET6) { /* not AF_INET */ struct sockaddr_in6 saddr; unsigned char nulladdr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; memset(&saddr, 0, sizeof(saddr)); saddr.sin6_family = AF_INET6; if(memcmp(&t->src6, nulladdr, sizeof(t->src6)) != 0) { /* is not null addr */ memcpy(&saddr.sin6_addr, &t->src6, sizeof(t->src6)); if(bind(probe_fd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { syslog(LOG_ERR, "ping6 probe bind failed for %s \"%s\"", cur->name,strerror(errno)); close(probe_fd); /* earlier probed src addr is not usable, wipe it */ memset(&t->src6, 0, sizeof(t->src6)); return(2); } } else { /* is null addr */ socklen_t alen = sizeof(saddr); saddr.sin6_port = htons(1025); saddr.sin6_family = cur->dstinfo->ai_family; memcpy(&saddr.sin6_addr, &t->dst6, sizeof(t->dst6)); #if 0 if(setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)) == -1) syslog(LOG_INFO, "WARNING: ping6 probe setsockopt(SO_DONTROUTE) for %s \"%s\"", cur->name, strerror(errno)); #endif if(connect(probe_fd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) { syslog(LOG_ERR, "ping6 probe connect for %s failed \"%s\"", cur->name, strerror(errno)); close(probe_fd); return(2); } if(getsockname(probe_fd, (struct sockaddr *)&saddr, &alen) == -1) { syslog(LOG_ERR, "ping6 probe getsockname for %s failed \"%s\"", cur->name, strerror(errno)); close(probe_fd); return(2); } memcpy(&t->src6, &saddr.sin6_addr, sizeof(saddr.sin6_addr)); } } /* if AF_INET */ close(probe_fd); return(0); } #if defined(DEBUG) static void dump_pkt(const void *buf, size_t len) { int i; unsigned char *s; char obuf[BUFSIZ]; char *pad; memset(obuf, 0, BUFSIZ); s = (unsigned char *)buf; pad = ""; for(i = 0; i < len && i < BUFSIZ; i++) { snprintf(obuf + strlen(obuf), BUFSIZ, "%s%02x", pad, s[i]); pad = " "; } syslog(LOG_INFO, "%s: %s: hexdump %s", __FILE__, __FUNCTION__, obuf); memset(obuf, 0, BUFSIZ); s = (unsigned char *)buf; pad = ""; for(i = 0; i < len && i < BUFSIZ; i++) { snprintf(obuf + strlen(obuf), BUFSIZ, "%s%03d", pad, s[i]); pad = " "; } syslog(LOG_INFO, "%s: %s: decdump %s", __FILE__, __FUNCTION__, obuf); } #endif /* EOF */ lsm-1.0.21/foolsm.conf000066400000000000000000000023121455003704000145300ustar00rootroot00000000000000# # Copyright (C) 2009-2015 Mika Ilmaranta # # License: GPLv2 # # # Debug level: 0 .. 8 are normal, 9 gives lots of stuff # #debug=10 #debug=9 #debug=8 # # Defaults for the connection entries # These are set in the code. You may override any values here. # #defaults { # name=defaults # checkip=127.0.0.1 # eventscript= # notifyscript=/usr/libexec/foolsm/default_script # max_packet_loss=15 # max_successive_pkts_lost=7 # min_packet_loss=5 # min_successive_pkts_rcvd=10 # interval_ms=1000 # timeout_ms=1000 # warn_email=root # check_arp=0 # sourceip= # if using ping probes for monitoring only then defaults should # not define a default device for packets to autodiscover their path # to destination # device=eth0 # use system default ttl # ttl=0 # assume initial up state at foolsm startup # (1 = up, 0 = down, 2 = unknown (default)) # status=1 # how many packets have to go through before a connection is declared up # (0 = disabled) # startup_acceleration=0 # send first 10 packets in a burst (0 = disabled) with interval 200ms # startup_burst_pkts=10 # startup_burst_interval=200 #} # # Some example connections are found in foolsm.conf.sample # -include /etc/foolsm/local*.conf lsm-1.0.21/foolsm.conf.sample000066400000000000000000000053761455003704000160250ustar00rootroot00000000000000# # (C) 2009 Mika Ilmaranta # # License: GPLv2 # # # Debug level: 0 .. 8 are normal, 9 gives lots of stuff and 100 doesn't # bother to detach # #debug=10 #debug=9 debug=8 # # Defaults for the connection entries # defaults { name=defaults checkip=127.0.0.1 eventscript=/usr/libexec/foolsm/default_script max_packet_loss=15 max_successive_pkts_lost=7 min_packet_loss=5 min_successive_pkts_rcvd=10 interval_ms=1000 timeout_ms=1000 warn_email=root check_arp=0 sourceip= # if using ping probes for monitoring only then defaults should # not define a default device for packets to autodiscover their path # to destination # device=eth0 # use system default ttl ttl=0 # assume initial up state at foolsm startup (1 = up, 0 = down, 2 = unknown (default)) # status=1 } # # Some example connections # NOTE: don't use any white space in name ... # # connection { # name=connection-1 # checkip=127.108.68.69 # eventscript=/usr/libexec/foolsm/conn1 # max_packet_loss=15 # max_successive_pkts_lost=7 # min_packet_loss=5 # min_successive_pkts_rcvd=10 # interval_ms=1000 # timeout_ms=1000 # warn_email=root1@some.tld # check_arp=0 # sourceip= # device= # ttl=64 # } # connection { # name=connection-2 # checkip=127.108.68.65 # eventscript=/usr/libexec/foolsm/conn2 # max_packet_loss=15 # max_successive_pkts_lost=7 # min_packet_loss=5 # min_successive_pkts_rcvd=10 # interval_ms=1000 # timeout_ms=1000 # warn_email=root2@some.tld # check_arp=0 # sourceip= # device= # ttl=64 # } # connection { # name=connection-3 # checkip=127.108.68.68 # eventscript=/usr/libexec/foolsm/conn3 # max_packet_loss=15 # max_successive_pkts_lost=7 # min_packet_loss=5 # min_successive_pkts_rcvd=10 # interval_ms=1000 # timeout_ms=1000 # warn_email=root3@some.tld # check_arp=0 # sourceip= # device= # ttl=64 # } # connection { # name=connection-4 # checkip=127.108.68.75 # } # # Arping example # # connection { # name=connection-5 # checkip=127.108.68.71 # check_arp=1 # # if the remote end is not behind the defaults device # # then you have to set this # device=eth0 # # setting source ip is not mandatory # sourceip=127.108.68.68 # # use system default ttl # ttl=0 # } # # Group example # # connection { # name=conn-a # checkip=127.108.68.99 # eventscript= # } # # connection { # name=conn-b # checkip=127.108.68.100 # eventscript= # } # # group { # name=conn-group-a # eventscript=/usr/libexec/lsm/default_script # warn_email=root@some.domain.tld # # device name reported to the scripts # device=eth0 # # logic between member connetion statuses # # logic=0 == or # # logic=1 == and # logic=0 # member-connection=conn-a # member-connection=conn-b # } lsm-1.0.21/foolsm.h000066400000000000000000000027341455003704000140420ustar00rootroot00000000000000/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #ifndef __FOOLSM_H__ #define __FOOLSM_H__ #include /* for struct sockaddr_in */ #include /* for struct sockadd_ll */ #include /* for struct icmp6_filter */ #include "defs.h" typedef struct sentpkt { unsigned short seq; struct timeval sent_time; struct timeval replied_time; unsigned long rtt; struct { unsigned replied:1; unsigned timeout:1; unsigned waiting:1; unsigned used:1; unsigned error:1; } flags; } SENTPKT; typedef struct target { unsigned short id; /* target id */ unsigned short seq; unsigned short downseq; unsigned short downseqreported; struct timeval down_timestamp; struct sockaddr_in src_addr; struct sockaddr_in dst_addr; struct sockaddr_in6 src_addr6; struct sockaddr_in6 dst_addr6; struct sockaddr_ll me; /* arping only */ struct sockaddr_ll he; /* arping only */ struct in_addr src; struct in_addr dst; struct in6_addr src6; struct in6_addr dst6; unsigned long num_sent; struct timeval last_send_time; STATUS status; int sock; unsigned char cmsgbuf[4096]; int cmsglen; struct icmp6_filter filter; SENTPKT sentpkts[FOLLOWED_PKTS]; int timeout; int timeout_max; int replied; int waiting; int reply_late; int used; int consecutive_waiting; int consecutive_missing; int consecutive_missing_max; int consecutive_rcvd; long min_rtt; long avg_rtt; long max_rtt; int status_change; } TARGET; #endif /* EOF */ lsm-1.0.21/foolsm.init000066400000000000000000000035521455003704000145550ustar00rootroot00000000000000#!/bin/sh # # /etc/init.d/foolsm # # This shellscript takes care of starting and stopping foolsm. # # chkconfig: - 79 31 # description: Foolsm, Link Status Monitor # ### BEGIN INIT INFO # Provides: foolsm # Required-Start: $network $syslog # Required-Stop: # Default-Start: # Default-Stop: 0 1 6 # Short-Description: Foolsm - link status monitor # Description: Foolsm is the link status monitor # Foolsm can ping multiple targets and when up or down event happens # it will execute user configured external script so it can be used # as poor man's routing protocol. ### END INIT INFO # Source function library. . /etc/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 CONFIGFILE="/etc/foolsm/foolsm.conf" PIDFILE="/var/run/foolsm.pid" [ -f /etc/sysconfig/foolsm ] && . /etc/sysconfig/foolsm [ -x /usr/sbin/foolsm ] || exit 0 RETVAL=0 start() { echo -n $"Starting foolsm: " daemon --pidfile=${PIDFILE} /usr/sbin/foolsm --config $CONFIGFILE --pidfile $PIDFILE RETVAL=$? /bin/usleep 10000 echo [ $RETVAL = 0 ] && touch /var/lock/subsys/foolsm return $RETVAL } stop() { echo -n $"Stopping foolsm: " killproc /usr/sbin/foolsm RETVAL=$? echo [ $RETVAL = 0 ] && rm -f /var/lock/subsys/foolsm return $RETVAL } restart() { stop start } reload() { echo -n $"Reloading foolsm: " killproc -p ${PIDFILE} /usr/sbin/foolsm -HUP RETVAL=$? echo return $RETVAL } # See how we were called. case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; condrestart) [ -f /var/lock/subsys/foolsm ] && restart ;; status) status -p ${PIDFILE} foolsm RETVAL=$? ;; *) echo "Usage: foolsm {start|stop|restart|condrestart|status}" RETVAL=2 esac exit $RETVAL lsm-1.0.21/foolsm.service000066400000000000000000000005551455003704000152520ustar00rootroot00000000000000[Unit] Description=Foolsm is the link status monitor Documentation=http://lsm.foobar.fi/ Wants=network-online.target After=network-online.target shorewall.service shorewall6.service [Service] Type=simple ExecStart=/usr/sbin/foolsm --config /etc/foolsm/foolsm.conf --no-fork ExecReload=/bin/kill -HUP $MAINPID Restart=on-abort [Install] WantedBy=multi-user.target lsm-1.0.21/foolsm.spec000066400000000000000000001007141455003704000145420ustar00rootroot00000000000000#define devel 1 Summary: The Foobar Link Status Monitor Name: foolsm Version: 1.0.21 Release: 1%{?dist} License: GPLv2 URL: https://lsm.foobar.fi/ Source0: https://lsm.foobar.fi/download/%{name}-%{version}.tar.gz %if 0%{?devel} BuildRequires: ElectricFence %endif BuildRequires: systemd %{?systemd_requires} Requires: mailx Requires: hostname %description Foolsm is the Foobar Link Status Monitor. Foolsm can ping multiple targets and when up or down event happens it will execute user configured external script so it can be used as poor man's routing protocol. %prep %setup -q %build EFENCE= %if 0%{?devel} # Disable -O2 temporarily RPM_OPT_FLAGS="$(echo "%{optflags}" | sed 's/-O.\ / /')" EFENCE="-lefence" %endif make PREFIX=%{_prefix} CFLAGS="$RPM_OPT_FLAGS" LDFLAGS=${EFENCE} %{?_smp_mflags} %install mkdir -p %{buildroot}%{_sysconfdir}/foolsm mkdir -p %{buildroot}%{_sbindir} mkdir -p %{buildroot}%{_libexecdir}/foolsm mkdir -p %{buildroot}%{_sharedstatedir}/foolsm mkdir -p %{buildroot}%{_datadir}/munin/plugins/ install -m0755 foolsm %{buildroot}%{_sbindir} install -m0644 foolsm.conf %{buildroot}%{_sysconfdir}/foolsm install -m0755 default_script group_script shorewall_script shorewall6_script \ %{buildroot}%{_libexecdir}/foolsm/ install -m0755 foolsm_ %{buildroot}%{_datadir}/munin/plugins/ mkdir -p %{buildroot}%{_unitdir} install -m0644 foolsm.service %{buildroot}%{_unitdir}/ %post %systemd_post foolsm.service %preun %systemd_preun foolsm.service %postun %systemd_postun_with_restart foolsm.service %files %doc README foolsm.conf.sample default_script.sample rsyslog-foolsm.conf.sample %license LICENSE %{_unitdir}/foolsm.service %dir %{_libexecdir}/foolsm %{_libexecdir}/foolsm/default_script %{_libexecdir}/foolsm/group_script %{_libexecdir}/foolsm/shorewall_script %{_libexecdir}/foolsm/shorewall6_script %dir %{_sysconfdir}/foolsm %config(noreplace) %{_sysconfdir}/foolsm/foolsm.conf %{_sbindir}/foolsm %dir %{_sharedstatedir}/foolsm %dir %{_datadir}/munin %dir %{_datadir}/munin/plugins %{_datadir}/munin/plugins/foolsm_ %changelog * Wed Aug 18 2021 Mika Ilmaranta - 1.0.21-1 - ensure sane value for connection min_rtt * Wed Aug 18 2021 Mika Ilmaranta - 1.0.20-1 - fixed comparison logic error * Mon Aug 16 2021 Mika Ilmaranta - 1.0.19-1 - added min & max rtt. making them the last parameters scripts get to not break old user scripts * Mon Jul 19 2021 Mika Ilmaranta - 1.0.18-1 - fixed compile time warnings with el8 compiler * Mon Jul 19 2021 Tuomo Soini - 1.0.17-1 - Drop legacy stuff from spec * Mon Jul 19 2021 Tuomo Soini - 1.0.16-1 - Add hostname to default_script and group_script - Change date format in emails to be more readable - Add Requirement for hostname package - Cleanup spec file - Add LICENSE file * Wed Mar 31 2021 Mika Ilmaranta - 1.0.15-1 - foolsm_ munin plugin added * Mon May 4 2020 Mika Ilmaranta - 1.0.14-1 - log recvfrom errors only on debug level >= 9 - foolsm.conf: debug level 100 doesn't control detaching from controlling terminal anymore, removed comment on that part * Fri Sep 20 2019 Mika Ilmaranta - 1.0.13-1 - dump_config: show group device - debian/changelog update - report group device also on down event to scripts * Fri Sep 20 2019 Mika Ilmaranta - 1.0.12-1 - report group device also to notify script * Fri Sep 20 2019 Mika Ilmaranta - 1.0.11-1 - added support for group to have device name * Tue Jul 25 2017 Mika Ilmaranta - 1.0.10-1 - debian patch from Roberto Suárez Soto * Sun Dec 18 2016 Mika Ilmaranta - 1.0.9-1 - README: fixed note about assumed start state * Fri Sep 9 2016 Mika Ilmaranta - 1.0.8-1 - foolsm.spec: triggers to move old lsm config for foolsm - fix forever loop in waitpid while loop * Wed Sep 7 2016 Mika Ilmaranta - 1.0.7-1 - project name changed to foolsm due to conflicting path(s) with libstoragemgmt * Wed Sep 7 2016 Mika Ilmaranta - 1.0.6-1 - patches from Andrew Timonin, better handling of waitpid, debug message fixes and ipv4 srcinfo handling * Fri May 13 2016 Mika Ilmaranta - 1.0.5-1 - patches from Lucas de Castro Borges for Debian - exept for POSIX compliancy scriptdir PREFIX/libexec/lsm * Tue Jan 26 2016 Mika Ilmaranta - 1.0.4-1 - For systemd start after shorewall otherwise shorewall_script may be executed too early * Fri Dec 4 2015 Mika Ilmaranta - 1.0.3-1 - recursive read_config reported errors many times * Fri Dec 4 2015 Mika Ilmaranta - 1.0.2-1 - call init_config in reload_config * Fri Dec 4 2015 Mika Ilmaranta - 1.0.1-1 - double free() fix? * Thu Nov 19 2015 Mika Ilmaranta - 1.0-1 - script API change. pass empty strings to scripts without converting to "-" or "NA". - include and -include now support patterns * Tue Nov 17 2015 Mika Ilmaranta - 0.195-1 - fix dynamic memory handling in sane values in code. * Mon Nov 16 2015 Mika Ilmaranta - 0.194-1 - set sane values in code. overridable in config defaults section as before. * Sat Oct 31 2015 Mika Ilmaranta - 0.193-1 - use full path for -included file * Fri Oct 30 2015 Mika Ilmaranta - 0.192-1 - default_script: run date after checks * Fri Oct 30 2015 Mika Ilmaranta - 0.191-1 - support for -include aka ignore include errors if file is missing * Fri Oct 23 2015 Mika Ilmaranta - 0.190-1 - fix groups_decide logic when group logic is 'or'. Thanks to Filippo Carletti for noticing there was a problem. * Mon Jun 1 2015 Mika Ilmaranta - 0.189-1 - update stats must not clear target used count * Mon Jun 1 2015 Mika Ilmaranta - 0.188-1 - moved target used slots book keeping to send function * Mon Jun 1 2015 Mika Ilmaranta - 0.187-1 - update stats after each ping round so that startup burst can use used slot count * Mon Jun 1 2015 Mika Ilmaranta - 0.186-1 - dump startup acceleration and startup burst config also - startup acceleration logic fix * Mon Jun 1 2015 Mika Ilmaranta - 0.185-1 - startup burst logic rewrite * Mon Jun 1 2015 Mika Ilmaranta - 0.184-1 - two separate config params for startup burst * Sun May 31 2015 Mika Ilmaranta - 0.183-1 - startup acceleration configurable - startup burst configurable * Sun May 31 2015 Tuomo Soini - 0.182-1 - add shorewall6_script and group_script - install scripts to /usr/libexec/lsm - add compatibility symlinks to /usr/share/lsm * Fri May 29 2015 Mika Ilmaranta - 0.181-1 - accelerate decision at startup, must have received at least one packet * Fri May 29 2015 Mika Ilmaranta - 0.180-1 - accelerate decision at startup * Tue Feb 3 2015 Mika Ilmaranta - 0.179-1 - lsm.service use correct path for the binary * Wed Jan 14 2015 Mika Ilmaranta - 0.178-1 - export status info to separate file - cleaned up compilation with different sets of NO_PLUGIN_EXPORT defines * Mon Jan 12 2015 Mika Ilmaranta - 0.177-1 - export connection statuses to plugin directory * Sun Jan 11 2015 Mika Ilmaranta - 0.176-1 - log status with other attributes (Luigi Iotti) * Sat Sep 13 2014 Mika Ilmaranta - 0.175-1 - if --no-fork don't write pid file * Sat Sep 13 2014 Mika Ilmaranta - 0.174-1 - systemd support * Wed Aug 6 2014 Mika Ilmaranta - 0.173-1 - fixed -v parameter * Wed Aug 6 2014 Mika Ilmaranta - 0.172-1 - better usage help - fixed bug in optarg use * Wed Aug 6 2014 Mika Ilmaranta - 0.171-1 - split source for pidfile processing - real cmdline argument processing with optarg - support no-daemon cmdline option for integration with systemd - debug level 100 no longer suppresses daemonization use the above mentioned cmdline option instead * Sun Feb 16 2014 Mika Ilmaranta - 0.170-1 - Makefile: debian frendlier install target * Sun Feb 16 2014 Mika Ilmaranta - 0.169-1 - Makefile: install target, not guaranteed to work - README: updated with INSTALL section * Fri Nov 22 2013 Mika Ilmaranta - 0.168-1 - zero timeout_max and consecutive_missing_max in dump_statuses if there is status_change to up state not in decide * Fri Nov 22 2013 Mika Ilmaranta - 0.167-1 - update timeout_max and consecutive_missing_max regardless of state * Fri Nov 22 2013 Mika Ilmaranta - 0.166-1 - added timeout_max * Fri Nov 22 2013 Mika Ilmaranta - 0.165-1 - drop max_successive_waiting, it delivers no additional info - renamed max_successive_missing to successive_missing_max * Fri Nov 22 2013 Mika Ilmaranta - 0.164-1 - added max_successive_waiting and max_successive_missing - cfg.debug controls now how much info is syslogged on dump_status debug >= 6, log calculated connection status debug >= 7, log connection probe statuses * Fri Aug 9 2013 Mika Ilmaranta - 0.163-1 - don't suppress all up notifies when unknown_up_notify is off * Thu Jul 25 2013 Mika Ilmaranta - 0.162-1 - option to skip executing notify script on unknown to up event for groups also * Thu Jul 25 2013 Mika Ilmaranta - 0.161-1 - added option to skip executing notify script on unknown to up event * Thu Jul 25 2013 Mika Ilmaranta - 0.160-1 - moved save/restore statuses to their own file from lsm.c * Wed Jul 24 2013 Mika Ilmaranta - 0.159-1 - updated shorewall_script with new parameters * Wed Jul 24 2013 Mika Ilmaranta - 0.158-1 - clean up munin titles * Tue Jul 23 2013 Mika Ilmaranta - 0.157-1 - use connection names in munin labels * Fri Jul 19 2013 Mika Ilmaranta - 0.156-1 - plugin_export: changed strlower to munin_src_name * Thu Jul 18 2013 Mika Ilmaranta - 0.155-1 - plugin_export: export statistics for munin * Wed Jul 17 2013 Mika Ilmaranta - 0.154-1 - fixed divide error * Wed Jul 17 2013 Mika Ilamranta - 0.153-1 - lsm.c: drop casts and keep avg_rtt in usec, divide by 1000.0 if needed as float * Wed Jul 17 2013 Mika Ilmaranta - 0.152-1 - lsm.c: cast avg_rtt for fork_exec * Wed Jul 17 2013 Mika Ilmaranta - 0.151-1 - prepare for exporting statistics to zabbix and munin * Wed Jul 10 2013 Mika Ilmaranta - 0.150-1 - lsm.c: change status to up directly from long_down * Tue Jul 9 2013 Mika Ilmaranta - 0.149-1 - default_script: added missing closing curly bracket * Mon Jul 8 2013 Mika Ilmaranta - 0.148-1 - pass event timestamp from lsm to scripts * Mon Jul 8 2013 Mika Ilmaranta - 0.147-1 - pass empty warn_email addr to script as hyphen - if script gets hyphen for email addr do not send mail * Thu Jul 4 2013 Mika Ilmaranta - 0.146-1 - long_down_to down -> long_down_to_down * Thu Jul 4 2013 Mika Ilmaranta - 0.145-1 - check also for nul-string email addr * Mon Jul 1 2013 Mika Ilmaranta - 0.144-1 - use enum for status - new config options for reporting only longer down time: - long_down_time = how many seconds down time is considered long - long_down_email = where to report long down time - long_down_notifyscript = script to use when reporting long down time - long_down_eventscript = script to use when reacting to long down time * Fri May 10 2013 Mika Ilmaranta - 0.143-1 - discard probed src addr if bind fails * Thu Mar 28 2013 Mika Ilmaranta - 0.142-1 - free exec_queue on exit * Thu Mar 28 2013 Mika Ilmaranta - 0.141-1 - make exec_queue_dump activate with -DDEBUG - rsyslog-lsm.conf.sample by Dimitar Angelov * Thu Mar 28 2013 Mika Ilmaranta - 0.140-1 - debug with exec_queue_dump * Thu Mar 28 2013 Mika Ilmaranta - 0.139-1 - forkexec.c: check that eq is set in exec_queue_process * Thu Mar 28 2013 Mika Ilmaranta - 0.138-1 - config.c: defaults.queue NULL * Thu Mar 28 2013 Mika Ilmaranta - 0.137-1 - exec queue, run event scripts sychronously * Thu Oct 11 2012 Mika Ilmaranta - 0.136-1 - lsm.c: clean up debugging and add check that reply addr matches original destination addr * Thu Oct 11 2012 Mika Ilmaranta - 0.135-1 - lsm.c: more debugging when pdp->id is out of range * Thu Oct 11 2012 Mika Ilmaranta - 0.134-1 - lsm.c: do bounds check on pdp->id before use as ctable index * Mon Aug 13 2012 Mika Ilmaranta - 0.133-1 - lsm.c: rethought group up/down logic. (original was wrong up event on transition to unknown state and 0.132 fix reported both up and down events) * Mon Aug 13 2012 Mika Ilmaranta - 0.132-1 - default_script: better english for timeout packets - config.c: recognise status for group, use default start status for group - lsm.c: report group prevstate textually * Sat May 12 2012 Mika Ilmaranta - 0.131-1 - close socket on recvfrom fail * Tue May 8 2012 Mika Ilmaranta - 0.130-1 - initialize cmsgbuf and cmsglen in open_icmp_sock as socket may be closed and reopened * Sun May 6 2012 Mika Ilmaranta - 0.129-1 - close socket also when sendmsg fails * Sun May 6 2012 Mika Ilmaranta - 0.128-1 - cleanup debugging - don't SO_BINDTODEVICE for AF_INET6 when device is specified as it uses sendmsg instead of sendto * Sun May 6 2012 Mika Ilmaranta - 0.127-1 - debug sendmsg a little * Sun May 6 2012 Mika Ilmaranta - 0.126-1 - set v6 filter * Sun May 6 2012 Mika Ilmaranta - 0.125-1 - handle v6 device setting like ping6 does * Sat May 5 2012 Mika Ilmaranta - 0.124-1 - v6 sequence now correct? - set v6 sockopts also if ttl is not set * Thu Apr 19 2012 Mika Ilmaranta - 0.123-1 - removed unnesessary debugging - v6 src ip addr autodiscovery works now * Thu Apr 19 2012 Mika Ilmaranta - 0.122-1 - debug memcpy t->src6 results * Thu Apr 19 2012 Mika Ilmaranta - 0.121-1 - check t->src6 contents * Thu Apr 19 2012 Mika Ilmaranta - 0.120-1 - debug v6 getsockname unconditionally * Thu Apr 19 2012 Mika Ilmaranta - 0.119-1 - debug getsockname for v6 conn * Thu Apr 19 2012 Mika Ilmaranta - 0.118-1 - no SO_DONTROUTE for v6 probe * Fri Apr 13 2012 Mika Ilmaranta - 0.117-1 - lsm.c: set more v6 socket options - TODO: check why probe_src_ip_addr doesn't work for v6 * Sat Mar 10 2012 Mika Ilmaranta - 0.116-2 - fix forkexec format str * Sat Mar 10 2012 Mika Ilmaranta - 0.116-1 - report prevstate as string up/down/unknown * Wed Mar 7 2012 Tuomo Soini - 0.115-2 - fix %%postun not to fail * Wed Dec 21 2011 Mika Ilmaranta - 0.115-1 - forkexec.c: remove space after func name - lsm.c: free_config_data: don't close t->sock if it is -1 * Wed Dec 21 2011 Mika Ilmaranta - 0.114-1 - moved time calculation functions to their own files * Wed Dec 21 2011 Mika Ilmaranta - 0.113-1 - decode all icmp and icmp6 types and codes * Tue Dec 20 2011 Mika Ilmaranta - 0.112-1 - Andrew Beverley's signal child handler - made static functions static * Tue Dec 20 2011 Mika Ilmaranta - 0.111-1 - moved children handling from forkexec to main loop * Tue Dec 20 2011 Mika Ilmaranta - 0.110-1 - lsm.c: use t->dst6 in v6 probe not cur->dstinfo * Tue Dec 20 2011 Mika Ilmaranta - 0.109-1 - rebuild with efence * Tue Dec 20 2011 Mika Ilmaranta - 0.108-1 - ipv6 support seems now stable enough * Tue Dec 20 2011 Mika Ilmaranta - 0.107-1 - lsm.c: debug probe_src_ip_addr call * Tue Dec 20 2011 Mika Ilmaranta - 0.106-1 - lsm.c: debug open_icmp_sock socket call removed - lsm.c: debug setting v6 src addr * Tue Dec 20 2011 Mika Ilmaranta - 0.105-1 - lsm.c: debug open_icmp_sock socket call * Tue Dec 20 2011 Mika Ilmaranta - 0.104-1 - lsm.c: more sin6_family fixes * Tue Dec 20 2011 Mika Ilmaranta - 0.103-1 - lsm.c: set sin6_family and show also v4 addr in debug * Tue Dec 20 2011 Mika Ilmaranta - 0.102-1 - lsm.c: debug reply pkts more * Mon Dec 19 2011 Mika Ilmaranta - 0.101-1 - lsm.c: fix inet_pton parameters * Mon Dec 19 2011 Mika Ilmaranta - 0.100-1 - lsm.c: fail if device can not be bound to by Andrew Beverley - lsm.c: bind also to ipv6 sourceip * Mon Dec 19 2011 Mika Ilmaranta - 0.99-1 - lsm.c: fix ping6 sendto params * Mon Dec 19 2011 Mika Ilmaranta - 0.98-1 - initial ipv6 support * Mon Dec 19 2011 Mika Ilmaranta - 0.97-1 - include version information in directory name inside tar pkg * Mon Dec 19 2011 Mika Ilmaranta - 0.96-1 - lsm.c: initialize last decision outside main loop. fixes decide call interval. patch by Andrew Beverley - config.c: debugging information added for unmatched config option by Andrew Beverley. strcpy changed to memmove in strip leading space - lsm.c: show correct usage - config.c: clean up config file lines harder before parsing * Fri Dec 9 2011 Mika Ilmaranta - 0.95-1 - shorewall_script: removed email notification, use it as eventscript and default_script as notifyscript for example * Fri Dec 9 2011 Mika Ilmaranta - 0.94-1 - added support for worker script (= eventscript) and notify script (= notifyscript) differentiation, both are called with same parameters. new script parameter previous state * Thu Dec 8 2011 Mika Ilmaranta - 0.93-1 - for the following two patches thanks to Pablo Gomez - lsm.c: use sourceip from config for icmp pkts if set - lsm.c,config.c: new default state is unknown, which can be overridden in config. this allows lsm to run event script "on startup" after connection statuses are discovered. same rules apply as for actual events. * Thu Oct 20 2011 Mika Ilmaranta > - 0.92-1 - report same seq status only once * Thu Oct 20 2011 Mika Ilmaranta - 0.91-1 - dump conn status to syslog every maxseq when status is down only if no status change * Thu Oct 20 2011 Mika Ilmaranta - 0.90-1 - dump conn status to syslog every maxseq when status is down * Tue Sep 27 2011 Mika Ilmaranta - 0.89-1 - added comment to shrewall_script about shorewall version requirement * Tue Sep 27 2011 Mika Ilmaranta - 0.88-1 - added shorewall_script * Fri Jul 15 2011 Mika Ilmaranta - 0.87-1 - production setting for compilation (no efence and use optimization) * Wed Jun 22 2011 Mika Ilmaranta - 0.86-1 - protect FD_ISSET also from closed socket * Wed Jun 22 2011 Mika Ilmaranta - 0.85-1 - compile with ElectricFence * Wed Jun 22 2011 Mika Ilmaranta - 0.84-1 - revert to v0.64 base - Makefile: merge v0.83 changeset - lsm.spec: v0.83 spec - config: removed reopen_on_enodev and added patch for double free - lsm.c: removed reopen_on_enodev handling - lsm.conf: removed reopen_on_enodev - added cksum files - lsm.c: use external cksum - v0.83 forkexec, globals and signal_handler - lsm.c: separate defs.h, merged fixes from v0.83 - lsm.c: close socket on fail and reopen just before next ping - spec: version and changelog * Wed Jun 22 2011 Mika Ilmaranta - 0.83-1 - check that device names match * Wed Jun 22 2011 Mika Ilmaranta - 0.82-1 - compile with ElectricFence depending on devel define * Wed Jun 22 2011 Mika Ilmaranta - 0.81-1 - check arp header differently * Tue Jun 21 2011 Mika Ilmaranta - 0.80-1 - further debugging of disappearing arp replies * Tue Jun 21 2011 Mika Ilmaranta - 0.79-1 - try to find out where arp replies vanish * Tue Jun 21 2011 Mika Ilmaranta - 0.78-1 - config.c: fix double free on warn_email and other group parameters * Tue Jun 21 2011 Mika Ilmaranta - 0.77-1 - fix BuildRequires for -lefence * Tue Jun 21 2011 Mika Ilmaranta - 0.76-1 - compile with -lefence * Tue Jun 21 2011 Mika Ilmaranta - 0.75-1 - check packet from addr to determine which connection it belongs to * Tue Jun 21 2011 Mika Ilmaranta - 0.74-1 - moved rest of io functions to io.c - io.c: split icmp and arp reply handling - io.c: differentiate ping_rcv error logging on find_interface use * Tue Jun 21 2011 Mika Ilmaranta - 0.73-1 - bind SIGUSR2 to signal_handler * Tue Jun 21 2011 Mika Ilmaranta - 0.72-1 - dump interface list on SIGUSR2 * Mon Jun 20 2011 Mika Ilmaranta - 0.71-1 - io.c: due to function splitting add missing gettimeofday call in icmp_send * Mon Jun 20 2011 Mika Ilmaranta - 0.70-1 - timeval_diff_[lt,gt] fix diff_usec calculation * Mon Jun 20 2011 Mika Ilmaranta - 0.69-1 - removed sock from target structure - added interface handling to interface.c and made other parts use it * Fri Jun 17 2011 Mika Ilmaranta - 0.68-1 - broke timeval_diff_cmp to two functions - globals.c: check for unset prog - lsm.c: removed handle_odd_icmp function as it did nothing - interface.c: made probe for src ip address function probe_addresses * Fri Jun 17 2011 Mika Ilmaranta - 0.67-1 - moved global variable handling to its own block - moved decision functions to their own block - moved signal_handler to its own block - moved ping function to its own file - lsm.c: reset reload_cfg after reloading config, not before - Makefile: clean also ~ files starting with dot * Fri Jun 17 2011 Mika Ilmaranta - 0.66-1 - moved structure definitions to lsm.h - lsm.c: moved socket opening functionality to interface.c - Makefile: use Makefile.depend - .gitignore: ignore Makefile.depend, obj files and lsm binary * Wed Jun 15 2011 Mika Ilmaranta - 0.65-1 - started rewrite - moved forkexec function and defines to their own files - added interface handling src files - moved cksum function to its own file - moved timeval functions to their own files * Fri Dec 31 2010 Mika Ilmaranta - 0.64-1 - fill up target struct src address for ping also do it at startup (should this be done every time we send ping packet?) * Fri Dec 31 2010 Mika Ilmaranta - 0.63-1 - Added src ip to script parameters src ip paramater is the last one so that every single old config doesn't have to be rewritten. * Fri Dec 10 2010 Mika Ilmaranta - 0.62-1 - check for valid ip-address in checkip parameter * Fri Dec 10 2010 Mika Ilmaranta - 0.61-1 - changed lsm.c init_config to init_config_data - added init_config to config.c which is then called before read_config - added a warning when checkip is not set * Sat Oct 9 2010 Mika Ilmaranta - 0.60-1 - fix recursive read_config use. set default values only once. - use cfg.debug only after config is read - added default_script.sample * Mon Sep 27 2010 Mika Ilmaranta - 0.59-1 - remember connection statuses after config reload * Mon Sep 27 2010 Mika Ilmaranta - 0.58-1 - reopen_on_enodev support. set this to 1 so lsm will try to reopen ping device when it encounters ENODEV error when sending ping packet * Sun Sep 19 2010 Mika Ilmaranta - 0.57-1 - added defaults status to give initial assumption of the connection status - added config status for connections. is it assumed down = 0 or up = 1 at lsm start * Sat Sep 18 2010 Mika Ilmaranta - 0.56-1 - fixed rpmlint-v0.91 warnings. * Sat Apr 24 2010 Mika Ilmaranta - 0.53-1 - added error checking to ftruncate and write so that mock build doesn't complain - introduced timeval_diff_cmp function as it seems quite problematic to really compare times in usec since epoch using integer values. especially 32bit systems were seeing 99% CPU loads because of this. * Thu Apr 22 2010 Mika Ilmaranta - 0.52-1 - initialize all struct timeval structures to tv_sec = 0 and tv_usec = 0 - added use of error flag to arping sending * Fri Mar 5 2010 Mika Ilmaranta - 0.51-1 - fix avg rtt calculation comment - show in the default mail template avg rtt unit [usec] - in syslog avg rtt is reported in [msec] as of v0.50 like ping does * Fri Mar 5 2010 Mika Ilmaranta - 0.50-1 - report rtt with three decimals accuracy * Thu Mar 4 2010 Mika Ilmaranta - 0.49-1 - count average rtt in milliseconds not in microseconds * Thu Mar 4 2010 Mika Ilmaranta - 0.48-1 - don't count timeouted late replies in cons rcvd * Thu Mar 4 2010 Mika Ilmaranta - 0.47-1 - added some missing fields to lsm.init * Thu Mar 4 2010 Mika Ilmaranta - 0.46-1 - dump all statuses only if requested by SIGUSR1 - otherwise dump only connection status data of connection whose status changed * Wed Mar 3 2010 Mika Ilmaranta - 0.45-1 - use LOG_PID openlog option * Thu Dec 17 2009 Mika Ilmaranta - 0.44-1 - added some parameter sanity checking. if max_packet_loss <= min_packet_loss then there can be a flip-flop effect and many many and still a few reports mailed to warn_email address. * Mon Nov 9 2009 Mika Ilmaranta - 0.43-1 - convert all tabs to spaces before processing config line * Sun Sep 27 2009 Mika Ilmaranta - 0.42-1 - changed action script to event script to follow suite with config * Sun Sep 27 2009 Mika Ilmaranta - 0.41-1 - added action_script_check() to check for valid action script * Wed Sep 2 2009 Mika Ilmaranta - 0.40-1 - lseek to start of pid file and ftruncate it to zero size before writing our new pid. this prevents us having two pids in the file if previous lsm crashed or exited and did not remove the file (fcntl lock is cleared by kernel as process dies "prematurely"). * Mon Jun 29 2009 Mika Ilmaranta - 0.39-1 - set close on exec flag for fds and sockets - call closelog() within forked child before exec * Thu Jun 18 2009 Mika Ilmaranta - 0.38-1 - require mailx for /bin/mail as default_script uses it * Thu Jun 18 2009 Mika Ilmaranta - 0.37-1 - when dumping config log also group's warn_email and logic - set last previous last group members next to the new last member * Thu Jun 18 2009 Mika Ilmaranta - 0.36-1 - apply sane defaults to group parameters * Thu Jun 4 2009 Mika Ilmaranta - 0.35-1 - only log sendto errors with debug >= 9 * Thu Jun 4 2009 Mika Ilmaranta - 0.34-1 - better sendto error ignore fix - changed syslog calls for Mandrake * Thu Jun 4 2009 Mika Ilmaranta - 0.33-1 - ping_send: don't care about sendto errors * Wed Apr 29 2009 Mika Ilmaranta - 0.32-1 - split long debug explanation line in lsm.conf * Wed Apr 29 2009 Mika Ilmaranta - 0.31-1 - left only defaults in lsm.conf and moved examples to lsm.conf.sample * Tue Apr 28 2009 Mika Ilmaranta - 0.30-1 - timeval_diff calculation order change to prevent long overflow. nobody has encoutered that but just to be sure - use default ttl=0 which uses system default ttl * Sat Apr 18 2009 Mika Ilmaranta - 0.29-1 - Added decision making section to README written by Dean Takemori he suggested to include it in lsm.conf but I thought it was long enough already * Sat Apr 18 2009 Mika Ilmaranta - 0.28-1 - Tom Eastep's fix for last_sent_time initialization - added time stamp to default_script mail body * Fri Apr 10 2009 Mika Ilmaranta - 0.27-1 - added checks for missing group members * Fri Apr 10 2009 Mika Ilmaranta - 0.26-1 - added connection grouping * Thu Apr 9 2009 Mika Ilmaranta - 0.25-1 - gettimeofday failure patch from Dean Takemori * Sun Apr 5 2009 Mika Ilmaranta - 0.24-1 - add support for SIGHUP to reload config * Tue Mar 24 2009 Mika Ilmaranta - 0.23-1 - changed ping packets to use own socket for each target. looks like setsockopt SO_BINDTOINTERFACE is not reversible and according to documentation I found using it multiple times may lead to unpredicted results due to kernel caching. * Tue Mar 24 2009 Mika Ilmaranta - 0.22-1 - changed indentation to tabs * Mon Mar 16 2009 Mika Ilmaranta - 0.21-1 - added LSM: to default_script mail subject * Tue Mar 10 2009 Mika Ilmaranta - 0.20-1 - handle ENODEV as if the ping packet was sent, but do barf to syslog about it. this will eventually cause a down event. added an error flag which is set if sendto returns with value < 0. - dump connection statuses to syslog when up/down-event happens. - moved status dump's "header" -line above the pkt status bits. - added error flag dumping. * Fri Mar 6 2009 Mika Ilmaranta - 0.19-1 - fix pid file write order * Thu Mar 5 2009 Mika Ilmaranta - 0.18-1 - added pid file handling * Thu Mar 5 2009 Mika Ilmaranta - 0.17-1 - rebuild because of mystical i386 build problems * Wed Mar 4 2009 Mika Ilmaranta - 0.16-1 - pass only LANG, PATH and TERM environment variables to scripts * Wed Mar 4 2009 Mika Ilmaranta - 0.15-1 - fixed all rpmlint errors reported in binary pkg which means that default_script is moved to /usr/share/lsm * Wed Mar 4 2009 Mika Ilmaranta - 0.14-1 - added ipv6 support to TODO list - fixed rpmlint errors in lsm.spec * Tue Feb 24 2009 Mika Ilmaranta - 0.13-1 - typos: lisence -> license - mention SIGUSR1 behaviour in README - added a check for ENODEV for ping packets * Fri Feb 20 2009 Mika Ilmaranta - 0.12-1 - don't define device= in defaults * Wed Feb 18 2009 Mika Ilmaranta - 0.11-1 - if device is not specified return NA * Wed Feb 18 2009 Mika Ilmaranta - 0.10-1 - added device to script parameters - first try on binding ping packets to device * Thu Feb 12 2009 Mika Ilmaranta - 0.9-1 - fixed rtt comments in default_script * Thu Feb 12 2009 Mika Ilmaranta - 0.8-1 - init script reload fix * Sat Feb 7 2009 Mika Ilmaranta - 0.7-1 - fixed typos - init script reload was missing * Sun Feb 1 2009 Mika Ilmaranta - 0.6-1 - fixed comments and readme to correspond current status * Sun Feb 1 2009 Mika Ilmaranta - 0.6-1 - now each target has its own ttl setting * Sat Jan 31 2009 Mika Ilmaranta - 0.5-8 - check for no targets specified in conf * Sat Jan 31 2009 Mika Ilmaranta - 0.5-7 - all except DEBUG is now syslogged * Sat Jan 31 2009 Mika Ilmaranta - 0.5-4 - added ttl setting handling for ping packets. currently all ping monitored links share a common ttl value which is taken from the first config entry's ttl value. - when SIGUSR1 is received lsm dumps current packet info to syslog ... * Sat Jan 31 2009 Mika Ilmaranta - 0.5-3 - changed avg rtt calculation so that only replied packets' rtt is counted * Sat Jan 31 2009 Mika Ilmaranta - 0.5-2 - Added a ping reply packet min size check * Fri Jan 30 2009 Mika Ilmaranta - 0.5-1 - started adding support for arp check which is actually using arp packets rather than ping packets in case your gw administrators have blocked ping * Thu Jan 29 2009 Mika Ilmaranta - 0.4-1 - Initial build #EOF lsm-1.0.21/foolsm_000077500000000000000000000011261455003704000137500ustar00rootroot00000000000000#!/bin/sh # # Wildcard-script to monitor foolsm. # # Parameters: # # config # autoconf # suggest # #%# family=auto #%# capabilities=autoconf suggest if [ "$1" = "autoconf" ]; then if [ ! -d /var/lib/foolsm ] ; then echo "no (foolsm not found)" exit 0 fi echo "yes" exit 0 fi if [ "$1" = "suggest" ]; then for file in /var/lib/foolsm/config.*; do echo ${file#/var/lib/foolsm/config.} done exit 0 fi GRAPH=$(basename $0 | sed 's/^foolsm_//') if [ "$1" = "config" ]; then cat /var/lib/foolsm/config.${GRAPH} exit 0 fi cat /var/lib/foolsm/status.${GRAPH} lsm-1.0.21/forkexec.c000066400000000000000000000210431455003704000143360ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "forkexec.h" static void sigchld_hdl(int sig); typedef struct exec_queue { pid_t pid; char **argv; char **envp; struct exec_queue *next; } EXEC_QUEUE; typedef struct exec_queues { char *name; EXEC_QUEUE *first; EXEC_QUEUE *last; struct exec_queues *next; } EXEC_QUEUES; static EXEC_QUEUES *exec_queues_first = NULL; static EXEC_QUEUES *exec_queues_last = NULL; pid_t forkexec(char **argv, char **envp) { pid_t pid; #if defined(DEBUG) int i; #endif if((pid = fork()) == -1) { syslog(LOG_ERR, "%s: %s: %d: fork() failed \"%s\"", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return(0); } if(pid) { /* parent */ if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: child process forked with pid: %d", __FILE__, __FUNCTION__, __LINE__, pid); return(pid); } /* child */ closelog(); /* openlog doesn't return a fd to set close on exec */ #if defined(DEBUG) for(i = 0; argv[i]; i++) { fprintf(stderr, "argv[%d] = \"%s\"\n", i, argv[i]); } for(i = 0; envp[i]; i++) { fprintf(stderr, "envp[%d] = \"%s\"\n", i, envp[i]); } #endif execve(argv[0], argv, envp); syslog(LOG_ERR, "%s: %s: %d: child process failed to execute external command", __FILE__, __FUNCTION__, __LINE__); exit(1); /* exec failed ... */ } /* Set up child signal handler to handle termination of children. This should be done once only for the main program. */ void create_sigchld_hdl(void) { struct sigaction act; memset (&act, 0, sizeof(act)); act.sa_handler = sigchld_hdl; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &act, 0)) { syslog(LOG_ERR, "%s: %s: %d: failed to set up child signal handler: %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return; } else { if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: successfully set up child signal handler", __FILE__, __FUNCTION__, __LINE__); } } /* SIGCHLD handler. Will be called for all children. */ static void sigchld_hdl(int sig) { /* Wait for the dead process. * We use a non-blocking call to be sure this signal handler will not * block if a child was cleaned up in another part of the program. */ int saved_errno = errno; int script_status; pid_t pid; while ((pid = waitpid(WAIT_ANY, &script_status, WNOHANG)) != 0) { if(pid == -1) { if(cfg.debug >= 9 && errno != ECHILD) syslog(LOG_ERR, "%s: %s: %d: waitpid failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); break; } else { if(cfg.debug >= 9 && WEXITSTATUS(script_status)) syslog(LOG_ERR, "%s: %s: %d: child script with pid %d exited with non null exit value %d", __FILE__, __FUNCTION__, __LINE__, pid, WEXITSTATUS(script_status)); else if(cfg.debug >= 9) syslog(LOG_ERR, "%s: %s: %d: child script with pid %d exited successfully", __FILE__, __FUNCTION__, __LINE__, pid); exec_queue_delete(pid); } } errno = saved_errno; } void exec_queue_add(char *queue, char **argv, char **envp) { EXEC_QUEUES *eqs; EXEC_QUEUE *eq; if(!exec_queues_first) { /* first queue */ if((eqs = malloc(sizeof(EXEC_QUEUES))) == NULL) { syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return; } eqs->name = strdup(queue); eqs->next = NULL; if((eq = malloc(sizeof(EXEC_QUEUE))) == NULL) { syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return; } eq->pid = 0; eq->argv = argv; eq->envp = envp; eq->next = NULL; eqs->first = eq; eqs->last = eq; exec_queues_first = eqs; exec_queues_last = eqs; } else { /* not first queue */ for(eqs = exec_queues_first; eqs; eqs = eqs->next) { if(!strcmp(eqs->name, queue)) { /* queue exists, add to it */ if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: found queue %s", __FILE__, __FUNCTION__, __LINE__, eqs->name); if((eq = malloc(sizeof(EXEC_QUEUE))) == NULL) { syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return; } eq->pid = 0; eq->argv = argv; eq->envp = envp; eq->next = NULL; if(!eqs->first) { /* empty queue */ eqs->first = eq; eqs->last = eq; } else { /* add after last */ eqs->last->next = eq; eqs->last = eq; } break; } } if(!eqs) { /* not found, create a new queue and add to it */ if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: queue %s not found adding new queue", __FILE__, __FUNCTION__, __LINE__, queue); if((eqs = malloc(sizeof(EXEC_QUEUES))) == NULL) { syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return; } eqs->name = strdup(queue); eqs->next = NULL; exec_queues_last->next = eqs; exec_queues_last = eqs; if((eq = malloc(sizeof(EXEC_QUEUE))) == NULL) { syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); return; } eq->pid = 0; eq->argv = argv; eq->envp = envp; eq->next = NULL; eqs->first = eq; eqs->last = eq; } } return; } #if defined(DEBUG) void exec_queue_dump(void) { EXEC_QUEUES *eqs; EXEC_QUEUE *eq; int i; for(eqs = exec_queues_first; eqs; eqs = eqs->next) { syslog(LOG_INFO, "%s: %s: %d: eqs->name %s", __FILE__, __FUNCTION__, __LINE__, eqs->name); for(eq = eqs->first; eq; eq = eq->next) { syslog(LOG_INFO, "%s: %s: %d: eq->pid %d", __FILE__, __FUNCTION__, __LINE__, eq->pid); for(i = 0; eq->argv[i]; i++) { syslog(LOG_INFO, "%s: %s: %d: argv[%d] = %s", __FILE__, __FUNCTION__, __LINE__, i, eq->argv[i]); } } } } #endif void exec_queue_process(void) { EXEC_QUEUES *eqs; EXEC_QUEUE *eq; for(eqs = exec_queues_first; eqs; eqs = eqs->next) { eq = eqs->first; if(eq && eq->pid == 0) eq->pid = forkexec(eq->argv, eq->envp); } } void exec_queue_delete(pid_t pid) { EXEC_QUEUES *eqs; EXEC_QUEUE *eq; for(eqs = exec_queues_first; eqs; eqs = eqs->next) { EXEC_QUEUE *prev = NULL; for(eq = eqs->first; eq; eq = eq->next) { if(eq->pid == pid) { if(!prev) { /* this is first */ if(!eq->next) { /* last */ eqs->first = NULL; eqs->last = NULL; } else { /* not last */ eqs->first = eq->next; } } else { /* not first */ prev->next = eq->next; } exec_queue_argv_free(eq->argv); exec_queue_envp_free(eq->envp); free(eq); return; } prev = eq; } } if(cfg.debug >= 9) syslog(LOG_ERR, "%s: %s: %d: child pid %d not found", __FILE__, __FUNCTION__, __LINE__, pid); } void exec_queue_free(void) { EXEC_QUEUES *eqs; EXEC_QUEUE *eq; eqs = exec_queues_first; while(eqs) { EXEC_QUEUES *prev_eqs = eqs; eq = eqs->first; while(eq) { EXEC_QUEUE *prev_eq = eq; eq = eq->next; free(prev_eq); } eqs = eqs->next; free(prev_eqs); } exec_queues_first = NULL; exec_queues_last = NULL; } char **exec_queue_argv(char *fmt, ...) { va_list vl; char **argv; char *s; int fmt_cnt; char buf[BUFSIZ]; int i; s = fmt; fmt_cnt = 0; while(*s) { if(*s++ == '%') fmt_cnt++; } if((argv = malloc((fmt_cnt + 1) * sizeof(char *))) == NULL) { syslog(LOG_ERR, "%s: %s: failed to malloc %s", __FILE__, __FUNCTION__, strerror(errno)); return(NULL); } s = fmt; i = 0; va_start(vl, fmt); while(*s) { if(*s == '%') { s++; switch(*s++) { case 's': /* string */ argv[i] = strdup(va_arg(vl, char *)); i++; break; case 'd': /* int */ snprintf(buf, BUFSIZ - 1, "%d", va_arg(vl, int)); argv[i] = strdup(buf); i++; break; default: /* skip unknown directives */ break; } } else { s++; } } va_end(vl); argv[i] = NULL; return(argv); } void exec_queue_argv_free(char **argv) { int i; for(i = 0; argv[i]; i++) { free(argv[i]); } free(argv); } char **exec_queue_envp(void) { char **envp; char buf[BUFSIZ]; if((envp = malloc(4 * sizeof(char *))) == NULL) { syslog(LOG_ERR, "%s: %s: malloc failed %s", __FILE__, __FUNCTION__, strerror(errno)); return(NULL); } snprintf(buf, BUFSIZ - 1, "LANG=%s", getenv("LANG")); envp[0] = strdup(buf); snprintf(buf, BUFSIZ - 1, "PATH=%s", getenv("PATH")); envp[1] = strdup(buf); snprintf(buf, BUFSIZ - 1, "TERM=%s", getenv("TERM")); envp[2] = strdup(buf); envp[3] = NULL; return(envp); } void exec_queue_envp_free(char **envp) { int i; for(i = 0; envp[i]; i++) { free(envp[i]); } free(envp); } /* EOF */ lsm-1.0.21/forkexec.h000066400000000000000000000010731455003704000143440ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #ifndef __FORKEXEC_H__ #define __FORKEXEC_H__ pid_t forkexec(char **argv, char **envp); void create_sigchld_hdl(void); void exec_queue_add(char *queue, char **argv, char **envp); void exec_queue_process(void); char **exec_queue_argv(char *fmt, ...); void exec_queue_argv_free(char **argv); char **exec_queue_envp(void); void exec_queue_envp_free(char **envp); void exec_queue_delete(pid_t pid); void exec_queue_free(void); #if defined(DEBUG) void exec_queue_dump(void); #endif #endif /* EOF */ lsm-1.0.21/globals.c000066400000000000000000000031211455003704000141500ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #include #include #include "globals.h" #include "defs.h" #define FOOLSM_CONFIG_FILE ETCDIR "/foolsm.conf" static char *prog = NULL; static int cont = TRUE; static int dump = FALSE; static int ident = 0; static int reload_cfg = FALSE; static int dump_if_list = FALSE; static char *configfile = FOOLSM_CONFIG_FILE; static char *pidfile = "/var/run/foolsm.pid"; static int nodaemon = 0; static char *status_str[] = { "down", "up", "unknown", "long_down" }; void set_prog(char *val) { prog = val; } char *get_prog(void) { if(prog == NULL) { syslog(LOG_ERR, "%s: called with prog unset", __FUNCTION__); return("prog unset"); } return(prog); } void set_cont(const int val) { cont = val; } int get_cont(void) { return(cont); } void set_dump(const int val) { dump = val; } int get_dump(void) { return(dump); } void set_ident(const int val) { ident = val; } int get_ident(void) { return(ident); } void set_reload_cfg(const int val) { reload_cfg = val; } int get_reload_cfg(void) { return(reload_cfg); } void set_dump_if_list(const int val) { dump_if_list = val; } int get_dump_if_list(void) { return(dump_if_list); } void set_configfile(char *val) { configfile = val; } char *get_configfile(void) { return(configfile); } void set_pidfile(char *val) { pidfile = val; } char *get_pidfile(void) { return(pidfile); } void set_nodaemon(const int val) { nodaemon = val; } int get_nodaemon(void) { return(nodaemon); } char *get_status_str(STATUS val) { return(status_str[val]); } /* EOF */ lsm-1.0.21/globals.h000066400000000000000000000012641455003704000141630ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #ifndef __GLOBALS_H__ #define __GLOBALS_H__ #include "config.h" void set_prog(char *val); char *get_prog(void); void set_cont(const int val); int get_cont(void); void set_dump(const int val); int get_dump(void); void set_ident(const int val); int get_ident(void); void set_reload_cfg(const int val); int get_reload_cfg(void); void set_dump_if_list(const int val); int get_dump_if_list(void); void set_configfile(char *val); char *get_configfile(void); void set_pidfile(char *val); char *get_pidfile(void); void set_nodaemon(const int val); int get_nodaemon(void); char *get_status_str(STATUS val); #endif /* EOF */ lsm-1.0.21/group_script000066400000000000000000000016721455003704000150350ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2012-2021 Tuomo Soini # # License: GPLv2 # # # event handling script for use with lsm groups # STATE=${1} NAME=${2} CHECKIP=${3} DEVICE=${4} WARN_EMAIL=${5} REPLIED=${6} WAITING=${7} TIMEOUT=${8} REPLY_LATE=${9} CONS_RCVD=${10} CONS_WAIT=${11} CONS_MISS=${12} AVG_RTT=${13} SRCIP=${14} PREVSTATE=${15} TIMESTAMP=${16} MIN_RTT=${17} MAX_RTT=${18} if [ -z "${WARN_EMAIL}" ] ; then exit 0 fi DATE=$(date +'%Y-%m-%d %H:%M:%S %Z' --date=@${TIMESTAMP}) HOSTNAME=$(hostname) cat < */ #include #include "icmp6_t.h" struct icmp6msg icmp6msgs[] = { /* error messages */ { 0, 0, "Reserved", "" }, { 1, 0, "Destination Unreachable", "no route to destination" }, { 1, 1, "Destination Unreachable", "communication with destination administratively prohibited" }, { 1, 2, "Destination Unreachable", "beyond scope of source address" }, { 1, 3, "Destination Unreachable", "address unreachable" }, { 1, 4, "Destination Unreachable", "port unreachable" }, { 1, 5, "Destination Unreachable", "source address failed ingress/egress policy" }, { 1, 6, "Destination Unreachable", "reject route to destination" }, { 1, 7, "Destination Unreachable", "Error in Source Routing Header" }, { 2, 0, "Packet Too Big", "" }, { 3, 0, "Time Exceeded", "hop limit exceeded in transit" }, { 3, 1, "Time Exceeded", "fragment reassembly time exceeded" }, { 4, 0, "Parameter Problem", "erroneous header field encountered" }, { 4, 1, "Parameter Problem", "unrecognized Next Header type encountered" }, { 4, 2, "Parameter Problem", "unrecognized IPv6 option encountered" }, { 100, 0, "Private experimentation", "" }, { 101, 0, "Private experimentation", "" }, { 127, 0, "Reserved for expansion of ICMPv6 error messages", "" }, /* infomational messages */ { 128, 0, "Echo Request", "" }, { 129, 0, "Echo Reply", "" }, { 130, 0, "Multicast Listener Query", "" }, { 131, 0, "Multicast Listener Report", "" }, { 132, 0, "Multicast Listener Done", "" }, { 133, 0, "Router Solicitation (NDP)", "" }, { 134, 0, "Router Advertisement (NDP)", "" }, { 135, 0, "Neighbor Solicitation (NDP)", "" }, { 136, 0, "Neighbor Advertisement (NDP)", "" }, { 137, 0, "Redirect Message (NDP)", "" }, { 138, 0, "Router Renumbering", "Router Renumbering Command" }, { 138, 1, "Router Renumbering", "Router Renumbering Result" }, { 138, 255, "Router Renumbering", "Sequence Number Reset" }, { 139, 0, "ICMP Node Information Query", "The Data field contains an IPv6 address which is the Subject of this Query." }, { 139, 1, "ICMP Node Information Query", "The Data field contains a name which is the Subject of this Query, or is empty, as in the case of a NOOP." }, { 139, 2, "ICMP Node Information Query", "The Data field contains an IPv4 address which is the Subject of this Query." }, { 140, 0, "ICMP Node Information Response", "A successful reply. The Reply Data field may or may not be empty." }, { 140, 1, "ICMP Node Information Response", "The Responder refuses to supply the answer. The Reply Data field will be empty." }, { 140, 2, "ICMP Node Information Response", "The Qtype of the Query is unknown to the Responder. The Reply Data field will be empty." }, { 141, 0, "Inverse Neighbor Discovery Solicitation Message", "" }, { 142, 0, "Inverse Neighbor Discovery Advertisement Message", "" }, { 143, 0, "Multicast Listener Discovery (MLDv2) reports (RFC 3810)", "" }, { 144, 0, "Home Agent Address Discovery Request Message", "" }, { 145, 0, "Home Agent Address Discovery Reply Message", "" }, { 146, 0, "Mobile Prefix Solicitation", "" }, { 147, 0, "Mobile Prefix Advertisement", "" }, { 148, 0, "Certification Path Solicitation (SEND)", "" }, { 149, 0, "Certification Path Advertisement (SEND)", "" }, { 151, 0, "Multicast Router Advertisement (MRD)", "" }, { 152, 0, "Multicast Router Solicitation (MRD)", "" }, { 153, 0, "Multicast Router Termination (MRD)", "" }, { 200, 0, "Private experimentation", "" }, { 201, 0, "Private experimentation", "" }, { 255, 0, "Reserved for expansion of ICMPv6 informational messages", "" }, { 256, 256, "impossible combination", "impossible combination" } }; struct icmp6msg icmp6error = { 256, 256, "unknown", "unknown" }; struct icmp6msg *stricmp6(int type, int code) { int i; if(type > 255) return &icmp6error; for(i = 0; icmp6msgs[i].type <= type; i++) if(icmp6msgs[i].type == type && icmp6msgs[i].code == code) return(&(icmp6msgs[i])); for(i = 0; icmp6msgs[i].type <= type; i++) if(icmp6msgs[i].type == type) return(&(icmp6msgs[i])); return &icmp6error; } /* EOF */ lsm-1.0.21/icmp6_t.h000066400000000000000000000003631455003704000141000ustar00rootroot00000000000000/* (C) 2011 Mika Ilmaranta */ #ifndef __ICMP6_T_H__ #define __ICMP6_T_H__ struct icmp6msg { int type; int code; char *type_msg; char *code_msg; }; struct icmp6msg *stricmp6(int type, int code); #endif /* EOF */ lsm-1.0.21/icmp_t.c000066400000000000000000000242601455003704000140070ustar00rootroot00000000000000/* (C) 2009 Mika Ilmaranta License: GPLv2 */ #include #include "icmp_t.h" static struct icmpmsg icmpmsgs[] = { { 0, 0, "Echo Reply", "" }, { 1, 0, "Reserved", "" }, { 2, 0, "Reserved", "" }, { 3, 0, "Destination Unreachable", "Destination network unreachable" }, { 3, 1, "Destination Unreachable", "Destination host unreachable" }, { 3, 2, "Destination Unreachable", "Destination protocol unreachable" }, { 3, 3, "Destination Unreachable", "Destination port unreachable" }, { 3, 4, "Destination Unreachable", "Fragmentation required, and DF flag set" }, { 3, 5, "Destination Unreachable", "Source route failed" }, { 3, 6, "Destination Unreachable", "Destination network unknown" }, { 3, 7, "Destination Unreachable", "Destination host unknown" }, { 3, 8, "Destination Unreachable", "Source host isolated" }, { 3, 9, "Destination Unreachable", "Network administratively prohibited" }, { 3, 10, "Destination Unreachable", "Host administratively prohibited" }, { 3, 11, "Destination Unreachable", "Network unreachable for TOS" }, { 3, 12, "Destination Unreachable", "Host unreachable for TOS" }, { 3, 13, "Destination Unreachable", "Communication administratively prohibited" }, { 4, 0, "Source Quench", "Source quench (congestion control)" }, { 5, 0, "Redirect Message", "Redirect Datagram for the Network" }, { 5, 1, "Redirect Message", "Redirect Datagram for the Host" }, { 5, 2, "Redirect Message", "Redirect Datagram for the TOS & network" }, { 5, 3, "Redirect Message", "Redirect Datagram for the TOS & host" }, { 6, 0, "Alternate Host Address", "" }, { 7, 0, "Reserved", "" }, { 8, 0, "Echo Request", "Echo request (used to ping)" }, { 9, 0, "Router Advertisement", "Router Advertisement" }, { 10, 0, "Router Solicitation", "Router discovery/selection/solicitation" }, { 11, 0, "Time Exceeded", "TTL expired in transit" }, { 11, 1, "Time Exceeded", "Fragment reassembly time exceeded" }, { 12, 0, "Parameter Problem: Bad IP header", "Pointer indicates the error" }, { 12, 1, "Parameter Problem: Bad IP header", "Missing a required option" }, { 12, 2, "Parameter Problem: Bad IP header", "Bad length" }, { 13, 0, "Timestamp", "Timestamp" }, { 14, 0, "Timestamp Reply", "Timestamp reply" }, { 15, 0, "Information Request", "Information Request" }, { 16, 0, "Information Reply", "Information Reply" }, { 17, 0, "Address Mask Request", "Address Mask Request" }, { 18, 0, "Address Mask Reply", "Address Mask Reply" }, { 19, 0, "Reserved for security", "Reserved for security" }, { 20, 0, "Reserved for robustness experiment", "" }, { 21, 0, "Reserved for robustness experiment", "" }, { 22, 0, "Reserved for robustness experiment", "" }, { 23, 0, "Reserved for robustness experiment", "" }, { 24, 0, "Reserved for robustness experiment", "" }, { 25, 0, "Reserved for robustness experiment", "" }, { 26, 0, "Reserved for robustness experiment", "" }, { 27, 0, "Reserved for robustness experiment", "" }, { 28, 0, "Reserved for robustness experiment", "" }, { 29, 0, "Reserved for robustness experiment", "" }, { 30, 0, "Traceroute", "Information Request" }, { 31, 0, "Datagram Conversion Error", "" }, { 32, 0, "Mobile Host Redirect", "" }, { 33, 0, "Where-Are-You (originally meant for IPv6)", "" }, { 34, 0, "Here-I-Am (originally meant for IPv6)", "" }, { 35, 0, "Mobile Registration Request", "" }, { 36, 0, "Mobile Registration Reply", "" }, { 37, 0, "Domain Name Request", "" }, { 38, 0, "Domain Name Reply", "" }, { 39, 0, "SKIP Algorithm Discovery Protocol, Simple Key-Management for Internet Protocol", "" }, { 40, 0, "Photuris, Security failures", "" }, { 41, 0, "ICMP for experimental mobility protocols such as Seamoby [RFC4065]", "" }, { 42, 0, "Reserved", "" }, { 43, 0, "Reserved", "" }, { 44, 0, "Reserved", "" }, { 45, 0, "Reserved", "" }, { 46, 0, "Reserved", "" }, { 47, 0, "Reserved", "" }, { 48, 0, "Reserved", "" }, { 49, 0, "Reserved", "" }, { 50, 0, "Reserved", "" }, { 51, 0, "Reserved", "" }, { 52, 0, "Reserved", "" }, { 53, 0, "Reserved", "" }, { 54, 0, "Reserved", "" }, { 55, 0, "Reserved", "" }, { 56, 0, "Reserved", "" }, { 57, 0, "Reserved", "" }, { 58, 0, "Reserved", "" }, { 59, 0, "Reserved", "" }, { 60, 0, "Reserved", "" }, { 61, 0, "Reserved", "" }, { 62, 0, "Reserved", "" }, { 63, 0, "Reserved", "" }, { 64, 0, "Reserved", "" }, { 65, 0, "Reserved", "" }, { 66, 0, "Reserved", "" }, { 67, 0, "Reserved", "" }, { 68, 0, "Reserved", "" }, { 69, 0, "Reserved", "" }, { 70, 0, "Reserved", "" }, { 71, 0, "Reserved", "" }, { 72, 0, "Reserved", "" }, { 73, 0, "Reserved", "" }, { 74, 0, "Reserved", "" }, { 75, 0, "Reserved", "" }, { 76, 0, "Reserved", "" }, { 77, 0, "Reserved", "" }, { 78, 0, "Reserved", "" }, { 79, 0, "Reserved", "" }, { 80, 0, "Reserved", "" }, { 81, 0, "Reserved", "" }, { 82, 0, "Reserved", "" }, { 83, 0, "Reserved", "" }, { 84, 0, "Reserved", "" }, { 85, 0, "Reserved", "" }, { 86, 0, "Reserved", "" }, { 87, 0, "Reserved", "" }, { 88, 0, "Reserved", "" }, { 89, 0, "Reserved", "" }, { 90, 0, "Reserved", "" }, { 91, 0, "Reserved", "" }, { 92, 0, "Reserved", "" }, { 93, 0, "Reserved", "" }, { 94, 0, "Reserved", "" }, { 95, 0, "Reserved", "" }, { 96, 0, "Reserved", "" }, { 97, 0, "Reserved", "" }, { 98, 0, "Reserved", "" }, { 99, 0, "Reserved", "" }, { 100, 0, "Reserved", "" }, { 101, 0, "Reserved", "" }, { 102, 0, "Reserved", "" }, { 103, 0, "Reserved", "" }, { 104, 0, "Reserved", "" }, { 105, 0, "Reserved", "" }, { 106, 0, "Reserved", "" }, { 107, 0, "Reserved", "" }, { 108, 0, "Reserved", "" }, { 109, 0, "Reserved", "" }, { 110, 0, "Reserved", "" }, { 111, 0, "Reserved", "" }, { 112, 0, "Reserved", "" }, { 113, 0, "Reserved", "" }, { 114, 0, "Reserved", "" }, { 115, 0, "Reserved", "" }, { 116, 0, "Reserved", "" }, { 117, 0, "Reserved", "" }, { 118, 0, "Reserved", "" }, { 119, 0, "Reserved", "" }, { 120, 0, "Reserved", "" }, { 121, 0, "Reserved", "" }, { 122, 0, "Reserved", "" }, { 123, 0, "Reserved", "" }, { 124, 0, "Reserved", "" }, { 125, 0, "Reserved", "" }, { 126, 0, "Reserved", "" }, { 127, 0, "Reserved", "" }, { 128, 0, "Reserved", "" }, { 129, 0, "Reserved", "" }, { 130, 0, "Reserved", "" }, { 131, 0, "Reserved", "" }, { 132, 0, "Reserved", "" }, { 133, 0, "Reserved", "" }, { 134, 0, "Reserved", "" }, { 135, 0, "Reserved", "" }, { 136, 0, "Reserved", "" }, { 137, 0, "Reserved", "" }, { 138, 0, "Reserved", "" }, { 139, 0, "Reserved", "" }, { 140, 0, "Reserved", "" }, { 141, 0, "Reserved", "" }, { 142, 0, "Reserved", "" }, { 143, 0, "Reserved", "" }, { 144, 0, "Reserved", "" }, { 145, 0, "Reserved", "" }, { 146, 0, "Reserved", "" }, { 147, 0, "Reserved", "" }, { 148, 0, "Reserved", "" }, { 149, 0, "Reserved", "" }, { 150, 0, "Reserved", "" }, { 151, 0, "Reserved", "" }, { 152, 0, "Reserved", "" }, { 153, 0, "Reserved", "" }, { 154, 0, "Reserved", "" }, { 155, 0, "Reserved", "" }, { 156, 0, "Reserved", "" }, { 157, 0, "Reserved", "" }, { 158, 0, "Reserved", "" }, { 159, 0, "Reserved", "" }, { 160, 0, "Reserved", "" }, { 161, 0, "Reserved", "" }, { 162, 0, "Reserved", "" }, { 163, 0, "Reserved", "" }, { 164, 0, "Reserved", "" }, { 165, 0, "Reserved", "" }, { 166, 0, "Reserved", "" }, { 167, 0, "Reserved", "" }, { 168, 0, "Reserved", "" }, { 169, 0, "Reserved", "" }, { 170, 0, "Reserved", "" }, { 171, 0, "Reserved", "" }, { 172, 0, "Reserved", "" }, { 173, 0, "Reserved", "" }, { 174, 0, "Reserved", "" }, { 175, 0, "Reserved", "" }, { 176, 0, "Reserved", "" }, { 177, 0, "Reserved", "" }, { 178, 0, "Reserved", "" }, { 179, 0, "Reserved", "" }, { 180, 0, "Reserved", "" }, { 181, 0, "Reserved", "" }, { 182, 0, "Reserved", "" }, { 183, 0, "Reserved", "" }, { 184, 0, "Reserved", "" }, { 185, 0, "Reserved", "" }, { 186, 0, "Reserved", "" }, { 187, 0, "Reserved", "" }, { 188, 0, "Reserved", "" }, { 189, 0, "Reserved", "" }, { 190, 0, "Reserved", "" }, { 191, 0, "Reserved", "" }, { 192, 0, "Reserved", "" }, { 193, 0, "Reserved", "" }, { 194, 0, "Reserved", "" }, { 195, 0, "Reserved", "" }, { 196, 0, "Reserved", "" }, { 197, 0, "Reserved", "" }, { 198, 0, "Reserved", "" }, { 199, 0, "Reserved", "" }, { 200, 0, "Reserved", "" }, { 201, 0, "Reserved", "" }, { 202, 0, "Reserved", "" }, { 203, 0, "Reserved", "" }, { 204, 0, "Reserved", "" }, { 205, 0, "Reserved", "" }, { 206, 0, "Reserved", "" }, { 207, 0, "Reserved", "" }, { 208, 0, "Reserved", "" }, { 209, 0, "Reserved", "" }, { 210, 0, "Reserved", "" }, { 211, 0, "Reserved", "" }, { 212, 0, "Reserved", "" }, { 213, 0, "Reserved", "" }, { 214, 0, "Reserved", "" }, { 215, 0, "Reserved", "" }, { 216, 0, "Reserved", "" }, { 217, 0, "Reserved", "" }, { 218, 0, "Reserved", "" }, { 219, 0, "Reserved", "" }, { 220, 0, "Reserved", "" }, { 221, 0, "Reserved", "" }, { 222, 0, "Reserved", "" }, { 223, 0, "Reserved", "" }, { 224, 0, "Reserved", "" }, { 225, 0, "Reserved", "" }, { 226, 0, "Reserved", "" }, { 227, 0, "Reserved", "" }, { 228, 0, "Reserved", "" }, { 229, 0, "Reserved", "" }, { 230, 0, "Reserved", "" }, { 231, 0, "Reserved", "" }, { 232, 0, "Reserved", "" }, { 233, 0, "Reserved", "" }, { 234, 0, "Reserved", "" }, { 235, 0, "Reserved", "" }, { 236, 0, "Reserved", "" }, { 237, 0, "Reserved", "" }, { 238, 0, "Reserved", "" }, { 239, 0, "Reserved", "" }, { 240, 0, "Reserved", "" }, { 241, 0, "Reserved", "" }, { 242, 0, "Reserved", "" }, { 243, 0, "Reserved", "" }, { 244, 0, "Reserved", "" }, { 245, 0, "Reserved", "" }, { 246, 0, "Reserved", "" }, { 247, 0, "Reserved", "" }, { 248, 0, "Reserved", "" }, { 249, 0, "Reserved", "" }, { 250, 0, "Reserved", "" }, { 251, 0, "Reserved", "" }, { 252, 0, "Reserved", "" }, { 253, 0, "Reserved", "" }, { 254, 0, "Reserved", "" }, { 255, 0, "Reserved", "" }, { 256, 256, "impossible combination", "impossible combination" } }; static struct icmpmsg icmperror = { 256, 256, "unknown", "unknown" }; struct icmpmsg *stricmp(int type, int code) { int i; if(type > 255) return &icmperror; for(i = 0; icmpmsgs[i].type <= type; i++) if(icmpmsgs[i].type == type && icmpmsgs[i].code == code) return(&(icmpmsgs[i])); for(i = 0; icmpmsgs[i].type <= type; i++) if(icmpmsgs[i].type == type) return(&(icmpmsgs[i])); return &icmperror; } /* EOF */ lsm-1.0.21/icmp_t.h000066400000000000000000000003561455003704000140140ustar00rootroot00000000000000/* (C) 2009 Mika Ilmaranta */ #ifndef __ICMP_T_H__ #define __ICMP_T_H__ struct icmpmsg { int type; int code; char *type_msg; char *code_msg; }; struct icmpmsg *stricmp(int type, int code); #endif /* EOF */ lsm-1.0.21/pidfile.c000066400000000000000000000033441455003704000141500ustar00rootroot00000000000000/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #include #include #include #include #include #include #include #include #include "pidfile.h" #include "globals.h" static int pidfilefh = 0; int pidfile_open(void) { if(get_nodaemon()) return(0); pidfilefh = open(get_pidfile(), O_RDWR|O_CREAT, 0640); if(pidfilefh < 0) { syslog(LOG_ERR, "can't open pid file %s", get_pidfile()); return(1); /* can not open */ } if(fcntl(pidfilefh, F_SETFD, FD_CLOEXEC) == -1) { syslog(LOG_ERR, "failed to set close on exec on pid file %s", get_pidfile()); } if(lockf(pidfilefh, F_TLOCK, 0) < 0) { syslog(LOG_ERR, "can't lock pid file %s", get_pidfile()); return(1); /* can not lock */ } return(0); } int pidfile_update(void) { if(get_nodaemon()) return(0); if(pidfilefh) { char str[BUFSIZ]; ssize_t n; lseek(pidfilefh, 0, SEEK_SET); if(ftruncate(pidfilefh, 0) == -1) { syslog(LOG_ERR, "ftruncate failed \"%s\"", strerror(errno)); return(1); } sprintf(str, "%d\n", getpid()); n = write(pidfilefh, str, strlen(str)); /* record pid to lockfile */ if(n == -1) { syslog(LOG_ERR, "write failed \"%s\"", strerror(errno)); return(1); } if(n != strlen(str)) { #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) syslog(LOG_ERR, "write failed, %ld bytes written of %ld bytes", n, strlen(str)); #else syslog(LOG_ERR, "write failed, %d bytes written of %d bytes", n, strlen(str)); #endif return(1); } } return(0); } void pidfile_close(void) { if(get_nodaemon()) return; if(pidfilefh) { close(pidfilefh); pidfilefh = 0; unlink(get_pidfile()); } } /* EOF */ lsm-1.0.21/pidfile.h000066400000000000000000000003241455003704000141500ustar00rootroot00000000000000/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #ifndef __PIDFILE_H__ #define __PIDFILE_H__ int pidfile_open(void); int pidfile_update(void); void pidfile_close(void); #endif /* EOF */ lsm-1.0.21/plugin_export.c000066400000000000000000000147101455003704000154320ustar00rootroot00000000000000/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #ifndef NO_PLUGIN_EXPORT #include #include #include #include #include #include #include "plugin_export.h" #include "timecalc.h" #include "defs.h" #ifndef NO_PLUGIN_EXPORT_STATUS #include "globals.h" #endif #ifndef NO_PLUGIN_EXPORT_MUNIN static char *munin_data_src_name(const char *src); static void plugin_export_munin(CONFIG *first); #endif static struct timeval export_time = {0, 0}; void plugin_export_init(void) { gettimeofday(&export_time, NULL); } void plugin_export(CONFIG *first) { struct timeval current_time = {0, 0}; gettimeofday(¤t_time, NULL); /* export every 300s */ if(timeval_diff_cmp(¤t_time, &export_time, TIMEVAL_DIFF_CMP_GT, 300, 0) == FALSE) return; /* next export after 300 sec */ timeval_add(&export_time, 300, 0); #ifndef NO_PLUGIN_EXPORT_MUNIN plugin_export_munin(first); #endif } #ifndef NO_PLUGIN_EXPORT_MUNIN static void plugin_export_munin(CONFIG *first) { FILE *fp; char buf[BUFSIZ]; CONFIG *cur; TARGET *t; /* export avg_rtt graph config */ snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "config.rtt"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } fprintf(fp, "graph_title Foolsm Average Ping Latency\n"); fprintf(fp, "graph_vlabel ms\n"); fprintf(fp, "graph_info This graph shows Foolsm status\n"); fprintf(fp, "graph_category network\n"); fprintf(fp, "graph_args --base 1000 -l 0\n"); for(cur = first; cur; cur = cur->next) { char *name = munin_data_src_name(cur->name); fprintf(fp, "%s_rtt.label %s rtt\n", name, cur->name); fprintf(fp, "%s_rtt.type GAUGE\n", name); } fclose(fp); /* export avg_rtt values */ snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status.rtt"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } for(cur = first; cur; cur = cur->next) { char *name = munin_data_src_name(cur->name); t = cur->data; fprintf(fp, "%s_rtt.value %.2f\n", name, (t->status == DOWN || t->status == LONG_DOWN) ? 0.0 : t->avg_rtt / 1000.0); } fclose(fp); /* export other counts config */ snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "config.counts"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } fprintf(fp, "graph_title Foolsm packet counts\n"); fprintf(fp, "graph_vlabel percent\n"); fprintf(fp, "graph_info This graph shows Foolsm status\n"); fprintf(fp, "graph_category network\n"); fprintf(fp, "graph_args --base 1000 -l 0\n"); for(cur = first; cur; cur = cur->next) { char *name = munin_data_src_name(cur->name); fprintf(fp, "%s_timeout.label %s Timed out\n", name, cur->name); fprintf(fp, "%s_timeout.type GAUGE\n", name); fprintf(fp, "%s_replied.label %s Replied\n", name, cur->name); fprintf(fp, "%s_replied.type GAUGE\n", name); fprintf(fp, "%s_waiting.label %s Waiting\n", name, cur->name); fprintf(fp, "%s_waiting.type GAUGE\n", name); fprintf(fp, "%s_latereply.label %s Late replied\n", name, cur->name); fprintf(fp, "%s_latereply.type GAUGE\n", name); fprintf(fp, "%s_cwait.label %s Consecutive waiting\n", name, cur->name); fprintf(fp, "%s_cwait.type GAUGE\n", name); fprintf(fp, "%s_cmiss.label %s Consecutive missing\n", name, cur->name); fprintf(fp, "%s_cmiss.type GAUGE\n", name); fprintf(fp, "%s_crcvd.label %s Consecutive received\n", name, cur->name); fprintf(fp, "%s_crcvd.type GAUGE\n", name); } fclose(fp); /* export other counts values */ snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status.counts"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } for(cur = first; cur; cur = cur->next) { char *name = munin_data_src_name(cur->name); t = cur->data; fprintf(fp, "%s_timeout.value %d\n", name, t->timeout); fprintf(fp, "%s_replied.value %d\n", name, t->replied); fprintf(fp, "%s_waiting.value %d\n", name, t->waiting); fprintf(fp, "%s_latereply.value %d\n", name, t->reply_late); fprintf(fp, "%s_cwait.value %d\n", name, t->consecutive_waiting); fprintf(fp, "%s_cmiss.value %d\n", name, t->consecutive_missing); fprintf(fp, "%s_crcvd.value %d\n", name, t->consecutive_rcvd); } fclose(fp); /* export connection status config */ snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "config.status"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } fprintf(fp, "graph_title Foolsm connection statuses\n"); fprintf(fp, "graph_vlabel Status\n"); fprintf(fp, "graph_info This graph shows Foolsm connection statuses\n"); fprintf(fp, "graph_category network\n"); fprintf(fp, "graph_info Status: 0 = DOWN, 1 = UP, 2 = UNKNOWN, 3 = LONG_DOWN\n"); fprintf(fp, "graph_args --base 1000 --lower-limit 0 --upper-limit 3\n"); for(cur = first; cur; cur = cur->next) { char *name = munin_data_src_name(cur->name); fprintf(fp, "%s_status.label %s Status\n", name, cur->name); } fclose(fp); /* export connection status values */ snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status.status"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } for(cur = first; cur; cur = cur->next) { char *name = munin_data_src_name(cur->name); t = cur->data; fprintf(fp, "%s_status.value %d\n", name, t->status); } fclose(fp); } #endif #ifndef NO_PLUGIN_EXPORT_STATUS void plugin_export_status(CONFIG *first) { FILE *fp; char buf[BUFSIZ]; CONFIG *cur; TARGET *t; snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status_export"); if((fp = fopen(buf, "w")) == NULL) { syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf); return; } for(cur = first; cur; cur = cur->next) { t = cur->data; fprintf(fp, "%s %s\n", cur->name, get_status_str(t->status)); } fclose(fp); } #endif #ifndef NO_PLUGIN_EXPORT_MUNIN static char *munin_data_src_name(const char *src) { static char buf[BUFSIZ]; char *p; strcpy(buf, "_"); strncat(buf, src, BUFSIZ - 1); for(p = buf; *p; p++) { if(*p == '-') *p = '_'; if(*p == ' ') *p = '_'; } return(buf); } #endif #endif /* EOF */ lsm-1.0.21/plugin_export.h000066400000000000000000000005571455003704000154430ustar00rootroot00000000000000/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #ifndef NO_PLUGIN_EXPORT #ifndef __PLUGIN_EXPORT_H__ #define __PLUGIN_EXPORT_H__ #include "config.h" #include "foolsm.h" void plugin_export_init(void); void plugin_export(CONFIG *first); #ifndef NO_PLUGIN_EXPORT_STATUS void plugin_export_status(CONFIG *first); #endif #endif #endif /* EOF */ lsm-1.0.21/rsyslog-foolsm.conf.sample000066400000000000000000000001761455003704000175160ustar00rootroot00000000000000# Foobar Link Status Monitor Log file if $programname == 'foolsm' then /var/log/foolsm.log if $programname == 'foolsm' then ~ lsm-1.0.21/save_statuses.c000066400000000000000000000035471455003704000154320ustar00rootroot00000000000000/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #include #include #include #include "config.h" #include "foolsm.h" #include "save_statuses.h" typedef struct status_data { char *name; STATUS status; struct status_data *next; } STATUS_DATA; static STATUS_DATA *first_status = NULL; static STATUS_DATA *last_status = NULL; static STATUS_DATA *cur_status = NULL; void save_statuses(CONFIG *first) { CONFIG *cur; if(first_status != NULL) { syslog(LOG_ERR, "%s: %s: statuses already saved?", __FILE__, __FUNCTION__); return; } /* store connection statuses temporarily */ for(cur = first; cur; cur = cur->next) { if((cur_status = malloc(sizeof(STATUS_DATA))) == NULL) { syslog(LOG_ERR, "%s: %s: couldn't malloc for status_data", __FILE__, __FUNCTION__); exit(1); } cur_status->name = strdup(cur->name); cur_status->status = ((TARGET *)cur->data)->status; if(last_status) { last_status->next = cur_status; last_status = cur_status; cur_status->next = NULL; } else { first_status = cur_status; last_status = cur_status; cur_status->next = NULL; } } } void restore_statuses(CONFIG *first) { CONFIG *cur; if(first_status == NULL) { syslog(LOG_ERR, "%s: %s: can't restore statuses, none saved?", __FILE__, __FUNCTION__); return; } /* restore connection statuses */ for(cur_status = first_status; cur_status; cur_status = cur_status->next) { for(cur = first; cur; cur = cur->next) { if(strcmp(cur_status->name, cur->name) == 0) { ((TARGET *)cur->data)->status = cur_status->status; } } } /* get rid of temporary statuses */ cur_status = first_status; while(cur_status) { STATUS_DATA *tmp; tmp = cur_status->next; free(cur_status->name); free(cur_status); cur_status = tmp; } first_status = NULL; last_status = NULL; cur_status = NULL; } /* EOF */ lsm-1.0.21/save_statuses.h000066400000000000000000000003311455003704000154230ustar00rootroot00000000000000/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #ifndef __SAVE_STATUSES_H__ #define __SAVE_STATUSES_H__ void save_statuses(CONFIG *first); void restore_statuses(CONFIG *first); #endif /* EOF */ lsm-1.0.21/shorewall6_script000066400000000000000000000014161455003704000157630ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2015 Tuomo Soini # # License: GPLv2 # # # event handling script for use with shorewall6 multi-isp setup # To be able to utilize this script you must have shorewall6 >= 4.4.23.3 # STATE=${1} NAME=${2} CHECKIP=${3} DEVICE=${4} WARN_EMAIL=${5} REPLIED=${6} WAITING=${7} TIMEOUT=${8} REPLY_LATE=${9} CONS_RCVD=${10} CONS_WAIT=${11} CONS_MISS=${12} AVG_RTT=${13} SRCIP=${14} PREVSTATE=${15} TIMESTAMP=${16} if [ ${STATE} = up ]; then state=0 action=enable else state=1 action=disable fi VARDIR=$(shorewall6 show vardir) VARDIR=${VARDIR:-/var/lib/shorewall6} echo ${state} > ${VARDIR}/${DEVICE}.status if [ -x ${VARDIR}/firewall ]; then ${VARDIR}/firewall ${action} ${DEVICE} else shorewall6 -q restart fi exit 0 lsm-1.0.21/shorewall_script000066400000000000000000000015171455003704000156770ustar00rootroot00000000000000#!/bin/sh # # Copyright (C) 2009,2013 Mika Ilmaranta # Copyright (C) 2009-2010,2015 Tuomo Soini # # License: GPLv2 # # # event handling script for use with shorewall multi-isp setup # To be able to utilize this script you must have shorewall >= 4.4.23.3 # STATE=${1} NAME=${2} CHECKIP=${3} DEVICE=${4} WARN_EMAIL=${5} REPLIED=${6} WAITING=${7} TIMEOUT=${8} REPLY_LATE=${9} CONS_RCVD=${10} CONS_WAIT=${11} CONS_MISS=${12} AVG_RTT=${13} SRCIP=${14} PREVSTATE=${15} TIMESTAMP=${16} if [ ${STATE} = up ]; then state=0 action=enable else state=1 action=disable fi VARDIR=$(shorewall show vardir) VARDIR=${VARDIR:-/var/lib/shorewall} echo ${state} > ${VARDIR}/${DEVICE}.status if [ -x ${VARDIR}/firewall ]; then ${VARDIR}/firewall ${action} ${DEVICE} else shorewall -q restart fi exit 0 lsm-1.0.21/signal_handler.c000066400000000000000000000007021455003704000155010ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #include #include #include #include "globals.h" void signal_handler(int signo) { int errno_save; errno_save = errno; switch(signo) { case SIGINT: set_cont(0); break; case SIGUSR1: set_dump(1); break; case SIGUSR2: set_dump_if_list(1); break; case SIGHUP: set_reload_cfg(1); break; } errno = errno_save; } /* EOF */ lsm-1.0.21/signal_handler.h000066400000000000000000000002671455003704000155140ustar00rootroot00000000000000/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #ifndef __SIGNAL_HANDLER_H__ #define __SIGNAL_HANDLER_H__ void signal_handler(int signo); #endif /* EOF */ lsm-1.0.21/timecalc.c000066400000000000000000000022501455003704000143100ustar00rootroot00000000000000/* (C) 2011 Mika Ilmaranta */ #include #include "defs.h" #include "timecalc.h" int timeval_diff_cmp(struct timeval *a, struct timeval *b, int operation, time_t sec, suseconds_t usec) { time_t diff_sec; suseconds_t diff_usec; diff_sec = a->tv_sec - b->tv_sec; diff_usec = a->tv_usec - b->tv_usec; if(diff_usec < 0) { diff_sec--; diff_usec = 1000000L + diff_usec; } switch(operation) { case TIMEVAL_DIFF_CMP_GT: if(diff_sec > sec) return(TRUE); if(diff_sec == sec && diff_usec > usec) return(TRUE); return(FALSE); break; case TIMEVAL_DIFF_CMP_LT: if(diff_sec < sec) return(TRUE); if(diff_sec == sec && diff_usec < usec) return(TRUE); return(FALSE); break; default: syslog(LOG_ERR, "%s: %s: warning: unknown timeval_diff_cmp operation requested %d", __FILE__, __FUNCTION__, operation); return(FALSE); } } long timeval_diff(struct timeval *a, struct timeval *b) { return( ((a->tv_sec - b->tv_sec) * 1000000L) + (a->tv_usec - b->tv_usec) ); } void timeval_add(struct timeval *a, time_t sec, suseconds_t usec) { a->tv_sec += sec; a->tv_usec += usec; if(a->tv_usec >= 1000000L) { a->tv_sec++; a->tv_usec -= 1000000L; } } /* EOF */ lsm-1.0.21/timecalc.h000066400000000000000000000007071455003704000143220ustar00rootroot00000000000000/* (C) 2011 Mika Ilmaranta */ #ifndef __TIMECALC_H__ #define __TIMECALC_H__ #include #include #define TIMEVAL_DIFF_CMP_GT (0) #define TIMEVAL_DIFF_CMP_LT (1) int timeval_diff_cmp(struct timeval *a, struct timeval *b, int operation, time_t sec, suseconds_t usec); long timeval_diff(struct timeval *a, struct timeval *b); void timeval_add(struct timeval *a, time_t sec, suseconds_t usec); #endif /* EOF */ lsm-1.0.21/usage.c000066400000000000000000000011051455003704000136310ustar00rootroot00000000000000/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #include #include #include #include "globals.h" #include "usage.h" void usage_and_exit(void) { #if defined(FOOLSM_VERSION) printf("%s version %s\n", get_prog(), FOOLSM_VERSION); #endif printf("usage: %s\n" " [-h|--help|-v|--version]\n" " [-c|--config ]\n" " [-p|--pidfile ]\n" " [-f|--no-fork]\n", get_prog()); printf("check syslog for debug/error messages\n"); exit(2); } /* EOF */ lsm-1.0.21/usage.h000066400000000000000000000002371455003704000136430ustar00rootroot00000000000000/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #ifndef __USAGE_H__ #define __USAGE_H__ void usage_and_exit(void); #endif /* EOF */