summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon duSaint2022-11-23 18:03:50 -0800
committerJon duSaint2022-11-23 18:03:50 -0800
commitb7890d06f467a0b5e0ca7f7753120aec1cc23260 (patch)
tree8cc8674f84b34f392656314c98ae1809de84438d
parent4445becdc84e05859d90d13d0d98625a0b72f9e9 (diff)

netmon: Network interface monitor

Monitor a network interface. If the IP address disappears, reconfigure the interface.

-rw-r--r--jobmon/Makefile11
-rw-r--r--jobmon/netmon.c256
-rw-r--r--jobmon/netmon.rc9
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