From fae3615ce07bb5ced41486cf97844f271118e893 Mon Sep 17 00:00:00 2001 From: Guy Harris Date: Tue, 15 Jul 2014 18:26:46 -0700 Subject: 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 --- caputils/CMakeLists.txt | 1 + caputils/Makefile.common | 2 + caputils/iface_monitor.c | 375 +++++++++++++++++++++++++++++++++++++++++++++++ caputils/iface_monitor.h | 75 ++++++++++ 4 files changed, 453 insertions(+) create mode 100644 caputils/iface_monitor.c create mode 100644 caputils/iface_monitor.h (limited to 'caputils') diff --git a/caputils/CMakeLists.txt b/caputils/CMakeLists.txt index 4c8dab086a..a12d09f15d 100644 --- a/caputils/CMakeLists.txt +++ b/caputils/CMakeLists.txt @@ -37,6 +37,7 @@ endif() set(CAPUTILS_SRC ${PLATFORM_CAPUTILS_SRC} capture-pcap-util.c + iface_monitor.c ws80211_utils.c ) diff --git a/caputils/Makefile.common b/caputils/Makefile.common index 57599380a7..f09079d9e7 100644 --- a/caputils/Makefile.common +++ b/caputils/Makefile.common @@ -24,6 +24,7 @@ CAPUTILS_SRC = \ $(PLATFORM_CAPUTILS_SRC) \ capture-pcap-util.c \ + iface_monitor.c \ ws80211_utils.c noinst_HEADERS = \ @@ -32,4 +33,5 @@ noinst_HEADERS = \ capture-pcap-util-int.h \ capture-wpcap.h \ capture_wpcap_packet.h \ + iface_monitor.h \ ws80211_utils.h 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 + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 + +#if defined(HAVE_LIBNL) + +/* + * Linux with libnl. + */ + +#include +#include +#include + +#include +#include +#include + +#ifndef IFF_UP +/* + * Apparently, some versions of libnl drag in headers that define IFF_UP + * and others don't. Include iff IFF_UP isn't already defined, + * so that if has been included by some or all of the + * netlink headers, we don't include and get a bunch of + * complaints about various structures being redefined. + */ +#include +#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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +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 */ diff --git a/caputils/iface_monitor.h b/caputils/iface_monitor.h new file mode 100644 index 0000000000..d6269cf627 --- /dev/null +++ b/caputils/iface_monitor.h @@ -0,0 +1,75 @@ +/* iface_monitor.h + * interface monitor by Pontus Fuchs + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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. + */ +#ifndef IFACE_MONITOR_H +#define IFACE_MONITOR_H + +#ifdef HAVE_LIBPCAP + +/* + * Callback for interface changes. + * + * iface is a pointer to the name of the interface. + * + * up is 1 if the interface is up, 0 if it's down. + * + * XXX - we really want "gone", not "down", where "gone" may include + * "down" if the OS requires an interface to be up in order to start + * a capture on it (as is the case in Linux and in OS X prior to + * Lion), but should also include *gone*, as in "there is no longer + * an interface with this name, so it's neither down nor up". + * + * We also may want other events, such as address changes, so what + * we might want is "add", "remove", and "modify" as the events. + */ +typedef void (*iface_mon_cb)(const char *iface, int up); + +/* + * Start watching for interface changes. + */ +int +iface_mon_start(iface_mon_cb cb); + +/* + * Stop watching for interface changes. + */ +void +iface_mon_stop(void); + +/* + * Get the socket on which interface changes are delivered, so that + * we can add it to the event loop. + * + * XXX - what if it's not a socket or other file descriptor? + */ +int +iface_mon_get_sock(void); + +/* + * Call this if something is readable from the interface change socket. + * It will call the callback as appropriate. + */ +void +iface_mon_event(void); + +#endif /* HAVE_LIBPCAP */ + +#endif /* IFACE_MONITOR_H */ -- cgit v1.2.1