summaryrefslogtreecommitdiffstats
path: root/capture/iface_monitor.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--capture/iface_monitor.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/capture/iface_monitor.c b/capture/iface_monitor.c
index f742a664..870761ca 100644
--- a/capture/iface_monitor.c
+++ b/capture/iface_monitor.c
@@ -17,6 +17,8 @@
/*
* Linux with libnl.
+ *
+ * Use Netlink to get indications of new/removed intrfaces.
*/
#include <stdio.h>
@@ -181,6 +183,8 @@ iface_mon_stop(void)
/*
* macOS.
+ *
+ * Use a PF_SYSTEM socket to get indications of new/removed intrfaces.
*/
#include <stddef.h>
@@ -354,6 +358,176 @@ iface_mon_event(void)
}
}
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+
+/*
+ * FreeBSD, NetBSD, OpenBSD, DragonFly BSD.
+ *
+ * Use a PF_ROUTE socket to get indications of new/removed intrfaces.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+
+static int s;
+static iface_mon_cb callback;
+
+int
+iface_mon_start(iface_mon_cb cb)
+{
+#ifdef RO_MSGFILTER
+ unsigned char msgfilter[] = {
+ RTM_IFANNOUNCE,
+ };
+#endif
+
+ /* Create a socket of type PF_ROUTE to listen for events. */
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s == -1)
+ return -errno;
+
+#ifdef RO_MSGFILTER
+ /*
+ * This OS supports filtering PF_ROUTE sockets for specific
+ * events; we only want interface announcement events.
+ *
+ * If this fails, we just live with extra events that we ignore,
+ * which we also do on platforms that don't support filtering.
+ */
+ (void) setsockopt(s, PF_ROUTE, RO_MSGFILTER, &msgfilter, sizeof msgfilter);
+#endif
+#ifdef SO_RERROR
+ /*
+ * This OS supports getting error reports from recvmsg() if a
+ * receive buffer overflow occurs. If that happens, it means
+ * that we may have lost interface reports, so we should
+ * probably just refetch all interface data.
+ *
+ * If we can't get those error reports, we're out of luck.
+ */
+ int n = 1;
+ (void) setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n));
+#endif
+
+ callback = cb;
+ return 0;
+}
+
+void
+iface_mon_stop(void)
+{
+ close(s);
+}
+
+int
+iface_mon_get_sock(void)
+{
+ return s;
+}
+
+void
+iface_mon_event(void)
+{
+ union msgbuf {
+ char buf[2048];
+ struct rt_msghdr hd;
+ struct if_announcemsghdr ifan;
+ } msgbuf;
+ struct iovec iov[1];
+ struct msghdr msg;
+ bool message_seen = false;
+ ssize_t received;
+
+ iov[0].iov_base = &msgbuf;
+ iov[0].iov_len = sizeof msgbuf;
+ memset(&msg, 0, sizeof msg);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ while (!message_seen) {
+ received = recvmsg(s, &msg, 0);
+ if (received == -1) {
+ if (errno == ENOBUFS) {
+ /*
+ * Receive buffer overflow. Keep reading,
+ * to get any messages in the socket buffer.
+ *
+ * XXX - that means we may have lost indications;
+ * should there be a callback that indicates that
+ * all interface data should be refreshed?
+ */
+ continue;
+ } else {
+ /*
+ * Other error - just ignore.
+ */
+ return;
+ }
+ }
+
+ /*
+ * We've seen a message.
+ */
+ message_seen = true;
+ }
+ if (received != 0) {
+ /*
+ * XXX - should we check received, to make sure it's large
+ * enough?
+ */
+ if (msgbuf.hd.rtm_version != RTM_VERSION)
+ return;
+
+ switch (msgbuf.hd.rtm_type) {
+
+ case RTM_IFANNOUNCE:
+ switch (msgbuf.ifan.ifan_what) {
+
+ case IFAN_ARRIVAL:
+ /*
+ * A new interface has arrived.
+ *
+ * XXX - see comment about interface arrivals in the
+ * macOS section; it applies here as well.
+ */
+ callback(msgbuf.ifan.ifan_name, 1, 1);
+ break;
+
+ case IFAN_DEPARTURE:
+ /*
+ * An existing interface has been removed.
+ */
+ callback(msgbuf.ifan.ifan_name, 0, 0);
+ break;
+
+ default:
+ /*
+ * Ignore other notifications.
+ */
+ break;
+ }
+ break;
+
+ default:
+ /*
+ * Ignore other messages.
+ */
+ break;
+ }
+ }
+}
+
#else /* don't have something we support */
int