Replace the capture start and restart icons with a green fin similar to
[metze/wireshark/wip.git] / iface_monitor.c
index b90f760710517a645115666ead713025567ae57a..281411a75fda741e91be45b61ee5408f0c94f714 100644 (file)
@@ -19,7 +19,7 @@
  *
  * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 #include "config.h"
 
 
 #include "iface_monitor.h"
 
-#ifdef HAVE_LIBNL
+#if defined(HAVE_LIBNL)
+
+/*
+ * Linux with libnl.
+ */
 
 #include <stdio.h>
 #include <strings.h>
 #include <errno.h>
 
-#include <net/if.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
@@ -64,7 +77,7 @@ iface_mon_handler2(struct nl_object *obj, void *arg)
     struct rtnl_link *link_obj;
     int flags, up;
     char *ifname;
-    iface_mon_cb cb = arg;
+    iface_mon_cb cb = (iface_mon_cb)arg;
 
     filter = rtnl_link_alloc();
     if (!filter) {
@@ -81,6 +94,17 @@ iface_mon_handler2(struct nl_object *obj, void *arg)
     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);
@@ -97,8 +121,20 @@ iface_mon_handler(struct nl_msg *msg, void *arg)
     return 0;
 }
 
-static int
-iface_mon_nl_init(void *arg)
+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;
 
@@ -110,7 +146,7 @@ iface_mon_nl_init(void *arg)
 
     nl_socket_disable_seq_check(iface_mon_sock);
 
-    nl_socket_modify_cb(iface_mon_sock, NL_CB_VALID, NL_CB_CUSTOM, iface_mon_handler, arg);
+    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");
@@ -128,32 +164,191 @@ out_handle_destroy:
 }
 
 void
-iface_mon_event(void)
+iface_mon_stop(void)
 {
-    nl_recvmsgs_default(iface_mon_sock);
+    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_get_sock(void)
+iface_mon_start(iface_mon_cb cb)
 {
-    return nl_socket_get_fd(iface_mon_sock);
+    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_start(iface_mon_cb cb)
+iface_mon_get_sock(void)
 {
-    return iface_mon_nl_init(cb);
+    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_stop(void)
+iface_mon_event(void)
 {
-    if(iface_mon_sock)
-        nl_socket_free(iface_mon_sock);
-    iface_mon_sock = NULL;
+    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 /* HAVE_LIBNL */
+#else /* don't have something we support */
 
 int
 iface_mon_start(iface_mon_cb cb _U_)