/* * netmon -- * Monitor a network interface. If the IP address disappears, run "ifconfig inet autoconf" */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_INTERVAL 10 #define MAX_INTERVAL 3600 bool foreground; bool use_syslog; #define PID_DIR "/var/run" #define PID_FILE "netmon.pid" const char *pid_file = PID_DIR"/"PID_FILE; void do_log (int priority, const char *msg, va_list ap) { if (use_syslog) { vsyslog (priority, msg, ap); } else { FILE *out = (priority == LOG_NOTICE || priority == LOG_INFO || priority == LOG_DEBUG) ? stdout : stderr; vfprintf (out, msg, ap); fputc ('\n', out); } } void debug (const char *msg, ...) { if (foreground) { va_list ap; va_start (ap, msg); do_log (LOG_DEBUG, msg, ap); va_end (ap); } } void info (const char *msg, ...) { va_list ap; va_start (ap, msg); do_log (LOG_INFO, msg, ap); va_end (ap); } void error (const char *msg, ...) { va_list ap; va_start (ap, msg); do_log (LOG_ERR, msg, ap); va_end (ap); } void die (const char *msg, ...) { va_list ap; va_start (ap, msg); do_log (LOG_ERR, msg, ap); va_end (ap); unlink (pid_file); exit (EXIT_FAILURE); } void sigexit (int sig) { unlink (pid_file); exit (EXIT_SUCCESS); } bool is_up (int af, char *interface) { struct ifaddrs *ifap, *ifa = NULL; bool found = false; if (getifaddrs (&ifa) == -1) die ("getifaddrs: %s", strerror (errno)); for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { struct sockaddr *sa = ifap->ifa_addr; if (sa->sa_family == af && ! strcmp (interface, ifap->ifa_name)) { if (foreground) { if (sa->sa_family == AF_INET) { char addr[INET_ADDRSTRLEN] = {0}; debug ("%s up: %s", ifap->ifa_name, inet_ntop (AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), addr, sizeof (addr))); } else if (sa->sa_family == AF_INET6) { char addr[INET6_ADDRSTRLEN] = {0}; debug ("%s up: %s", ifap->ifa_name, inet_ntop (AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr, sizeof (addr))); } } found = true; goto out; } } out: freeifaddrs (ifa); return found; } void configure (int af, char *interface) { pid_t p, wp; int status; unsigned int iters = 0; #define MAX_ITERS 30 info ("reconfiguring %s", interface); if ((p = fork ()) == -1) die ("fork failure: %s", strerror (errno)); if (p == 0) { execl ("/sbin/ifconfig", "ifconfig", interface, (af == AF_INET) ? "inet" : "inet6", "autoconf", NULL); die ("exec failure: %s", strerror (errno)); } while (1) { wp = waitpid (p, &status, WNOHANG); if (wp == -1) { if (errno == ECHILD) { break; } die ("waitpid failure: %s", strerror (errno)); } else if (wp == 0) { if (iters++ > MAX_ITERS) { error ("child overdue, killing and bailing"); kill (p, SIGTERM); sleep (1); kill (p, SIGKILL); unlink (pid_file); exit (EXIT_FAILURE); } } else { if (WIFEXITED (status)) { debug ("child exited with exit code %d", WEXITSTATUS (status)); } else if (WIFSIGNALED (status)) { debug ("child exited with signal %d", WTERMSIG (status)); } else { debug ("child exited with status %d", status); } } sleep (1); } } void help (int ec) { printf ("netmon [--interval|-i ] [-4|-6] \n" "\n" " Monitor a network interface and reconfigure it if it goes down\n" "\n" "Options:\n" " --debug|-d run in the foreground and send output to the console\n" " --interval|-i how frequently to check in seconds (default %u)\n" " --ipv4|-4 check for configured IPv4 address (default)\n" " --ipv6|-6 check for a configured IPv6 address instead of IPv4\n", DEFAULT_INTERVAL); exit (ec); } int main (int argc, char *argv[]) { struct option options[] = { { "interval", required_argument, NULL, 'i' }, { "debug", no_argument, NULL, 'd' }, { "ipv4", no_argument, NULL, '4' }, { "ipv6", no_argument, NULL, '6' }, { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; int opt; int af = AF_INET; unsigned long interval = DEFAULT_INTERVAL; char *interface = NULL; while ((opt = getopt_long (argc, argv, "i:46dh", options, NULL)) != -1) { switch (opt) { case 'i': errno = 0; interval = strtoul (optarg, NULL, 10); if (interval < 1 || interval > MAX_INTERVAL || errno) { die ("invalid interval '%s', must be in the range [1,%u]", optarg, MAX_INTERVAL); } break; case '4': af = AF_INET; break; case '6': af = AF_INET6; break; case 'd': foreground = true; break; case 'h': help (EXIT_SUCCESS); break; default: help (EXIT_FAILURE); } } if (optind >= argc) { help (EXIT_FAILURE); } interface = argv[optind]; if (! foreground) { if (access (PID_DIR, W_OK) == -1) die ("unable to write pid file '%s'", pid_file); use_syslog = true; if (daemon (0, 0) == -1) die ("daemon failed: %s", strerror (errno)); FILE *fp = fopen (pid_file, "w"); if (fp == NULL) die ("error writing pid file '%s': %s", pid_file, strerror (errno)); fprintf (fp, "%d\n", getpid ()); fclose (fp); if (pledge ("cpath exec inet proc stdio unveil", NULL) == -1) die ("pledge: %s", strerror (errno)); if (unveil (pid_file, "c") == -1) die ("unveil %s: %s", pid_file, strerror (errno)); if (unveil ("/sbin", "rx") == -1) die ("unveil /sbin: %s", strerror (errno)); if (unveil (NULL, NULL) == -1) die ("unveil: %s", strerror (errno)); } signal (SIGINT, sigexit); signal (SIGTERM, sigexit); signal (SIGQUIT, sigexit); while (1) { if (! is_up (af, interface)) configure (af, interface); sleep ((unsigned int)interval); } return 0; }