lsm-1.0.4/0000755000175200017520000000000012651706422011334 5ustar ilmisilmislsm-1.0.4/README0000664000175200017520000000434612300106517012213 0ustar ilmisilmisREADME ====== License: GPLv2 You should be able to find GNU Public License from www.gnu.org. LSM 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 lsm v0.27, the detection algorithm works like this: LSM 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: LSM assumes each connection starts UP. 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 lsm.spec which does all what I need. AUTHORS ======= Mika Ilmaranta See lsm.spec's changelog section for patch submitters. #EOF lsm-1.0.4/cksum.c0000664000175200017520000000104112174444561012623 0ustar ilmisilmis/* (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.4/cksum.h0000664000175200017520000000027612174444561012641 0ustar ilmisilmis/* (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.4/cmdline.c0000664000175200017520000000161512370513257013120 0ustar ilmisilmis/* (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.4/cmdline.h0000664000175200017520000000026412370420473013121 0ustar ilmisilmis/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #ifndef __CMDLINE_H__ #define __CMDLINE_H__ void cmdline_parse(int argc, char *argv[]); #endif /* EOF */ lsm-1.0.4/group_script0000664000175200017520000000144512623315357014007 0ustar ilmisilmis#!/bin/sh # # Copyright (C) 2012,2015 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} if [ -z "${WARN_EMAIL}" ] ; then exit 0 fi DATE=$(date --date=@${TIMESTAMP}) cat <= 7 || 0%{?fedora} %bcond_without systemd # enabled Requires(post): systemd Requires(preun): systemd Requires(postun): systemd BuildRequires: systemd %else %bcond_with systemd # disabled Requires(post): chkconfig Requires(postun): /sbin/service Requires(preun): /sbin/service Requires(preun): chkconfig %endif %if 0%{?rhel} && 0%{?rhel} <= 5 %global _sharedstatedir /var/lib %endif Requires: mailx %if 0%{?devel} BuildRequires: ElectricFence %endif BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %description Lsm is the link status monitor. Lsm 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 CFLAGS="$RPM_OPT_FLAGS" LDFLAGS=${EFENCE} %{?_smp_mflags} %install rm -rf %{buildroot} mkdir -p %{buildroot}%{_sysconfdir}/lsm mkdir -p %{buildroot}%{_sbindir} mkdir -p %{buildroot}%{_libexecdir}/lsm mkdir -p %{buildroot}%{_sharedstatedir}/lsm install -m0755 lsm %{buildroot}%{_sbindir} install -m0644 lsm.conf %{buildroot}%{_sysconfdir}/lsm install -m0755 default_script group_script shorewall_script shorewall6_script \ %{buildroot}%{_libexecdir}/lsm/ %if %{with systemd} mkdir -p %{buildroot}%{_unitdir} install -m0644 lsm.service %{buildroot}%{_unitdir}/ %else mkdir -p %{buildroot}%{_initrddir} install -m0755 lsm.init %{buildroot}%{_initrddir}/lsm # Compat symlinks for older system versions mkdir -p %{buildroot}%{_datadir}/lsm ln -s ../../libexec/lsm/shorewall_script \ %{buildroot}%{_datadir}/lsm/ ln -s ../../libexec/lsm/default_script \ %{buildroot}%{_datadir}/lsm/ %endif %clean rm -rf %{buildroot} %post %if %{with systemd} %systemd_post lsm.service %else /sbin/chkconfig --add lsm %endif %preun %if %{with systemd} %systemd_preun lsm.service %else if [ $1 -eq 0 ]; then /sbin/service lsm stop >/dev/null 2>&1 || : /sbin/chkconfig --del lsm fi %endif %postun %if %{with systemd} %systemd_postun_with_restart lsm.service %else if [ $1 -ge 1 ]; then /sbin/service lsm condrestart >/dev/null 2>&1 || : fi %endif %files %defattr(-,root,root) %doc README lsm.conf.sample default_script.sample rsyslog-lsm.conf.sample %if %{with systemd} %{_unitdir}/lsm.service %else %{_initrddir}/lsm %dir %{_datadir}/lsm %{_datadir}/lsm/default_script %{_datadir}/lsm/shorewall_script %endif %dir %{_libexecdir}/lsm %{_libexecdir}/lsm/default_script %{_libexecdir}/lsm/group_script %{_libexecdir}/lsm/shorewall_script %{_libexecdir}/lsm/shorewall6_script %dir %{_sysconfdir}/lsm %config(noreplace) %{_sysconfdir}/lsm/lsm.conf %{_sbindir}/lsm %dir %{_sharedstatedir}/lsm %changelog * 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.4/defs.h0000664000175200017520000000112412174444561012431 0ustar ilmisilmis/* (C) 2009-2011 Mika Ilmaranta 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/lsm" #endif /* EOF */ lsm-1.0.4/forkexec.c0000664000175200017520000002050012174444561013310 0ustar ilmisilmis/* (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; 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 exitval; pid_t pid; if((pid = waitpid(-1, &exitval, WNOHANG)) == -1) { if(cfg.debug >= 9 && errno != ECHILD) syslog(LOG_ERR, "%s: %s: %d: waitpid failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); } else { if(cfg.debug >= 9 && exitval) syslog(LOG_ERR, "%s: %s: %d: child script with pid %d exited with non null exit value %d", __FILE__, __FUNCTION__, __LINE__, pid, exitval); 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); } } 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.4/forkexec.h0000664000175200017520000000107312174444561013321 0ustar ilmisilmis/* (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.4/globals.c0000664000175200017520000000303512455500013013114 0ustar ilmisilmis/* (C) 2009-2011 Mika Ilmaranta License: GPLv2 */ #include #include #include "globals.h" #include "defs.h" 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 = "/etc/lsm/lsm.conf"; static char *pidfile = "/var/run/lsm.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.4/globals.h0000664000175200017520000000126412455500013013123 0ustar ilmisilmis/* (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.4/icmp6_t.c0000664000175200017520000001011212174444561013041 0ustar ilmisilmis/* (C) 2011 Mika Ilmaranta */ #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 *stricmp6(int type, int code) { int i; if(type > 255) return(&((struct icmp6msg) { 256, 256, "unknown", "unknown" })); 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(&((struct icmp6msg) { 256, 256, "unknown", "unknown" })); } /* EOF */ lsm-1.0.4/icmp6_t.h0000664000175200017520000000036312174444561013055 0ustar ilmisilmis/* (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.4/icmp_t.c0000664000175200017520000002430212174444561012761 0ustar ilmisilmis/* (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" } }; struct icmpmsg *stricmp(int type, int code) { int i; if(type > 255) return(&((struct icmpmsg) { 256, 256, "unknown", "unknown" })); 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(&((struct icmpmsg) { 256, 256, "unknown", "unknown" })); } /* EOF */ lsm-1.0.4/icmp_t.h0000664000175200017520000000035612174444561012771 0ustar ilmisilmis/* (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.4/lsm.h0000664000175200017520000000267012243732112012277 0ustar ilmisilmis/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #ifndef __LSM_H__ #define __LSM_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 avg_rtt; int status_change; } TARGET; #endif /* EOF */ lsm-1.0.4/lsm.init0000664000175200017520000000342112370420473013013 0ustar ilmisilmis#!/bin/sh # # /etc/init.d/lsm # # This shellscript takes care of starting and stopping lsm. # # chkconfig: - 79 31 # description: Lsm, Link Status Monitor # ### BEGIN INIT INFO # Provides: lsm # Required-Start: $network $syslog # Required-Stop: # Default-Stop: 0 1 6 # Short-Description: LSM - link status monitor # Description: LSM is the link status monitor # LSM 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/lsm/lsm.conf" PIDFILE="/var/run/lsm.pid" [ -f /etc/sysconfig/lsm ] && . /etc/sysconfig/lsm [ -x /usr/sbin/lsm ] || exit 0 RETVAL=0 start() { echo -n $"Starting lsm: " daemon --pidfile=${PIDFILE} /usr/sbin/lsm --config $CONFIGFILE --pidfile $PIDFILE RETVAL=$? /bin/usleep 10000 echo [ $RETVAL = 0 ] && touch /var/lock/subsys/lsm return $RETVAL } stop() { echo -n $"Stopping lsm: " killproc /usr/sbin/lsm RETVAL=$? echo [ $RETVAL = 0 ] && rm -f /var/lock/subsys/lsm return $RETVAL } restart() { stop start } reload() { echo -n $"Reloading lsm: " killproc -p ${PIDFILE} /usr/sbin/lsm -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/lsm ] && restart ;; status) status -p ${PIDFILE} lsm RETVAL=$? ;; *) echo "Usage: lsm {start|stop|restart|condrestart|status}" RETVAL=2 esac exit $RETVAL lsm-1.0.4/pidfile.c0000664000175200017520000000334412405072727013123 0ustar ilmisilmis/* (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.4/pidfile.h0000664000175200017520000000032412370420473013117 0ustar ilmisilmis/* (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.4/plugin_export.c0000664000175200017520000001466612455500013014404 0ustar ilmisilmis/* (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 LSM Average Ping Latency\n"); fprintf(fp, "graph_vlabel ms\n"); fprintf(fp, "graph_info This graph shows LSM 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 LSM packet counts\n"); fprintf(fp, "graph_vlabel percent\n"); fprintf(fp, "graph_info This graph shows LSM 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 LSM connection statuses\n"); fprintf(fp, "graph_vlabel Status\n"); fprintf(fp, "graph_info This graph shows LSM 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.4/plugin_export.h0000664000175200017520000000055412455500013014400 0ustar ilmisilmis/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #ifndef NO_PLUGIN_EXPORT #ifndef __PLUGIN_EXPORT_H__ #define __PLUGIN_EXPORT_H__ #include "config.h" #include "lsm.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.4/rsyslog-lsm.conf.sample0000664000175200017520000000015612125043176015756 0ustar ilmisilmis# Link Status Monitor Log file if $programname == 'lsm' then /var/log/lsm.log if $programname == 'lsm' then ~ lsm-1.0.4/save_statuses.c0000664000175200017520000000354412174444561014404 0ustar ilmisilmis/* (C) 2013 Mika Ilmaranta License: GPLv2 */ #include #include #include #include "config.h" #include "lsm.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.4/save_statuses.h0000664000175200017520000000033112174444561014400 0ustar ilmisilmis/* (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.4/signal_handler.c0000664000175200017520000000070212174444561014456 0ustar ilmisilmis/* (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.4/signal_handler.h0000664000175200017520000000026712174444561014471 0ustar ilmisilmis/* (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.4/timecalc.c0000664000175200017520000000225012174444561013265 0ustar ilmisilmis/* (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.4/timecalc.h0000664000175200017520000000070712174444561013277 0ustar ilmisilmis/* (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.4/usage.c0000664000175200017520000000107712370444506012613 0ustar ilmisilmis/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #include #include #include #include "globals.h" #include "usage.h" void usage_and_exit(void) { #if defined(LSM_VERSION) printf("%s version %s\n", get_prog(), LSM_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.4/usage.h0000664000175200017520000000023712370420473012612 0ustar ilmisilmis/* (C) 2014 Mika Ilmaranta License: GPLv2 */ #ifndef __USAGE_H__ #define __USAGE_H__ void usage_and_exit(void); #endif /* EOF */ lsm-1.0.4/Makefile0000664000175200017520000000373712532672540013011 0ustar ilmisilmis# # (C) 2009-2011 Mika Ilmaranta # # License: GPLv2 # VERSION ?= $(lastword $(shell grep ^Version: lsm.spec)) PROGS = lsm PKG = lsm CC = gcc override CFLAGS += -Wall -O2 -DLSM_VERSION=\"$(VERSION)\" #override CFLAGS += -D NO_PLUGIN_EXPORT #override CFLAGS += -D NO_PLUGIN_EXPORT_MUNIN #override CFLAGS += -D NO_PLUGIN_EXPORT_STATUS LDFLAGS = INITDDIR ?= /etc/init.d DOCDIR ?= /usr/share/doc/lsm DOCFILES = README lsm.conf.sample default_script.sample rsyslog-lsm.conf.sample SCRIPTS = shorewall_script shorewall6_script default_script group_script .PHONY: all clean distclean tar rpm all: $(PROGS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< lsm: lsm.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 $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) clean distclean: rm -rf *~ .*~ *.o $(PROGS) Makefile.depend 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 Makefile.depend: *.c *.h Makefile $(CC) -MM *.c > $@ install: all mkdir -p $(DESTDIR)/etc/lsm mkdir -p $(DESTDIR)$(INITDDIR) mkdir -p $(DESTDIR)/usr/sbin mkdir -p $(DESTDIR)$(DOCDIR) mkdir -p $(DESTDIR)/usr/share/lsm mkdir -p $(DESTDIR)/usr/libexec/lsm mkdir -p $(DESTDIR)/var/lib/lsm install -m0644 lsm.conf $(DESTDIR)/etc/lsm install -m0755 lsm.init $(DESTDIR)$(INITDDIR)/lsm install -m0755 lsm $(DESTDIR)/usr/sbin install -m0644 $(DOCFILES) $(DESTDIR)$(DOCDIR) install -m0755 $(SCRIPTS) $(DESTDIR)/usr/libexec/lsm ln -sf ../../libexec/lsm/default_script $(DESTDIR)/usr/share/lsm/default_script ln -sf ../../libexec/lsm/shorewall_script $(DESTDIR)/usr/share/lsm/shorewall_script -include Makefile.depend # lsm-1.0.4/default_script.sample0000664000175200017520000000030512532672540015550 0ustar ilmisilmis#!/bin/sh # You can call a set of scripts like so on an event cd /etc/lsm/script.d for script in $(ls); do if [ ! -x $script ]; then continue fi ./$script "$@" done exit 0 # lsm-1.0.4/lsm.conf.sample0000664000175200017520000000527312532672540014270 0ustar ilmisilmis# # (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/lsm/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 lsm 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/lsm/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/lsm/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/lsm/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 # # logic between member connetion statuses # # logic=0 == or # # logic=1 == and # logic=0 # member-connection=conn-a # member-connection=conn-b # } #EOF lsm-1.0.4/shorewall6_script0000664000175200017520000000146312532672540014740 0ustar ilmisilmis#!/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} DATE=$(date --date=@${TIMESTAMP}) 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.4/shorewall_script0000664000175200017520000000156412532672540014654 0ustar ilmisilmis#!/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} DATE=$(date --date=@${TIMESTAMP}) 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.4/config.h0000664000175200017520000000333212533023050012741 0ustar ilmisilmis/* (C) 2009-2015 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 */ 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.4/lsm.c0000664000175200017520000017030012623315357012300 0ustar ilmisilmis/* (C) 2009-2015 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 "lsm.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("lsm", 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; /* 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->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, avg_rtt = %.3f, 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->avg_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, avg_rtt = %.3f, 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->avg_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", 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); 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", 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); 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", 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); 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", 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); 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", 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); 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", 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); 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", 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); 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", 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); 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", curg->eventscript, get_status_str(curg->status), curg->name, "", "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec); 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", curg->notifyscript, get_status_str(curg->status), curg->name, "", "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec); 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", curg->eventscript, get_status_str(curg->status), curg->name, "", "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec); 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", curg->notifyscript, get_status_str(curg->status), curg->name, "", "", curg->warn_email ? curg->warn_email : "", 0, 0, 0, 0, 0, 0, 0, 0, "", get_status_str(prevstatus), current_time.tv_sec); 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) { syslog(LOG_INFO, "recvfrom failed with %s \"%s\"\n", cur->name, strerror(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 = %ld to %s, num_sent = %ld, %ld, pkt_size = %d\n", t->seq, inet_ntoa(t->saddr.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 = %ld to %s, num_sent = %ld, %ld, pkt_size = %d\n", t->seq, inet_ntoa(t->saddr.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(((*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.4/lsm.conf0000664000175200017520000000234712623315357013010 0ustar ilmisilmis# # Copyright (C) 2009-2015 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 # These are set in the code. You may override any values here. # #defaults { # name=defaults # checkip=127.0.0.1 # eventscript= # notifyscript=/usr/libexec/lsm/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 lsm 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 lsm.conf.sample # -include /etc/lsm/local*.conf #EOF lsm-1.0.4/config.c0000664000175200017520000006675112630257645012773 0ustar ilmisilmis/* (C) 2009-2015 Mika Ilmaranta License: GPLv2 */ #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "defs.h" 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->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("/usr/libexec/lsm/default_script"); 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[BUFSIZ], pattern[128], *p, s[BUFSIZ]; 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, BUFSIZ, "%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, "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->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->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.4/default_script0000664000175200017520000000262712623315357014302 0ustar ilmisilmis#!/bin/sh # # Copyright (C) 2009-2015 Mika Ilmaranta # Copyright (C) 2015 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} if [ -z "${WARN_EMAIL}" ] ; then exit 0 fi DATE=$(date --date=@${TIMESTAMP}) cat <