ipwatchd-1.3.0/0000775000175000017500000000000013326442607012325 5ustar jariqjariqipwatchd-1.3.0/doc/0000775000175000017500000000000013326442626013073 5ustar jariqjariqipwatchd-1.3.0/doc/ipwatchd.8.gz0000664000175000017500000000171213326442515015404 0ustar jariqjariq2Kipwatchd.8uя6WxJ{TUUU.uA *Lo8g;p vٗ]3$_|d}w?|uOg*uIOcSa"PɁmKZCuQxrbwrXo|N6,{{?Kc`qWAde#͵M%Lgr=_w'D(l a s j^6zmT g"@{M0vHe]#kn %q%Dž6zf`{v [\G{{vQ0rT9 Dxt+MG~A 5g6=0q$-+%s-x:렕q(\%F%_8ݡi2AKjz V >K|(Fci'*[bGc^g˟|Lq[([ F|xT4|eVm ?y40CI6ͩ45FoL:T4"IIJGsKyzCEO sm–alq-A@k lo[.lxjwI a/s7t|[d/ez 0 ] yufnOzpM5_a{JXЍInD'Gs%J:mH I6Dt4p6;N8Ņ\$߇Z,#(sՌOvԢžt$-9M"+ց?;?Rj)WDQwNd I[x#"`_)ХALxܐD+H$U|qt.^IaikC$ CZBNޡ[m&0t4Y.py>Cd02xJ$NؽuG:C_qij#OflKtH 덮9Yلu~UN\'&:i׃΢ښkHZg40/ u]>> ӷdkeO7+ejmt7a-ĎfiN?|F~)!Yw٤2X-YpeC :\l"6cb/ЈG'( ;# on%(рnk5PRnA-Et#QnUF @.K^OC9 ӨSؠ:k?U赌0*1A9e9gӌnˆk؉^=uk 9\VzyD~y^/aȸDSst|*ZZa;ahZ[/°HA3}#P><<}<^|jE0?X`ʝmn&/?ߍipwatchd-1.3.0/INSTALL0000664000175000017500000000307313326442515013357 0ustar jariqjariqIPwatchD requirements --------------------- To build IPwatchD from source code you will need gcc, libpcap and libnet1 installed in your system. On Ubuntu 18.04 it should be sufficient to install following packages: - build-essential - C compiler and other development tools - libpcap-dev - Network packet capture library - libnet1-dev - Network packet construction library Building IPwatchD ----------------- Download IPwatchD source from sourceforge project page. Extract code and build it: $ gunzip ipwatchd-x.x.tar.gz $ tar -xf ipwatchd-x.x.tar $ cd ipwatchd-x.x/src $ make You can install IPwatchD as root with command: # make install You can uninstall IPwatchD as root with command: # make uninstall What gets installed ------------------- /etc/ipwatchd.conf - daemon configuration file /lib/systemd/system/ipwatchd.service - systemd service configuration /usr/sbin/ipwatchd - daemon executable /usr/sbin/ipwatchd-script - user-defined script /usr/share/man/man8/ipwatchd.8.gz - manual page /usr/share/man/man5/ipwatchd.conf.5.gz - manual page /usr/share/man/man1/ipwatchd-script.1.gz - manual page IPwatchD configuration ---------------------- Edit configuration file "/etc/ipwatchd.conf" with your favorite text editor to suite your needs. You can run IPwatchD as systemd managed service: # systemctl start ipwatchd Or you can run it directly with following command: # /usr/sbin/ipwatchd -c /etc/ipwatchd.conf For more information please read "ipwatchd" manual page. ipwatchd-1.3.0/src/0000775000175000017500000000000013326442515013112 5ustar jariqjariqipwatchd-1.3.0/src/devinfo.c0000664000175000017500000001552613326442515014721 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file devinfo.c * \brief Contains logic used for acquiring information about network devices */ #include "ipwatchd.h" extern IPWD_S_CONFIG config; extern IPWD_S_DEVS devices; //! Gets the IP and MAC addresses of specified device in human readable form /*! * Based on examples from: http://english.geekpage.jp/programming/linux-network/ * \param p_dev Name of the device (i.e. eth0) * \param p_ip Pointer to string where the IP address should be stored * \param p_mac Pointer to string where the MAC address should be stored * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_devinfo (const char *p_dev, char *p_ip, char *p_mac) { /* Create UDP socket */ int sock = -1; sock = socket (AF_INET, SOCK_DGRAM, 0); if (sock < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not open socket"); return (IPWD_RV_ERROR); } struct ifreq ifr; memset (&ifr, 0, sizeof(struct ifreq)); ifr.ifr_addr.sa_family = AF_INET; strcpy (ifr.ifr_name, p_dev); /* Get IP address of interface */ if (ioctl (sock, SIOCGIFADDR, &ifr) < 0) { /* Do not log error for interfaces without IP address */ if (errno == EADDRNOTAVAIL) { close (sock); return (IPWD_RV_ERROR); } ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not retrieve IP address of the device \"%s\" : %s", p_dev, strerror(errno)); close (sock); return (IPWD_RV_ERROR); } char *p_dev_ip = NULL; /* Following variable was added because gcc 4.4.1 displayed warning: dereferencing pointer ‘({anonymous})’ does break strict-aliasing rules */ struct in_addr sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; if ((p_dev_ip = inet_ntoa (sin_addr)) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not convert IP address of the device \"%s\"", p_dev); close (sock); return (IPWD_RV_ERROR); } memset (p_ip, '\0', IPWD_MAX_DEVICE_ADDRESS_LEN); strncpy (p_ip, p_dev_ip, IPWD_MAX_DEVICE_ADDRESS_LEN - 1); /* Get MAC address of interface */ if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not retrieve IP address of the device \"%s\"", p_dev); close (sock); return (IPWD_RV_ERROR); } char *p_dev_mac = NULL; if ((p_dev_mac = ether_ntoa ((const struct ether_addr *) &ifr.ifr_hwaddr.sa_data[0])) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not convert IP address of the device \"%s\"", p_dev); close (sock); return (IPWD_RV_ERROR); } memset (p_mac, '\0', IPWD_MAX_DEVICE_ADDRESS_LEN); strncpy (p_mac, p_dev_mac, IPWD_MAX_DEVICE_ADDRESS_LEN - 1); /* Close socket */ close (sock); ipwd_message (IPWD_MSG_TYPE_DEBUG, "Device info: %s %s-%s", p_dev, p_ip, p_mac); return (IPWD_RV_SUCCESS); } //! Gets list of available network interfaces and fills devices structure with acquired information /*! * Based on example from: http://www.doctort.org/adam/nerd-notes/enumerating-network-interfaces-on-linux.html * See netdevice(7) manual page for more information. * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_fill_devices (void) { int sock = -1; char ifaces_buf[10240]; struct ifconf ifc; struct ifreq * ifr = NULL; struct ifreq * iface = NULL; int ifaces_num = 0; int i = 0; pcap_t *h_pcap = NULL; char errbuf[PCAP_ERRBUF_SIZE]; memset (ifaces_buf, 0, sizeof (ifaces_buf)); memset (&ifc, 0, sizeof (ifc)); memset (errbuf, 0, PCAP_ERRBUF_SIZE); /* Verify that devices structure is empty and configuration mode is automatic */ if ((devices.dev != NULL) || (devices.devnum != 0) || (config.mode != IPWD_CONFIGURATION_MODE_AUTOMATIC)) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Cannot proceed with automatic configuration. Please check that configuration file does not contain iface variables"); return (IPWD_RV_ERROR); } /* Create UDP socket */ sock = socket (AF_INET, SOCK_DGRAM, 0); if (sock < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not open socket"); return (IPWD_RV_ERROR); } /* Set buffer */ ifc.ifc_len = sizeof (ifaces_buf); ifc.ifc_buf = ifaces_buf; /* Get list of interfaces */ if (ioctl (sock, SIOCGIFCONF, &ifc) < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not retrieve list of network interfaces"); close (sock); return (IPWD_RV_ERROR); } /* Determine number of interfaces */ ifaces_num = ifc.ifc_len / sizeof (struct ifreq); /* Get pointer to array with interfaces */ ifr = ifc.ifc_req; /* Loop through array with interfaces */ for (i = 0; i < ifaces_num; i++) { iface = &ifr[i]; /* Skip loopback devices */ if (ioctl(sock, SIOCGIFFLAGS, iface) < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not get interface flags for device \"%s\"", iface->ifr_name); continue; } if (iface->ifr_ifru.ifru_flags & IFF_LOOPBACK) { ipwd_message (IPWD_MSG_TYPE_DEBUG, "Skipping loopback device \"%s\"", iface->ifr_name); continue; } /* Check if device is valid ethernet device */ h_pcap = pcap_open_live (iface->ifr_name, BUFSIZ, 0, 0, errbuf); if (h_pcap == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "IPwatchD is unable to work with device \"%s\"", iface->ifr_name); continue; } if (pcap_datalink (h_pcap) != DLT_EN10MB) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Device \"%s\" is not valid ethernet device", iface->ifr_name); pcap_close (h_pcap); continue; } pcap_close (h_pcap); /* Put read values into devices structure */ if ((devices.dev = (IPWD_S_DEV *) realloc (devices.dev, (devices.devnum + 1) * sizeof (IPWD_S_DEV))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to resize devices structure"); close (sock); return (IPWD_RV_ERROR); } memset (devices.dev[devices.devnum].device, '\0', IPWD_MAX_DEVICE_NAME_LEN); strncpy (devices.dev[devices.devnum].device, iface->ifr_name, IPWD_MAX_DEVICE_NAME_LEN - 1); devices.dev[devices.devnum].mode = IPWD_PROTECTION_MODE_PASSIVE; /* Set time of last conflict */ devices.dev[devices.devnum].time.tv_sec = 0; devices.dev[devices.devnum].time.tv_usec = 0; ipwd_message (IPWD_MSG_TYPE_DEBUG, "Found device %s", devices.dev[devices.devnum].device); devices.devnum = devices.devnum + 1; } /* Close socket */ close (sock); return (IPWD_RV_SUCCESS); } ipwatchd-1.3.0/src/config.c0000664000175000017500000002306713326442515014533 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file config.c * \brief Contains logic used for parsing of configuration */ #include "ipwatchd.h" extern int debug_flag; extern IPWD_S_DEVS devices; extern IPWD_S_CONFIG config; //! Checks existence of the file /*! * \param filename Path to the file * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_file_exists (const char *filename) { FILE *fr = NULL; if ((fr = fopen (filename, "r")) == NULL) { return (IPWD_RV_ERROR); } if (fclose (fr) == EOF) { return (IPWD_RV_ERROR); } return (IPWD_RV_SUCCESS); } //! Reads configuration file and stores names of interfaces into the "devices" structure /*! * \param filename Path to the configuration file * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_read_config (const char *filename) { FILE *fr = NULL; char line[500]; int linenum = 0; char variable[100]; char value[400]; pcap_t *h_pcap = NULL; char errbuf[PCAP_ERRBUF_SIZE]; int iface_len = 0; // Initialize structures with default values config.facility = LOG_DAEMON; config.script = NULL; config.defend_interval = 0; config.mode = IPWD_CONFIGURATION_MODE_AUTOMATIC; devices.dev = NULL; devices.devnum = 0; if ((fr = fopen (filename, "r")) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to open configuration file %s", filename); return (IPWD_RV_ERROR); } memset (line, 0, sizeof (line)); /* Parse config file */ while (fgets (line, 499, fr) != NULL) { linenum = linenum + 1; memset (variable, 0, sizeof (variable)); memset (value, 0, sizeof (value)); if ((line[0] == '#') || (line[0] == '\n')) { continue; } if (sscanf (line, "%99s %399s", variable, value) != 2) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Not enough parameters in configuration file on line %d", linenum); return (IPWD_RV_ERROR); } /* Syslog Facility */ if (strcasecmp (variable, "syslog_facility") == 0) { if (strcasecmp (value, "auth") == 0) { config.facility = LOG_AUTH; continue; } else if (strcasecmp (value, "authpriv") == 0) { config.facility = LOG_AUTHPRIV; continue; } else if (strcasecmp (value, "cron") == 0) { config.facility = LOG_CRON; continue; } else if (strcasecmp (value, "daemon") == 0) { config.facility = LOG_DAEMON; continue; } else if (strcasecmp (value, "kern") == 0) { config.facility = LOG_KERN; continue; } else if (strcasecmp (value, "lpr") == 0) { config.facility = LOG_LPR; continue; } else if (strcasecmp (value, "mail") == 0) { config.facility = LOG_MAIL; continue; } else if (strcasecmp (value, "news") == 0) { config.facility = LOG_NEWS; continue; } else if (strcasecmp (value, "syslog") == 0) { config.facility = LOG_SYSLOG; continue; } else if (strcasecmp (value, "user") == 0) { config.facility = LOG_USER; continue; } else if (strcasecmp (value, "uucp") == 0) { config.facility = LOG_UUCP; continue; } else if (strcasecmp (value, "local0") == 0) { config.facility = LOG_LOCAL0; continue; } else if (strcasecmp (value, "local1") == 0) { config.facility = LOG_LOCAL1; continue; } else if (strcasecmp (value, "local2") == 0) { config.facility = LOG_LOCAL2; continue; } else if (strcasecmp (value, "local3") == 0) { config.facility = LOG_LOCAL3; continue; } else if (strcasecmp (value, "local4") == 0) { config.facility = LOG_LOCAL4; continue; } else if (strcasecmp (value, "local5") == 0) { config.facility = LOG_LOCAL5; continue; } else if (strcasecmp (value, "local6") == 0) { config.facility = LOG_LOCAL6; continue; } else if (strcasecmp (value, "local7") == 0) { config.facility = LOG_LOCAL7; continue; } else { ipwd_message (IPWD_MSG_TYPE_ERROR, "Configuration parse error : %s as a value of syslog_facility is not supported", value); return (IPWD_RV_ERROR); } } /* Path to user-defined script */ if (strcasecmp (variable, "user_script") == 0) { if (ipwd_file_exists (value) == IPWD_RV_ERROR) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Configuration parse error : file %s specified as user_script does not exist", value); return (IPWD_RV_ERROR); } if ((config.script = (char *) malloc ((strlen (value) + 1) * sizeof (char))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Configuration parse error : malloc for user_script failed"); return (IPWD_RV_ERROR); } strcpy (config.script, value); continue; } /* Minimum interval between defensive ARPs */ if (strcasecmp (variable, "defend_interval") == 0) { config.defend_interval = strtol (value, NULL, 10); if ((config.defend_interval < 0) || (config.defend_interval > 600)) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Configuration parse error : defend_interval value must be between 0 and 600"); return (IPWD_RV_ERROR); } continue; } /* Configuration mode for network interfaces */ if (strcasecmp (variable, "iface_configuration") == 0) { /* Check mode value */ if ((strcasecmp (value, "automatic") != 0) && (strcasecmp (value, "manual") != 0)) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Configuration mode \"%s\" on line %d in configuration file not supported", value, linenum); return (IPWD_RV_ERROR); } /* Switch to manual mode if requested */ if (strcasecmp (value, "manual") == 0) { config.mode = IPWD_CONFIGURATION_MODE_MANUAL; continue; } /* Automatic mode is default */ if (ipwd_fill_devices () != IPWD_RV_SUCCESS) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Automatic configuration mode failed. Please switch to manual configuration mode."); return (IPWD_RV_ERROR); } continue; } /* Monitored interfaces */ if (strcasecmp (variable, "iface") == 0) { /* Check if configuration mode is manual */ if (config.mode != IPWD_CONFIGURATION_MODE_MANUAL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Found iface variable in automatic configuration mode. Please check configuration file for logical errors"); return (IPWD_RV_ERROR); } /* Read interface name and protection mode */ if (sscanf (line, "%*s %93s %399s", variable, value) != 2) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Not enough parameters in configuration file on line %d", linenum); return (IPWD_RV_ERROR); } /* Check if device is valid ethernet device */ h_pcap = pcap_open_live (variable, BUFSIZ, 0, 0, errbuf); if (h_pcap == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "IPwatchD is unable to work with interface \"%s\"", variable); return (IPWD_RV_ERROR); } if (pcap_datalink (h_pcap) != DLT_EN10MB) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Device \"%s\" is not valid ethernet interface", variable); return (IPWD_RV_ERROR); } pcap_close (h_pcap); /* Check mode value */ if ((strcasecmp (value, "active") != 0) && (strcasecmp (value, "passive") != 0)) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Protection mode \"%s\" on line %d in configuration file not supported", value, linenum); return (IPWD_RV_ERROR); } /* Put read values into devices structure */ if ((devices.dev = (IPWD_S_DEV *) realloc (devices.dev, (devices.devnum + 1) * sizeof (IPWD_S_DEV))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to resize devices structure"); return (IPWD_RV_ERROR); } memset (devices.dev[devices.devnum].device, '\0', IPWD_MAX_DEVICE_NAME_LEN); iface_len = snprintf (devices.dev[devices.devnum].device, IPWD_MAX_DEVICE_NAME_LEN, "%s", variable); if (iface_len < 1 || iface_len > IPWD_MAX_DEVICE_NAME_LEN - 1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Interface name \"%s\" is not valid", variable); return (IPWD_RV_ERROR); } if (strcasecmp (value, "active") == 0) { devices.dev[devices.devnum].mode = IPWD_PROTECTION_MODE_ACTIVE; } else { devices.dev[devices.devnum].mode = IPWD_PROTECTION_MODE_PASSIVE; } /* Set time of last conflict */ devices.dev[devices.devnum].time.tv_sec = 0; devices.dev[devices.devnum].time.tv_usec = 0; ipwd_message (IPWD_MSG_TYPE_DEBUG, "Found interface %s", devices.dev[devices.devnum].device); devices.devnum = devices.devnum + 1; } memset (line, 0, sizeof (line)); } if (fclose (fr) == EOF) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to close configuration file %s", filename); return (IPWD_RV_ERROR); } /* Check number of discovered interfaces */ if (devices.devnum < 1) { if (config.mode == IPWD_CONFIGURATION_MODE_MANUAL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "No interfaces specified in configuration file"); return (IPWD_RV_ERROR); } else { ipwd_message (IPWD_MSG_TYPE_ERROR, "No interfaces discovered"); return (IPWD_RV_ERROR); } } return (IPWD_RV_SUCCESS); } ipwatchd-1.3.0/src/ipwatchd.c0000664000175000017500000001627213326442515015071 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file ipwatchd.c * \brief Main source file of the project */ #include "ipwatchd.h" //! Flag indicating debug mode int debug_flag = 0; //! Flag indicating that output of program must be recorded by syslog int syslog_flag = 0; //! Flag indicating testing mode when every ARP packet is considered to be conflicting int testing_flag = 0; //! Structure that holds information about network interfaces IPWD_S_DEVS devices; //! Structure that holds values of particular configuration variables IPWD_S_CONFIG config; //! Handle for libpcap pcap_t *h_pcap = NULL; //! Main function of the ipwatchd program /*! * \param argc Number of received command line arguments * \param argv Argument values * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int main (int argc, char *argv[]) { /* Name of configuration file */ char *config_file = NULL; int c = 0; int option_index = 0; /* Open connection to syslog with default daemon facility */ openlog ("ipwatchd", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_DAEMON); /* Parse command line arguments */ while (1) { static struct option long_options[] = { { "config", required_argument, 0, 'c' }, { "debug", no_argument, 0, 'd' }, { "test", no_argument, 0, 't' }, { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { 0, 0, 0, 0 } }; c = getopt_long (argc, argv, "c:dthv", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'c': if (ipwd_file_exists (optarg) == IPWD_RV_ERROR) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to open configuration file %s", optarg); return (IPWD_RV_ERROR); } if ((config_file = (char *) malloc ((strlen (optarg) + 1) * sizeof (char))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to open configuration file %s - malloc failed", optarg); return (IPWD_RV_ERROR); } strcpy (config_file, optarg); break; case 'd': debug_flag = 1; break; case 't': testing_flag = 1; break; case 'h': ipwd_print_help (); return (IPWD_RV_SUCCESS); case 'v': ipwd_message (IPWD_MSG_TYPE_INFO, IPWATCHD_VERSION); return (IPWD_RV_SUCCESS); case '?': ipwd_message (IPWD_MSG_TYPE_ERROR, "Try %s --help", argv[0]); return (IPWD_RV_ERROR); default: ipwd_print_help (); return (IPWD_RV_ERROR); } } /* Print help if there is any unknown argument */ if (optind < argc) { ipwd_print_help (); return (IPWD_RV_ERROR); } /* Path to configuration file must be specified */ if (config_file == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "You must specify path to configuration file."); ipwd_message (IPWD_MSG_TYPE_ERROR, "Try %s --help", argv[0]); return (IPWD_RV_ERROR); } /* Only root can run IPwatchD */ if (getuid () != 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "You must be root to run IPwatchD"); return (IPWD_RV_ERROR); } /* Read config file */ if (ipwd_read_config (config_file) == IPWD_RV_ERROR) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to read configuration file"); return (IPWD_RV_ERROR); } free (config_file); config_file = NULL; /* Daemonize */ if (ipwd_daemonize () != IPWD_RV_SUCCESS) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to daemonize"); return (IPWD_RV_ERROR); } ipwd_message (IPWD_MSG_TYPE_INFO, "IPwatchD started"); char errbuf[PCAP_ERRBUF_SIZE]; struct bpf_program fp; /* Check if "any" pseudodevice is available */ /* IPwatchD cannot be used on Debian GNU/kFreeBSD because of the lack of this device */ pcap_if_t * pcap_alldevs = NULL; pcap_if_t * pcap_dev = NULL; int any_exists = 0; if (pcap_findalldevs(&pcap_alldevs, errbuf)) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to get network device list - %s", errbuf); return (IPWD_RV_ERROR); } for (pcap_dev = pcap_alldevs; pcap_dev; pcap_dev = pcap_dev->next) { if (strcasecmp (pcap_dev->name, "any") == 0) { any_exists = 1; break; } } if (!any_exists) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Pseudodevice \"any\" used by libpcap is not available"); return (IPWD_RV_ERROR); } /* Initialize libpcap and listen on all interfaces */ h_pcap = pcap_open_live ("any", BUFSIZ, 0, 0, errbuf); if (h_pcap == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to create packet capture object - %s", errbuf); return (IPWD_RV_ERROR); } /* Set SIGTERM handler */ if (ipwd_set_signal_handler () != IPWD_RV_SUCCESS) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to set signal handlers"); return (IPWD_RV_ERROR); } /* Compile packet capture filter - only ARP packets will be captured */ if (pcap_compile (h_pcap, &fp, "arp", 0, 0) == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to compile packet capture filter - %s", pcap_geterr (h_pcap)); return (IPWD_RV_ERROR); } /* Set packet capture filter */ if (pcap_setfilter (h_pcap, &fp) == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to set packet capture filter - %s", pcap_geterr (h_pcap)); return (IPWD_RV_ERROR); } pcap_freecode (&fp); ipwd_message (IPWD_MSG_TYPE_DEBUG, "Entering pcap loop"); /* Loop until SIGTERM calls pcap_breakloop */ pcap_loop (h_pcap, -1, ipwd_analyse, NULL); pcap_close (h_pcap); /* Stop IPwatchD */ ipwd_message (IPWD_MSG_TYPE_INFO, "IPwatchD stopped"); closelog (); if (config.script != NULL) { free (config.script); config.script = NULL; } if (devices.dev != NULL) { free (devices.dev); devices.dev = NULL; } return (IPWD_RV_SUCCESS); } //! Prints help to the stdout void ipwd_print_help (void) { fprintf (stdout, "IPwatchD - IP conflict detection tool for Linux\n"); fprintf (stdout, "\n"); fprintf (stdout, "IPwatchD is simple daemon that analyses all incoming ARP\n"); fprintf (stdout, "packets in order to detect IP conflicts.\n"); fprintf (stdout, "\n"); fprintf (stdout, "Usage: ipwatchd --config config_file [--debug] [--test]\n"); fprintf (stdout, "\n"); fprintf (stdout, " -c | --config config_file - Path to configuration file\n"); fprintf (stdout, " -d | --debug - Run in debug mode\n"); fprintf (stdout, " -t | --test - Run in testing mode\n"); fprintf (stdout, "\n"); fprintf (stdout, "or ipwatchd --version|--help\n"); fprintf (stdout, "\n"); fprintf (stdout, " -v | --version - Prints program version\n"); fprintf (stdout, " -v | --help - Displays this help message\n"); fprintf (stdout, "\n"); fprintf (stdout, "Please send any bug reports to jariq@jariq.sk\n"); } ipwatchd-1.3.0/src/ipwatchd.conf0000664000175000017500000000033713326442515015567 0ustar jariqjariq# IPwatchD configuration file # See ipwatchd.conf(5) for more details iface_configuration automatic #iface eth0 active #iface eth0:0 passive defend_interval 10 user_script /usr/sbin/ipwatchd-script syslog_facility daemon ipwatchd-1.3.0/src/genarp.c0000664000175000017500000001155413326442515014540 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file genarp.c * \brief Contains logic used for generation of ARP packets */ #include "ipwatchd.h" //! Generates ARP packet with libnet1 /*! * \param dev Name of the device * \param p_sip Source IP address * \param p_smac Source MAC address * \param p_dip Destination IP address * \param p_dmac Destination MAC address * \param opcode ARPOP_REQUEST or ARPOP_REPLY * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_genarp (const char *dev, const char *p_sip, const char *p_smac, const char *p_dip, const char *p_dmac, int opcode) { struct in_addr sip, dip; struct ether_addr smac, dmac; /* Convert source IP address */ if (inet_aton (p_sip, &sip) == 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to convert source IP address %s", p_sip); return (IPWD_RV_ERROR); } /* Convert destination IP address */ if (inet_aton (p_dip, &dip) == 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to convert destination IP address %s", p_dip); return (IPWD_RV_ERROR); } /* Convert source MAC address */ if (ether_aton_r (p_smac, &smac) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to convert source MAC address %s", p_smac); return (IPWD_RV_ERROR); } /* Convert destination MAC address */ if (ether_aton_r (p_dmac, &dmac) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to convert destination MAC address %s", p_dmac); return (IPWD_RV_ERROR); } /* Set opcode once again. Just in case.. */ if (opcode != ARPOP_REQUEST) { opcode = ARPOP_REPLY; } libnet_t *h_net = NULL; char errbuf[LIBNET_ERRBUF_SIZE]; /* Initialize libnet */ h_net = libnet_init (LIBNET_LINK_ADV, (char *) dev, errbuf); if (h_net == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to initialize libnet1 - %s", errbuf); return (IPWD_RV_ERROR); } /* Gratuitous ARP request will be created if destination MAC address is broadcast. * GARP requests from Windows and OpenBSD have destination MAC in ARP header * always set to 00:00:00:00:00:00 so we will do the same thing */ if ((strcasecmp (p_dmac, "ff:ff:ff:ff:ff:ff") == 0) && (opcode == ARPOP_REQUEST)) { struct ether_addr nullmac; char *null_mac = "00:00:00:00:00:00"; /* Convert null MAC address */ if (ether_aton_r (null_mac, &nullmac) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to convert destination MAC address for gratuitous ARP request"); libnet_destroy (h_net); return (IPWD_RV_ERROR); } /* Build ARP header for gratuitous ARP packet */ libnet_ptag_t arp = 0; arp = libnet_build_arp ( ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, opcode, (u_int8_t *) & smac, (u_int8_t *) & sip, (u_int8_t *) & nullmac, (u_int8_t *) & dip, NULL, 0, h_net, arp ); if (arp == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to build ARP header: %s", libnet_geterror (h_net)); libnet_destroy (h_net); return (IPWD_RV_ERROR); } } else { /* Build ARP header for normal ARP packet */ libnet_ptag_t arp = 0; arp = libnet_build_arp ( ARPHRD_ETHER, ETHERTYPE_IP, 6, 4, opcode, (u_int8_t *) & smac, (u_int8_t *) & sip, (u_int8_t *) & dmac, (u_int8_t *) & dip, NULL, 0, h_net, arp); if (arp == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to build ARP header: %s", libnet_geterror (h_net)); libnet_destroy (h_net); return (IPWD_RV_ERROR); } } /* Build ethernet header */ libnet_ptag_t ether = 0; ether = libnet_build_ethernet ((u_int8_t *) & dmac, (u_int8_t *) & smac, ETHERTYPE_ARP, NULL, 0, h_net, ether); if (ether == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to build ethernet header: %s", libnet_geterror (h_net)); libnet_destroy (h_net); return (IPWD_RV_ERROR); } /* Send packet */ int c = libnet_write (h_net); if (c == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to send packet: %s", libnet_geterror (h_net)); libnet_destroy (h_net); return (IPWD_RV_ERROR); } else { ipwd_message (IPWD_MSG_TYPE_DEBUG, "Packet with size of %d bytes sent", c); } libnet_destroy (h_net); return (IPWD_RV_SUCCESS); } ipwatchd-1.3.0/src/message.c0000664000175000017500000000456613326442515014715 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file message.c * \brief Contains logic used for manipulating program messages */ #include "ipwatchd.h" extern int debug_flag; extern int syslog_flag; //! Handles output of messages generated by IPwatchD /*! * \param type Type of message. * \param format Message to output in format similar to printf. */ void ipwd_message (IPWD_MSG_TYPE type, const char *format, ...) { /* Handle debug mode first for efficiency */ if ((type == IPWD_MSG_TYPE_DEBUG) && (!debug_flag)) { return; } va_list arguments; char msg[IPWD_MSG_BUFSIZ]; /* Put formatted message to msg buffer */ va_start(arguments, format); vsnprintf(msg, IPWD_MSG_BUFSIZ, format, arguments); va_end(arguments); /* Every message is recorded by syslog no matter if process is daemonized or not */ switch (type) { case IPWD_MSG_TYPE_INFO: syslog (LOG_INFO, "%s", msg); break; case IPWD_MSG_TYPE_ERROR: syslog (LOG_ERR, "%s", msg); break; case IPWD_MSG_TYPE_ALERT: syslog (LOG_ALERT, "%s", msg); break; case IPWD_MSG_TYPE_DEBUG: syslog (LOG_DEBUG, "%s", msg); break; default: syslog (LOG_ERR, "%s", msg); break; } /* Output message also to terminal if process is not daemonized */ if (!syslog_flag) { switch (type) { case IPWD_MSG_TYPE_INFO: fprintf (stdout, "%s\n", msg); break; case IPWD_MSG_TYPE_ERROR: fprintf (stderr, "%s\n", msg); break; case IPWD_MSG_TYPE_ALERT: fprintf (stderr, "%s\n", msg); break; case IPWD_MSG_TYPE_DEBUG: fprintf (stdout, "%s\n", msg); break; default: fprintf (stderr, "%s\n", msg); break; } } } ipwatchd-1.3.0/src/ipwatchd.service0000664000175000017500000000040013326442515016271 0ustar jariqjariq[Unit] Description=IP conflict detection daemon Documentation=man:ipwatchd(8) man:ipwatchd.conf(5) man:ipwatchd-script(1) After=network.target [Service] Type=forking ExecStart=/usr/sbin/ipwatchd -c /etc/ipwatchd.conf [Install] WantedBy=multi-user.target ipwatchd-1.3.0/src/daemonize.c0000664000175000017500000001354513326442515015241 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file daemonize.c * \brief Contains logic used for daemonization of the proccess */ #include "ipwatchd.h" extern int syslog_flag; extern IPWD_S_CONFIG config; //! Daemonizes the proccess /*! * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_daemonize (void) { /* Check if ipwatchd is running - only one daemon instance is allowed */ if (ipwd_check_pidfile () != IPWD_RV_SUCCESS) { return (IPWD_RV_ERROR); } /* Fork child process */ pid_t pid = fork (); if (pid < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to fork a child process"); return (IPWD_RV_ERROR); } /* Fork was successful we can exit parent */ if (pid > 0) { exit (IPWD_RV_SUCCESS); } /* All messages must be sysloged since now */ openlog ("ipwatchd", LOG_PID | LOG_CONS | LOG_NDELAY, config.facility); syslog_flag = 1; /* Set default umask */ umask (0166); /* Create new session */ pid_t sid = setsid (); if (sid < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to create a new session"); return (IPWD_RV_ERROR); } /* Change current directory to root */ if ((chdir ("/")) == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to change current directory to /"); return (IPWD_RV_ERROR); } /* Redirect standard input */ if ((freopen ("/dev/null", "r", stdin)) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to redirect STDIN"); return (IPWD_RV_ERROR); } /* Redirect standard output */ if ((freopen ("/dev/null", "w", stdout)) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to redirect STDOUT"); return (IPWD_RV_ERROR); } /* Redirect standard error output */ if ((freopen ("/dev/null", "w", stderr)) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to redirect STDERR"); return (IPWD_RV_ERROR); } /* Create PID file */ if (ipwd_create_pidfile () != IPWD_RV_SUCCESS) { return (IPWD_RV_ERROR); } return (IPWD_RV_SUCCESS); } //! Creates PID file /*! * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_create_pidfile (void) { FILE * fw = NULL; if ((fw = fopen (IPWD_PIDFILE, "w")) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to open PID file %s", IPWD_PIDFILE); return (IPWD_RV_ERROR); } if (fprintf (fw, "%d", getpid()) < 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to write process PID into PID file %s", IPWD_PIDFILE); fclose (fw); return (IPWD_RV_ERROR); } if (fclose (fw) == EOF) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to close PID file %s", IPWD_PIDFILE); return (IPWD_RV_ERROR); } return (IPWD_RV_SUCCESS); } //! Checks if process is already running /*! * \return IPWD_RV_SUCCESS if process is not running IPWD_RV_ERROR otherwise */ int ipwd_check_pidfile (void) { FILE * fr = NULL; char proc_this_lnk[PATH_MAX]; char proc_this_bin[PATH_MAX]; int proc_this_pid = -1; char proc_pidfile_lnk[PATH_MAX]; char proc_pidfile_bin[PATH_MAX]; int proc_pidfile_pid = -1; /* Fill buffers with nulls */ memset (proc_this_lnk, '\0', PATH_MAX); memset (proc_this_bin, '\0', PATH_MAX); memset (proc_pidfile_lnk, '\0', PATH_MAX); memset (proc_pidfile_bin, '\0', PATH_MAX); /* Determine absolute path of executable for current process */ proc_this_pid = getpid (); snprintf (proc_this_lnk, PATH_MAX, "/proc/%d/exe", proc_this_pid); if (readlink (proc_this_lnk, proc_this_bin, PATH_MAX) <= 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to get information about current process"); return (IPWD_RV_ERROR); } /* Read contents of PID file */ if ((fr = fopen (IPWD_PIDFILE, "r")) == NULL) { if (errno == ENOENT) { ipwd_message (IPWD_MSG_TYPE_DEBUG, "Daemon can be executed because PID file %s does not exist", IPWD_PIDFILE); return (IPWD_RV_SUCCESS); } ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to open PID file %s", IPWD_PIDFILE); return (IPWD_RV_ERROR); } if (fscanf (fr, "%d", &proc_pidfile_pid) != 1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to read PID from PID file %s", IPWD_PIDFILE); fclose (fr); return (IPWD_RV_ERROR); } if (fclose (fr) == EOF) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to close PID file %s", IPWD_PIDFILE); return (IPWD_RV_ERROR); } /* Determine absolute path of executable for process specified in PID file */ snprintf (proc_pidfile_lnk, PATH_MAX, "/proc/%d/exe", proc_pidfile_pid); if (readlink (proc_pidfile_lnk, proc_pidfile_bin, PATH_MAX) <= 0) { if (errno == ENOENT) { ipwd_message (IPWD_MSG_TYPE_DEBUG, "Daemon can be executed because process specified in PID file %s does not exist", IPWD_PIDFILE); return (IPWD_RV_SUCCESS); } ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to get information about process specified in PID file"); return (IPWD_RV_ERROR); } /* Compare absolute path of executable for current process with absolute path of executable for process specified in PID file */ if (strcmp (proc_this_bin, proc_pidfile_bin) == 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "IPwatchD is already running"); return (IPWD_MSG_TYPE_ERROR); } // ipwd_message (IPWD_MSG_INFO, "Daemon can be executed because process specified in PID file is not IPwatchD"); return (IPWD_RV_SUCCESS); } ipwatchd-1.3.0/src/Makefile0000664000175000017500000000360713326442515014560 0ustar jariqjariq# IPwatchD - IP conflict detection tool for Linux # Copyright (C) 2007-2018 Jaroslav Imrich CC= gcc CFLAGS= -g -Wall -O2 LIBS= -lpcap -lnet all: analyse.o config.o daemonize.o devinfo.o genarp.o ipwatchd.o message.o signal.o $(CC) $(CFLAGS) $(LDFLAGS) analyse.o config.o daemonize.o devinfo.o genarp.o \ ipwatchd.o message.o signal.o -o ipwatchd $(LIBS) analyse.o: analyse.c ipwatchd.h $(CC) $(CFLAGS) -c analyse.c -o analyse.o config.o: config.c ipwatchd.h $(CC) $(CFLAGS) -c config.c -o config.o daemonize.o: daemonize.c ipwatchd.h $(CC) $(CFLAGS) -c daemonize.c -o daemonize.o devinfo.o: devinfo.c ipwatchd.h $(CC) $(CFLAGS) -c devinfo.c -o devinfo.o genarp.o: genarp.c ipwatchd.h $(CC) $(CFLAGS) -c genarp.c -o genarp.o ipwatchd.o: ipwatchd.c ipwatchd.h $(CC) $(CFLAGS) -c ipwatchd.c -o ipwatchd.o message.o: message.c ipwatchd.h $(CC) $(CFLAGS) -c message.c -o message.o signal.o: signal.c ipwatchd.h $(CC) $(CFLAGS) -c signal.c -o signal.o clean: -rm *.o distclean: clean -rm ipwatchd install: mkdir -p $(DESTDIR)/etc mkdir -p $(DESTDIR)/lib/systemd/system mkdir -p $(DESTDIR)/usr/sbin mkdir -p $(DESTDIR)/usr/share/man/man8 mkdir -p $(DESTDIR)/usr/share/man/man5 mkdir -p $(DESTDIR)/usr/share/man/man1 cp ipwatchd $(DESTDIR)/usr/sbin cp ipwatchd-script $(DESTDIR)/usr/sbin cp ipwatchd.service $(DESTDIR)/lib/systemd/system cp ipwatchd.conf $(DESTDIR)/etc cp ../doc/ipwatchd.8.gz $(DESTDIR)/usr/share/man/man8 cp ../doc/ipwatchd.conf.5.gz $(DESTDIR)/usr/share/man/man5 cp ../doc/ipwatchd-script.1.gz $(DESTDIR)/usr/share/man/man1 uninstall: rm $(DESTDIR)/usr/sbin/ipwatchd rm $(DESTDIR)/usr/sbin/ipwatchd-script rm $(DESTDIR)/lib/systemd/system/ipwatchd.service rm $(DESTDIR)/etc/ipwatchd.conf rm $(DESTDIR)/usr/share/man/man8/ipwatchd.8.gz rm $(DESTDIR)/usr/share/man/man5/ipwatchd.conf.5.gz rm $(DESTDIR)/usr/share/man/man1/ipwatchd-script.1.gz ipwatchd-1.3.0/src/signal.c0000664000175000017500000000313613326442515014536 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file signal.c * \brief Contains logic used for signal handling */ #include "ipwatchd.h" extern pcap_t *h_pcap; //! Sets signal handler for SIGTERM /*! * \return IPWD_RV_SUCCESS if successful IPWD_RV_ERROR otherwise */ int ipwd_set_signal_handler (void) { struct sigaction sigact; sigemptyset (&sigact.sa_mask); sigact.sa_flags = 0; sigact.sa_handler = ipwd_signal_handler; if (sigaction (SIGTERM, &sigact, 0) != 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to set SIGTERM handler"); return (IPWD_RV_ERROR); } return (IPWD_RV_SUCCESS); } //! Signal handler that is called when signal received /*! * \param signal Signal identifier */ void ipwd_signal_handler (int signal) { ipwd_message (IPWD_MSG_TYPE_DEBUG, "Received signal %d", signal); if (signal == SIGTERM) { pcap_breakloop (h_pcap); } } ipwatchd-1.3.0/src/ipwatchd.h0000664000175000017500000001352513326442515015074 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file ipwatchd.h * \brief Main header file of the project */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //! String with IPwatchD version information #define IPWATCHD_VERSION "IPwatchD 1.3.0" //! Absolute path to pid file #define IPWD_PIDFILE "/var/run/ipwatchd.pid" /* Return values */ //! "Success" return value of IPwatchD internal functions #define IPWD_RV_SUCCESS 0 //! "Error" return value of IPwatchD internal functions #define IPWD_RV_ERROR 2 /* Message options */ //! Size of buffer for output messages #define IPWD_MSG_BUFSIZ 1024 //! Message type typedef enum { IPWD_MSG_TYPE_INFO = 1, /**< Message type: information */ IPWD_MSG_TYPE_ERROR = 2, /**< Message type: error */ IPWD_MSG_TYPE_ALERT = 3, /**< Message type: alert */ IPWD_MSG_TYPE_DEBUG = 4 /**< Message type: debug */ } IPWD_MSG_TYPE; /* File operation options */ //! Size of buffer used for reading content of files #define IPWD_FILE_BUFSIZ 1000 /* Operation modes */ //! Protection modes typedef enum { IPWD_PROTECTION_MODE_ACTIVE = 1, /**< Indicates active protection mode */ IPWD_PROTECTION_MODE_PASSIVE = 2, /**< Indicates passive protection mode */ } IPWD_PROTECTION_MODE; //! Configuration modes typedef enum { IPWD_CONFIGURATION_MODE_AUTOMATIC = 1, /**< Indicates automatic configuration mode */ IPWD_CONFIGURATION_MODE_MANUAL = 2 /**< Indicates manual configuration mode */ } IPWD_CONFIGURATION_MODE; /* Configuration */ //! Structure that holds values of particular configuration variables typedef struct { int facility; /**< Syslog facility */ char * script; /**< Absolute path to user-defined script */ int defend_interval; /**< Minimum interval between defensive ARPs */ IPWD_CONFIGURATION_MODE mode; /**< Configuration mode for network devices */ } IPWD_S_CONFIG; /* Network device information */ //! Size of buffer used for the name of the device #define IPWD_MAX_DEVICE_NAME_LEN IFNAMSIZ //! Size of buffer used for IP and MAC address of the device #define IPWD_MAX_DEVICE_ADDRESS_LEN 20 //! State of the device device indicating if it should be used in conflict detection process typedef enum { IPWD_DEVICE_STATE_USABLE = 1, /**< Device should be used in conflict detection process */ IPWD_DEVICE_STATE_UNUSABLE = 2 /**< Device should not be used in conflict detection process */ } IPWD_DEVICE_STATE; //! Structure that holds information about ONE network interface typedef struct { char device[IPWD_MAX_DEVICE_NAME_LEN]; /**< Device name */ IPWD_DEVICE_STATE state; /**< Indicates if device should be used in conflict detection process */ char ip[IPWD_MAX_DEVICE_ADDRESS_LEN]; /**< IP address of device */ char mac[IPWD_MAX_DEVICE_ADDRESS_LEN]; /**< MAC address of device */ IPWD_PROTECTION_MODE mode; /**< IPwatch mode on interface */ struct timeval time; /**< Time information indicating when the last conflict was detected */ } IPWD_S_DEV; //! Structure that holds information about ALL network interfaces typedef struct { IPWD_S_DEV *dev; /**< Dynamicaly allocated array of IPWD_S_DEV structures */ int devnum; /**< Number of watched interfaces */ } IPWD_S_DEVS; /* ARP packet information */ //! Number of useless bytes in ARP packet /*! Parsing of packets captured by libpcap on pseudo device "any" seems to be * slightly different than parsing of packets captured on one particular interface. * Instead of usual 14th byte addresses begin on 24th byte of ARP header. */ #define IPWD_ARP_HEADER_SIZE 24 //! Structure useful for parsing of individual addresses from packet typedef struct { u_int8_t arp_sha[ETH_ALEN]; /**< Source MAC address */ u_int8_t arp_spa[4]; /**< Source IP address */ u_int8_t arp_tha[ETH_ALEN]; /**< Destination MAC address */ u_int8_t arp_tpa[4]; /**< Destination IP address */ } IPWD_S_ARP_HEADER; /* IPwatchD internal functions - described in corresponding source files */ // \cond Doxygen ignore block start /* analyse.c */ void ipwd_analyse (u_char * args, const struct pcap_pkthdr *header, const u_char * packet); /* config.c */ int ipwd_file_exists (const char *filename); int ipwd_read_config (const char *filename); /* daemonize.c */ int ipwd_daemonize (void); int ipwd_create_pidfile (void); int ipwd_check_pidfile (void); /* devinfo.c */ int ipwd_devinfo (const char *p_dev, char *p_ip, char *p_mac); int ipwd_fill_devices (void); /* genarp.c */ int ipwd_genarp (const char *dev, const char *p_sip, const char *p_smac, const char *p_dip, const char *p_dmac, int opcode); /* ipwatchd.c */ void ipwd_print_help (void); /* message.c */ void ipwd_message (IPWD_MSG_TYPE type, const char *format, ...); /* signal.c */ int ipwd_set_signal_handler (void); void ipwd_signal_handler (int signal); // \endcond Doxygen ignore block end ipwatchd-1.3.0/src/ipwatchd-script0000775000175000017500000000103013326442515016137 0ustar jariqjariq#!/bin/sh # This user-defined script is called by IPwatchD daemon when IP conflict occurs. # Feel free to modify it to suit your needs. # Name of network device in conflict DEVICE=$1 # IP address in conflict IP=$2 # MAC address of conflicting system MAC=$3 # Run notification tool for Gnome environment if [ -x /usr/sbin/ipwatchd-gnotify ]; then /usr/sbin/ipwatchd-gnotify \ --broadcast \ --title "IP conflict occurred" \ --message "MAC address $MAC causes IP conflict with address $IP set on interface $DEVICE" fi exit 0 ipwatchd-1.3.0/src/analyse.c0000664000175000017500000001714313326442515014720 0ustar jariqjariq/* IPwatchD - IP conflict detection tool for Linux * Copyright (C) 2007-2018 Jaroslav Imrich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /** \file analyse.c * \brief Contains logic used for analysing of captured ARP packets */ #include "ipwatchd.h" extern IPWD_S_DEVS devices; extern IPWD_S_CONFIG config; extern int testing_flag; //! Callback for "pcap_loop" with standard parameters. Called when ARP packet is received (detection of conflict is done here). /*! * \param args Last parameter of pcap_loop * \param header Packet header * \param packet Packet data */ void ipwd_analyse (u_char * args, const struct pcap_pkthdr *header, const u_char * packet) { struct timeval current_time; double difference = 0.0; char * command = NULL; int command_len = 0; int rv = 0; int i = 0; /* Get addresses from packet */ IPWD_S_ARP_HEADER *arpaddr; arpaddr = (IPWD_S_ARP_HEADER *) (packet + IPWD_ARP_HEADER_SIZE); /* Source IP address */ char rcv_sip[IPWD_MAX_DEVICE_ADDRESS_LEN]; char *p_rcv_sip = NULL; /* Following variable was added because gcc 4.4.1 displayed warning: dereferencing type-punned pointer will break strict-aliasing rules */ struct in_addr * p_arp_spa = (struct in_addr *) &arpaddr->arp_spa; if ((p_rcv_sip = inet_ntoa (*(p_arp_spa))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not get source IP address from packet"); return; } memset (rcv_sip, '\0', IPWD_MAX_DEVICE_ADDRESS_LEN); strncpy (rcv_sip, p_rcv_sip, IPWD_MAX_DEVICE_ADDRESS_LEN - 1); /* Source MAC address */ char rcv_smac[IPWD_MAX_DEVICE_ADDRESS_LEN]; char *p_rcv_smac = NULL; if ((p_rcv_smac = ether_ntoa ((const struct ether_addr *) &arpaddr->arp_sha)) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not get source MAC address from packet"); return; } memset (rcv_smac, '\0', IPWD_MAX_DEVICE_ADDRESS_LEN); strncpy (rcv_smac, p_rcv_smac, IPWD_MAX_DEVICE_ADDRESS_LEN - 1); /* Destination IP address */ char rcv_dip[IPWD_MAX_DEVICE_ADDRESS_LEN]; char *p_rcv_dip = NULL; /* Following variable was added because gcc 4.4.1 displayed warning: dereferencing type-punned pointer will break strict-aliasing rules */ struct in_addr * p_arp_tpa = (struct in_addr *) &arpaddr->arp_tpa; if ((p_rcv_dip = inet_ntoa (*(p_arp_tpa))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not get destination IP address from packet"); return; } memset (rcv_dip, '\0', IPWD_MAX_DEVICE_ADDRESS_LEN); strncpy (rcv_dip, p_rcv_dip, IPWD_MAX_DEVICE_ADDRESS_LEN - 1); /* Destination MAC address */ char rcv_dmac[IPWD_MAX_DEVICE_ADDRESS_LEN]; char *p_rcv_dmac = NULL; if ((p_rcv_dmac = ether_ntoa ((const struct ether_addr *) &arpaddr->arp_tha)) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Could not get destination MAC address from packet"); return; } memset (rcv_dmac, '\0', IPWD_MAX_DEVICE_ADDRESS_LEN); strncpy (rcv_dmac, p_rcv_dmac, IPWD_MAX_DEVICE_ADDRESS_LEN - 1); ipwd_message (IPWD_MSG_TYPE_DEBUG, "Received ARP packet: S:%s-%s D:%s-%s", rcv_sip, rcv_smac, rcv_dip, rcv_dmac); /* Update devices structure with actual IP and MAC addresses of interfaces */ for (i = 0; i < devices.devnum; i++) { if (ipwd_devinfo (devices.dev[i].device, devices.dev[i].ip, devices.dev[i].mac) == IPWD_RV_ERROR) { devices.dev[i].state = IPWD_DEVICE_STATE_UNUSABLE; continue; } else { devices.dev[i].state = IPWD_DEVICE_STATE_USABLE; } /* Ignore packets coming from local interfaces */ if (strcasecmp (rcv_smac, devices.dev[i].mac) == 0) { if (strcasecmp (rcv_sip, devices.dev[i].ip) == 0) { ipwd_message (IPWD_MSG_TYPE_DEBUG, "ARP packet ignored because it comes from local interface."); } else { /* Happens when there is more than one interface connected to the same subnet */ ipwd_message (IPWD_MSG_TYPE_DEBUG, "ARP packet ignored because it comes from local machine."); } return; } } /* Search through devices structure */ for (i = 0; i < devices.devnum; i++) { /* Skip devices that are currently unusable */ if (devices.dev[i].state == IPWD_DEVICE_STATE_UNUSABLE) continue; if (testing_flag == 0) { /* Check if received packet causes conflict with IP address of this interface */ if (!((strcasecmp (rcv_sip, devices.dev[i].ip) == 0) && (strcasecmp (rcv_smac, devices.dev[i].mac) != 0))) { ipwd_message (IPWD_MSG_TYPE_DEBUG, "Packet does not conflict with: %s %s-%s", devices.dev[i].device, devices.dev[i].ip, devices.dev[i].mac); continue; } } /* Get current system time */ if (gettimeofday (¤t_time, NULL) != 0) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to get current time"); break; } difference = ((current_time.tv_sec + (current_time.tv_usec / 1000000.0)) - (devices.dev[i].time.tv_sec + (devices.dev[i].time.tv_usec / 1000000.0))); /* Check if current time is within the defend interval */ if (difference < config.defend_interval) { ipwd_message (IPWD_MSG_TYPE_ALERT, "MAC address %s causes IP conflict with address %s set on interface %s - no action taken because this happened within the defend interval", rcv_smac, devices.dev[i].ip, devices.dev[i].device); break; } /* Store conflict time */ devices.dev[i].time.tv_sec = current_time.tv_sec; devices.dev[i].time.tv_usec = current_time.tv_usec; /* Handle IP conflict */ if (devices.dev[i].mode == IPWD_PROTECTION_MODE_ACTIVE) { ipwd_message (IPWD_MSG_TYPE_ALERT, "MAC address %s causes IP conflict with address %s set on interface %s - active mode - reply sent", rcv_smac, devices.dev[i].ip, devices.dev[i].device); /* Send reply to conflicting system */ ipwd_genarp (devices.dev[i].device, devices.dev[i].ip, devices.dev[i].mac, rcv_sip, rcv_smac, ARPOP_REPLY); /* Send GARP request to update cache of our neighbours */ ipwd_genarp (devices.dev[i].device, devices.dev[i].ip, devices.dev[i].mac, devices.dev[i].ip, "ff:ff:ff:ff:ff:ff", ARPOP_REQUEST); } else { ipwd_message (IPWD_MSG_TYPE_ALERT, "MAC address %s causes IP conflict with address %s set on interface %s - passive mode - reply not sent", rcv_smac, devices.dev[i].ip, devices.dev[i].device); } if (config.script != NULL) { /* Run user-defined script in form: script "dev" "ip" "mac" */ command_len = strlen (config.script) + 2 + strlen (devices.dev[i].device) + 3 + strlen (devices.dev[i].ip) + 3 + strlen (rcv_smac) + 2; if ((command = (char *) malloc (command_len * sizeof (char))) == NULL) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to execute user-defined script - malloc failed"); break; } snprintf (command, command_len, "%s \"%s\" \"%s\" \"%s\"", config.script, devices.dev[i].device, devices.dev[i].ip, rcv_smac); ipwd_message (IPWD_MSG_TYPE_DEBUG, "Running user-defined script: %s", command); rv = system (command); if (rv == -1) { ipwd_message (IPWD_MSG_TYPE_ERROR, "Unable to execute user-defined script: %s", command); } free (command); command = NULL; } else { ipwd_message (IPWD_MSG_TYPE_DEBUG, "No user-defined script specified"); } if (testing_flag == 1) { break; } } } ipwatchd-1.3.0/LICENSE0000664000175000017500000004310313326442515013331 0ustar jariqjariq GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.