diff options
| author | Jon duSaint | 2022-11-23 18:03:50 -0800 |
|---|---|---|
| committer | Jon duSaint | 2022-11-23 18:03:50 -0800 |
| commit | b7890d06f467a0b5e0ca7f7753120aec1cc23260 (patch) | |
| tree | 8cc8674f84b34f392656314c98ae1809de84438d | |
| parent | 4445becdc84e05859d90d13d0d98625a0b72f9e9 (diff) | |
netmon: Network interface monitor
Monitor a network interface. If the IP address disappears, reconfigure the interface.
| -rw-r--r-- | jobmon/Makefile | 11 | ||||
| -rw-r--r-- | jobmon/netmon.c | 256 | ||||
| -rw-r--r-- | jobmon/netmon.rc | 9 |
3 files changed, 276 insertions, 0 deletions
diff --git a/jobmon/Makefile b/jobmon/Makefile new file mode 100644 index 0000000..9fa9050 --- /dev/null +++ b/jobmon/Makefile @@ -0,0 +1,11 @@ +# Use EXTRA_FLAGS on the command line for additional options (-g, -static, etc.) +all: netmon +netmon: netmon.c + $(CC) -Wall $(CFLAGS) $(EXTRA_FLAGS) -o $@ $< +install-netmon: netmon + install -m 0555 netmon /usr/sbin/netmon + install -m 0555 netmon.rc /etc/rc.d/netmon + @echo "-> Enable netmon service manually with rcctl(8)" +install: install-netmon +clean: + rm -f netmon diff --git a/jobmon/netmon.c b/jobmon/netmon.c new file mode 100644 index 0000000..6c2b13b --- /dev/null +++ b/jobmon/netmon.c @@ -0,0 +1,256 @@ +/* + * netmon -- + * Monitor a network interface. If the IP address disappears, run "ifconfig <interface> inet autoconf" + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <getopt.h> +#include <ifaddrs.h> +#include <netinet/in.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <syslog.h> +#include <unistd.h> + +#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", (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 <seconds>] [-4|-6] <interface>\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; +} diff --git a/jobmon/netmon.rc b/jobmon/netmon.rc new file mode 100644 index 0000000..77a2ee7 --- /dev/null +++ b/jobmon/netmon.rc @@ -0,0 +1,9 @@ +#!/bin/ksh + +daemon="/usr/sbin/netmon" + +rc_reload=NO + +. /etc/rc.d/rc.subr + +rc_cmd $1 |
