aboutsummaryrefslogtreecommitdiffstats
path: root/caputils/iface_monitor.c
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2014-07-15 18:26:46 -0700
committerGuy Harris <guy@alum.mit.edu>2014-07-16 01:27:21 +0000
commitfae3615ce07bb5ced41486cf97844f271118e893 (patch)
tree0d60a9460cced5b4e80eb13757cb226a0d22ba8e /caputils/iface_monitor.c
parentbd30abf8701c940fc995317e6ac4aee3be04ad29 (diff)
Move the monitor-for-interface-list-changes stuff to the caputils library.
Change-Id: Ie0d4504688602c2aa8e9788643b079930ca7d305 Reviewed-on: https://code.wireshark.org/review/3076 Reviewed-by: Guy Harris <guy@alum.mit.edu>
Diffstat (limited to 'caputils/iface_monitor.c')
-rw-r--r--caputils/iface_monitor.c375
1 files changed, 375 insertions, 0 deletions
diff --git a/caputils/iface_monitor.c b/caputils/iface_monitor.c
new file mode 100644
index 0000000000..683a9cc25e
--- /dev/null
+++ b/caputils/iface_monitor.c
@@ -0,0 +1,375 @@
+/* iface_monitor.c
+ * interface monitor by Pontus Fuchs <pontus.fuchs@gmail.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * 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.
+ */
+#include "config.h"
+
+#ifdef HAVE_LIBPCAP
+
+#include <caputils/iface_monitor.h>
+
+#if defined(HAVE_LIBNL)
+
+/*
+ * Linux with libnl.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+#include <netlink/route/link.h>
+
+#ifndef IFF_UP
+/*
+ * Apparently, some versions of libnl drag in headers that define IFF_UP
+ * and others don't. Include <net/if.h> iff IFF_UP isn't already defined,
+ * so that if <linux/if.h> has been included by some or all of the
+ * netlink headers, we don't include <net/if.h> and get a bunch of
+ * complaints about various structures being redefined.
+ */
+#include <net/if.h>
+#endif
+
+/* libnl 1.x compatibility code */
+#ifdef HAVE_LIBNL1
+#define nl_sock nl_handle
+#define nl_socket_disable_seq_check nl_disable_sequence_check
+
+static inline struct nl_handle *nl_socket_alloc(void)
+{
+ return nl_handle_alloc();
+}
+
+static inline void nl_socket_free(struct nl_sock *h)
+{
+ nl_handle_destroy(h);
+}
+#endif /* HAVE_LIBNL1 */
+
+static struct nl_sock *iface_mon_sock;
+
+static void
+iface_mon_handler2(struct nl_object *obj, void *arg)
+{
+ struct rtnl_link *filter;
+ struct rtnl_link *link_obj;
+ int flags, up;
+ char *ifname;
+ iface_mon_cb cb = (iface_mon_cb)arg;
+
+ filter = rtnl_link_alloc();
+ if (!filter) {
+ fprintf(stderr, "error allocating filter\n");
+ return;
+ }
+
+ if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) {
+ rtnl_link_put(filter);
+ return;
+ }
+
+ link_obj = (struct rtnl_link *) obj;
+ flags = rtnl_link_get_flags (link_obj);
+ ifname = rtnl_link_get_name(link_obj);
+
+ /*
+ * You can't bind a PF_PACKET socket to an interface that's not
+ * up, so an interface going down is an "interface should be
+ * removed" indication.
+ *
+ * XXX - what indication, if any, do we get if the interface
+ * *completely goes away*?
+ *
+ * XXX - can we get events if an interface's link-layer or
+ * network addresses change?
+ */
+ up = (flags & IFF_UP) ? 1 : 0;
+
+ cb(ifname, up);
+
+ rtnl_link_put(filter);
+
+ return;
+}
+
+static int
+iface_mon_handler(struct nl_msg *msg, void *arg)
+{
+ nl_msg_parse (msg, &iface_mon_handler2, arg);
+ return 0;
+}
+
+void
+iface_mon_event(void)
+{
+ nl_recvmsgs_default(iface_mon_sock);
+}
+
+int
+iface_mon_get_sock(void)
+{
+ return nl_socket_get_fd(iface_mon_sock);
+}
+
+int
+iface_mon_start(iface_mon_cb cb)
+{
+ int err;
+
+ iface_mon_sock = nl_socket_alloc();
+ if (!iface_mon_sock) {
+ fprintf(stderr, "Failed to allocate netlink socket.\n");
+ return -ENOMEM;
+ }
+
+ nl_socket_disable_seq_check(iface_mon_sock);
+
+ nl_socket_modify_cb(iface_mon_sock, NL_CB_VALID, NL_CB_CUSTOM, iface_mon_handler, cb);
+
+ if (nl_connect(iface_mon_sock, NETLINK_ROUTE)) {
+ fprintf(stderr, "Failed to connect to generic netlink.\n");
+ err = -ENOLINK;
+ goto out_handle_destroy;
+ }
+
+ nl_socket_add_membership(iface_mon_sock, RTNLGRP_LINK);
+
+ return 0;
+
+out_handle_destroy:
+ nl_socket_free(iface_mon_sock);
+ return err;
+}
+
+void
+iface_mon_stop(void)
+{
+ if(iface_mon_sock)
+ nl_socket_free(iface_mon_sock);
+ iface_mon_sock = NULL;
+}
+
+#elif defined(__APPLE__)
+
+/*
+ * OS X.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <sys/kern_event.h>
+
+#include <glib.h>
+
+static int s;
+static iface_mon_cb callback;
+
+int
+iface_mon_start(iface_mon_cb cb)
+{
+ int ret;
+ struct kev_request key;
+
+ /* Create a socket of type PF_SYSTEM to listen for events. */
+ s = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
+ if (s == -1)
+ return -errno;
+
+ /*
+ * Ask for DLIL messages.
+ *
+ * XXX - also ask for KEV_INET_SUBCLASS and KEV_INET6_SUBCLASS,
+ * to detect new or changed network addresses, so those can be
+ * updated as well? Can we specify multiple filters on a socket,
+ * or must we specify KEV_ANY_SUBCLASS and filter the events after
+ * receiving them?
+ */
+ key.vendor_code = KEV_VENDOR_APPLE;
+ key.kev_class = KEV_NETWORK_CLASS;
+ key.kev_subclass = KEV_DL_SUBCLASS;
+ if (ioctl(s, SIOCSKEVFILT, &key) == -1) {
+ ret = -errno;
+ close(s);
+ return ret;
+ }
+
+ callback = cb;
+ return 0;
+}
+
+void
+iface_mon_stop(void)
+{
+ close(s);
+}
+
+int
+iface_mon_get_sock(void)
+{
+ return s;
+}
+
+/*
+ * Size of buffer for kernel network event.
+ */
+#define NET_EVENT_DATA_SIZE (KEV_MSG_HEADER_SIZE + sizeof (struct net_event_data))
+
+void
+iface_mon_event(void)
+{
+ char msg[NET_EVENT_DATA_SIZE];
+ ssize_t received;
+ struct kern_event_msg *kem;
+ struct net_event_data *evd;
+ size_t evd_len;
+ char ifr_name[IFNAMSIZ];
+
+ received = recv(s, msg, sizeof msg, 0);
+ if (received < 0) {
+ /* Error - ignore. */
+ return;
+ }
+ if ((size_t)received < sizeof msg) {
+ /* Short read - ignore. */
+ return;
+ }
+ kem = (struct kern_event_msg *)msg;
+ evd_len = kem->total_size - KEV_MSG_HEADER_SIZE;
+ if (evd_len != sizeof (struct net_event_data)) {
+ /* Length of the message is bogus. */
+ return;
+ }
+ evd = (struct net_event_data *)&kem->event_data[0];
+ g_snprintf(ifr_name, IFNAMSIZ, "%s%u", evd->if_name, evd->if_unit);
+
+ /*
+ * Check type of event.
+ *
+ * Note: if we also ask for KEV_INET_SUBCLASS, we will get
+ * events with keys
+ *
+ * KEV_INET_NEW_ADDR
+ * KEV_INET_CHANGED_ADDR
+ * KEV_INET_CHANGED_ADDR
+ * KEV_INET_SIFDSTADDR
+ * KEV_INET_SIFBRDADDR
+ * KEV_INET_SIFNETMASK
+ *
+ * reflecting network address changes, with the data being a
+ * struct kev_in_data rather than struct net_event_data, and
+ * if we also ask for KEV_INET6_SUBCLASS, we will get events
+ * with keys
+ *
+ * KEV_INET6_NEW_LL_ADDR
+ * KEV_INET6_NEW_USER_ADDR
+ * KEV_INET6_NEW_RTADV_ADDR
+ * KEV_INET6_ADDR_DELETED
+ *
+ * with the data being a struct kev_in6_data.
+ */
+ switch (kem->event_code) {
+
+ case KEV_DL_IF_ATTACHED:
+ /*
+ * A new interface has arrived.
+ *
+ * XXX - what we really want is "a new BPFable interface
+ * has arrived", but that's not available. While we're
+ * asking for additional help from BPF, it'd also be
+ * nice if we could ask it for a list of all interfaces
+ * that have had bpfattach()/bpf_attach() done on them,
+ * so we don't have to try to open the device in order
+ * to see whether we should show it as something on
+ * which we can capture.
+ */
+ callback(ifr_name, 1);
+ break;
+
+ case KEV_DL_IF_DETACHED:
+ /*
+ * An existing interface has been removed.
+ *
+ * XXX - use KEV_DL_IF_DETACHING instead, as that's
+ * called shortly after bpfdetach() is called, and
+ * bpfdetach() makes an interface no longer BPFable,
+ * and that's what we *really* care about.
+ */
+ callback(ifr_name, 0);
+ break;
+
+ default:
+ /*
+ * Is there any reason to care about:
+ *
+ * KEV_DL_LINK_ON
+ * KEV_DL_LINK_OFF
+ * KEV_DL_SIFFLAGS
+ * KEV_DL_LINK_ADDRESS_CHANGED
+ * KEV_DL_IFCAP_CHANGED
+ *
+ * or any of the other events? On Snow Leopard and, I think,
+ * earlier releases, you can't attach a BPF device to an
+ * interface that's not up, so KEV_DL_SIFFLAGS might be
+ * worth listening to so that we only say "here's a new
+ * interface" when it goes up; on Lion (and possibly Mountain
+ * Lion), an interface doesn't have to be up in order to
+ * have a BPF device attached to it.
+ */
+ break;
+ }
+}
+
+#else /* don't have something we support */
+
+int
+iface_mon_start(iface_mon_cb cb _U_)
+{
+ return -1;
+}
+
+void
+iface_mon_stop(void)
+{
+}
+
+int
+iface_mon_get_sock(void)
+{
+ return -1;
+}
+
+void
+iface_mon_event(void)
+{
+}
+
+#endif /* HAVE_LIBNL */
+
+#endif /* HAVE_LIBPCAP */