diff options
Diffstat (limited to 'caputils')
-rw-r--r-- | caputils/CMakeLists.txt | 61 | ||||
-rw-r--r-- | caputils/Makefile.am | 89 | ||||
-rw-r--r-- | caputils/Makefile.common | 35 | ||||
-rw-r--r-- | caputils/Makefile.nmake | 82 | ||||
-rw-r--r-- | caputils/capture-pcap-util-int.h | 44 | ||||
-rw-r--r-- | caputils/capture-pcap-util-unix.c | 434 | ||||
-rw-r--r-- | caputils/capture-pcap-util.c | 639 | ||||
-rw-r--r-- | caputils/capture-pcap-util.h | 74 | ||||
-rw-r--r-- | caputils/capture-wpcap.c | 990 | ||||
-rw-r--r-- | caputils/capture-wpcap.h | 42 | ||||
-rw-r--r-- | caputils/capture_ifinfo.h | 121 | ||||
-rw-r--r-- | caputils/capture_win_ifnames.c | 369 | ||||
-rw-r--r-- | caputils/capture_win_ifnames.h | 46 | ||||
-rw-r--r-- | caputils/capture_wpcap_packet.c | 331 | ||||
-rw-r--r-- | caputils/capture_wpcap_packet.h | 51 | ||||
-rw-r--r-- | caputils/doxygen.cfg.in | 81 | ||||
-rw-r--r-- | caputils/ws80211_utils.c | 748 | ||||
-rw-r--r-- | caputils/ws80211_utils.h | 61 |
18 files changed, 4298 insertions, 0 deletions
diff --git a/caputils/CMakeLists.txt b/caputils/CMakeLists.txt new file mode 100644 index 0000000000..4c8dab086a --- /dev/null +++ b/caputils/CMakeLists.txt @@ -0,0 +1,61 @@ +# CMakeLists.txt +# +# 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. +# + + +if(UNIX) + set(PLATFORM_CAPUTILS_SRC + capture-pcap-util-unix.c + ) +endif() + +if(WIN32) + set(PLATFORM_CAPUTILS_SRC + capture_win_ifnames.c + capture-wpcap.c + capture_wpcap_packet.c + ) +endif() + +set(CAPUTILS_SRC + ${PLATFORM_CAPUTILS_SRC} + capture-pcap-util.c + ws80211_utils.c +) + +set(CLEAN_FILES + ${CAPUTILS_SRC} +) + +if (WERROR) + set_source_files_properties( + ${CLEAN_FILES} + PROPERTIES + COMPILE_FLAGS -Werror + ) +endif() + + +add_library(caputils STATIC + ${CAPUTILS_SRC} +) + +set_target_properties(caputils PROPERTIES LINK_FLAGS "${WS_LINK_FLAGS}") +set_target_properties(caputils PROPERTIES FOLDER "CAPUTILS") diff --git a/caputils/Makefile.am b/caputils/Makefile.am new file mode 100644 index 0000000000..ad0326f5a5 --- /dev/null +++ b/caputils/Makefile.am @@ -0,0 +1,89 @@ +# Makefile.am +# Automake file for the "capture utilities" routines for Wireshark +# +# 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 Makefile.common +include ../Makefile.am.inc + +if HAVE_WARNINGS_AS_ERRORS +AM_CLEAN_CFLAGS = -Werror +endif + +noinst_LIBRARIES = libcaputils.a + +PLATFORM_CAPUTILS_SRC = \ + capture-pcap-util-unix.c + +CLEANFILES = \ + doxygen-caputils.tag \ + libcaputils.a \ + *~ + +MAINTAINERCLEANFILES = \ + $(GENERATED_FILES) \ + Makefile.in + +# All sources that should be put in the source distribution tarball +libcaputils_a_SOURCES = \ + $(CAPUTILS_SRC) \ + $(noinst_HEADERS) + +libcaputils_a_CFLAGS = $(AM_CLEAN_CFLAGS) + +libcaputils_a_DEPENDENCIES = + +# Common headers +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/wiretap $(LIBGCRYPT_CFLAGS) $(LIBGNUTLS_CFLAGS) $(PORTAUDIO_INCLUDES) + + +doxygen: +if HAVE_DOXYGEN + $(DOXYGEN) doxygen.cfg +endif # HAVE_DOXYGEN + +wsar_html: doxygen.cfg ../doxygen_global.cfg +if HAVE_DOXYGEN + (umask 022 ; $(DOXYGEN) doxygen.cfg) +endif + +checkapi: checkapi-base checkapi-todo + +checkapi-base: + $(PERL) $(top_srcdir)/tools/checkAPIs.pl -g deprecated-gtk -build \ + -sourcedir=$(srcdir) \ + $(CAPUTILS_SRC) + +checkapi-todo: + $(PERL) $(top_srcdir)/tools/checkAPIs.pl -M -g deprecated-gtk-todo -build \ + -sourcedir=$(srcdir) \ + $(CAPUTILS_SRC) + +EXTRA_DIST = \ + $(GENERATOR_FILES) \ + capture_win_ifnames.c \ + capture_win_ifnames.h \ + capture-wpcap.c \ + capture-wpcap.h \ + capture_wpcap_packet.c \ + capture_wpcap_packet.h \ + CMakeLists.txt \ + doxygen.cfg.in \ + Makefile.common \ + Makefile.nmake diff --git a/caputils/Makefile.common b/caputils/Makefile.common new file mode 100644 index 0000000000..57599380a7 --- /dev/null +++ b/caputils/Makefile.common @@ -0,0 +1,35 @@ +# Makefile.common +# Contains the stuff from Makefile.am and Makefile.nmake that is +# a) common to both files and +# b) portable between both files +# +# 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. + +CAPUTILS_SRC = \ + $(PLATFORM_CAPUTILS_SRC) \ + capture-pcap-util.c \ + ws80211_utils.c + +noinst_HEADERS = \ + capture_ifinfo.h \ + capture-pcap-util.h \ + capture-pcap-util-int.h \ + capture-wpcap.h \ + capture_wpcap_packet.h \ + ws80211_utils.h diff --git a/caputils/Makefile.nmake b/caputils/Makefile.nmake new file mode 100644 index 0000000000..55dd1a6fb1 --- /dev/null +++ b/caputils/Makefile.nmake @@ -0,0 +1,82 @@ +## Makefile for building wireshark.exe with Microsoft C and nmake +## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake +# + +include ..\config.nmake +include ..\Makefile.nmake.inc + +############### no need to modify below this line ######### + +# We use GENERATED_CFLAGS to get around flex's non-LLP64-compliant output +GENERATED_CFLAGS=\ + $(STANDARD_CFLAGS) \ + /Zm800 \ + /I.. /I../wiretap $(GLIB_CFLAGS) $(GNUTLS_CFLAGS) \ + /I$(PCAP_DIR)\WPCAP\LIBPCAP /I$(PCAP_DIR)\WPCAP\LIBPCAP\bpf \ + /I$(PCAP_DIR)\WPCAP\LIBPCAP\lbl \ + /I$(PCAP_DIR)\include $(AIRPCAP_CFLAGS) \ + $(PORTAUDIO_CFLAGS) $(GEOIP_CFLAGS) $(WINSPARKLE_CFLAGS) \ + $(HHC_CFLAGS) + +CFLAGS=$(WARNINGS_ARE_ERRORS) $(GENERATED_CFLAGS) + +.c.obj:: + $(CC) $(CFLAGS) $(WSUG_CFLAGS) -Fd.\ -c $< + +PLATFORM_CAPUTILS_SRC = \ + capture_win_ifnames.c \ + capture-wpcap.c \ + capture_wpcap_packet.c + +include Makefile.common + + +# if you add files here, be sure to include them also in Makefile.am EXTRA_DIST +CAPUTILS_OBJECTS = \ + $(CAPUTILS_SRC:.c=.obj) + +RUNLEX=..\tools\runlex.sh + +libcaputils.lib : ..\config.h $(CAPUTILS_OBJECTS) + link /lib /out:libcaputils.lib $(CAPUTILS_OBJECTS) + +clean: + rm -f $(CAPUTILS_OBJECTS) $(WIRESHARK_TAP_OBJECTS) libcaputils.lib *.pdb *.sbr \ + doxygen.cfg html/*.* wireshark-tap-register-cache.pkl + if exist html rmdir html + +distclean: clean + +maintainer-clean: distclean + rm -f $(GENERATED_FILES) + +# convert doxygen.cfg.in to doxygen.cfg with stamped version info +doxygen.cfg: ..\config.nmake doxygen.cfg.in +!IFDEF DOXYGEN + sed -e s/@VERSION@/$(VERSION)/ \ + < doxygen.cfg.in > $@ +!ENDIF + +doxygen-run: +!IFDEF DOXYGEN + $(DOXYGEN) doxygen.cfg +!ENDIF + +# MS html help compiler hhc returns 1 on success, but as nmake expects 0 it would stop here. +# the prepended -1 will raise the accepted error levels of nmake, so it will continue +doxygen.chm: +!IFDEF HHC + -1 $(HHC) html\index.hhp +!ENDIF + +doxygen: doxygen.cfg doxygen-run doxygen.chm + +checkapi: checkapi-base checkapi-todo + +checkapi-base: + $(PERL) ../tools/checkAPIs.pl -g deprecated-gtk -build \ + $(CAPUTILS_SRC) + +checkapi-todo: + $(PERL) ../tools/checkAPIs.pl -M -g deprecated-gtk-todo -build \ + $(CAPUTILS_SRC) diff --git a/caputils/capture-pcap-util-int.h b/caputils/capture-pcap-util-int.h new file mode 100644 index 0000000000..d49a0cef04 --- /dev/null +++ b/caputils/capture-pcap-util-int.h @@ -0,0 +1,44 @@ +/* capture-pcap-util-int.h + * Definitions of routines internal to the libpcap/WinPcap utilities + * + * 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. + */ + +#ifndef __PCAP_UTIL_INT_H__ +#define __PCAP_UTIL_INT_H__ + +extern if_info_t *if_info_new(const char *name, const char *description, + gboolean loopback); +extern void if_info_add_address(if_info_t *if_info, struct sockaddr *addr); +#ifdef HAVE_PCAP_FINDALLDEVS +#ifdef HAVE_PCAP_REMOTE +extern GList *get_interface_list_findalldevs_ex(const char *source, + struct pcap_rmtauth *auth, int *err, char **err_str); +#endif /* HAVE_PCAP_REMOTE */ +extern GList *get_interface_list_findalldevs(int *err, char **err_str); +#endif /* HAVE_PCAP_FINDALLDEVS */ + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_interface_list()". This is used to let the error message string + * be platform-dependent. + */ +extern gchar *cant_get_if_list_error_message(const char *err_str); + +#endif /* __PCAP_UTIL_INT_H__ */ diff --git a/caputils/capture-pcap-util-unix.c b/caputils/capture-pcap-util-unix.c new file mode 100644 index 0000000000..21e832b166 --- /dev/null +++ b/caputils/capture-pcap-util-unix.c @@ -0,0 +1,434 @@ +/* capture-pcap-util-unix.c + * UN*X-specific utility routines for packet capture + * + * 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" + +#include <glib.h> + +#ifdef HAVE_LIBPCAP + +#ifdef HAVE_PCAP_FINDALLDEVS + +#include <pcap.h> + +#else /* HAVE_PCAP_FINDALLDEVS */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +/* + * Keep Digital UNIX happy when including <net/if.h>. + */ +struct mbuf; +struct rtentry; +#include <net/if.h> + +#ifdef HAVE_SYS_SOCKIO_H +# include <sys/sockio.h> +#endif + +#include "caputils/capture-pcap-util.h" + +#endif /* HAVE_PCAP_FINDALLDEVS */ + +#ifdef HAVE_LIBCAP +# include <sys/capability.h> +#endif + +#include "caputils/capture_ifinfo.h" +#include "caputils/capture-pcap-util.h" +#include "caputils/capture-pcap-util-int.h" + +#ifdef HAVE_PCAP_REMOTE +GList * +get_remote_interface_list(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str) +{ + struct pcap_rmtauth auth; + char source[PCAP_BUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE]; + GList *result; + + if (pcap_createsrcstr(source, PCAP_SRC_IFREMOTE, hostname, port, + NULL, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + return NULL; + } + + auth.type = auth_type; + auth.username = g_strdup(username); + auth.password = g_strdup(passwd); + + result = get_interface_list_findalldevs_ex(source, &auth, err, err_str); + g_free(auth.username); + g_free(auth.password); + + return result; +} +#endif + +#ifdef HAVE_PCAP_FINDALLDEVS +GList * +get_interface_list(int *err, char **err_str) +{ + return get_interface_list_findalldevs(err, err_str); +} +#else /* HAVE_PCAP_FINDALLDEVS */ +struct search_user_data { + char *name; + if_info_t *if_info; +}; + +static void +search_for_if_cb(gpointer data, gpointer user_data) +{ + struct search_user_data *search_user_data = user_data; + if_info_t *if_info = data; + + if (strcmp(if_info->name, search_user_data->name) == 0) + search_user_data->if_info = if_info; +} + +GList * +get_interface_list(int *err, char **err_str) +{ + GList *il = NULL; + gint nonloopback_pos = 0; + struct ifreq *ifr, *last; + struct ifconf ifc; + struct ifreq ifrflags; + int sock = socket(AF_INET, SOCK_DGRAM, 0); + struct search_user_data user_data; + pcap_t *pch; + int len, lastlen; + char *buf; + if_info_t *if_info; + char errbuf[PCAP_ERRBUF_SIZE]; + gboolean loopback; + + if (sock < 0) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) { + *err_str = g_strdup_printf( + "Can't get list of interfaces: error opening socket: %s", + g_strerror(errno)); + } + return NULL; + } + + /* + * This code came from: W. Richard Stevens: "UNIX Network Programming", + * Networking APIs: Sockets and XTI, Vol 1, page 434. + */ + lastlen = 0; + len = 100 * sizeof(struct ifreq); + for ( ; ; ) { + buf = g_malloc(len); + ifc.ifc_len = len; + ifc.ifc_buf = buf; + memset (buf, 0, len); + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + if (errno != EINVAL || lastlen != 0) { + if (err_str != NULL) { + *err_str = g_strdup_printf( + "Can't get list of interfaces: SIOCGIFCONF ioctl error: %s", + g_strerror(errno)); + } + goto fail; + } + } else { + if ((unsigned int) ifc.ifc_len < sizeof(struct ifreq)) { + if (err_str != NULL) { + *err_str = g_strdup( + "Can't get list of interfaces: SIOCGIFCONF ioctl gave too small return buffer"); + } + goto fail; + } + if (ifc.ifc_len == lastlen) + break; /* success, len has not changed */ + lastlen = ifc.ifc_len; + } + len += 10 * sizeof(struct ifreq); /* increment */ + g_free(buf); + } + ifr = (struct ifreq *) ifc.ifc_req; + last = (struct ifreq *) ((char *) ifr + ifc.ifc_len); + while (ifr < last) { + /* + * Skip entries that begin with "dummy", or that include + * a ":" (the latter are Solaris virtuals). + */ + if (strncmp(ifr->ifr_name, "dummy", 5) == 0 || + strchr(ifr->ifr_name, ':') != NULL) + goto next; + + /* + * If we already have this interface name on the list, + * don't add it, but, if we don't already have an IP + * address for it, add that address (SIOCGIFCONF returns, + * at least on BSD-flavored systems, one entry per + * interface *address*; if an interface has multiple + * addresses, we get multiple entries for it). + */ + user_data.name = ifr->ifr_name; + user_data.if_info = NULL; + g_list_foreach(il, search_for_if_cb, &user_data); + if (user_data.if_info != NULL) { + if_info_add_address(user_data.if_info, &ifr->ifr_addr); + goto next; + } + + /* + * Get the interface flags. + */ + memset(&ifrflags, 0, sizeof ifrflags); + g_strlcpy(ifrflags.ifr_name, ifr->ifr_name, + sizeof ifrflags.ifr_name); + if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifrflags) < 0) { + if (errno == ENXIO) + goto next; + if (err_str != NULL) { + *err_str = g_strdup_printf( + "Can't get list of interfaces: SIOCGIFFLAGS error getting flags for interface %s: %s", + ifr->ifr_name, g_strerror(errno)); + } + goto fail; + } + + /* + * Skip interfaces that aren't up. + */ + if (!(ifrflags.ifr_flags & IFF_UP)) + goto next; + + /* + * Skip interfaces that we can't open with "libpcap". + * Open with the minimum packet size - it appears that the + * IRIX SIOCSNOOPLEN "ioctl" may fail if the capture length + * supplied is too large, rather than just truncating it. + */ + pch = pcap_open_live(ifr->ifr_name, MIN_PACKET_SIZE, 0, 0, + errbuf); + if (pch == NULL) + goto next; + pcap_close(pch); + + /* + * If it's a loopback interface, add it at the end of the + * list, otherwise add it after the last non-loopback + * interface, so all loopback interfaces go at the end - we + * don't want a loopback interface to be the default capture + * device unless there are no non-loopback devices. + */ + loopback = ((ifrflags.ifr_flags & IFF_LOOPBACK) || + strncmp(ifr->ifr_name, "lo", 2) == 0); + if_info = if_info_new(ifr->ifr_name, NULL, loopback); + if_info_add_address(if_info, &ifr->ifr_addr); + if (loopback) + il = g_list_append(il, if_info); + else { + il = g_list_insert(il, if_info, nonloopback_pos); + /* + * Insert the next non-loopback interface after this + * one. + */ + nonloopback_pos++; + } + + next: +#ifdef HAVE_SA_LEN + ifr = (struct ifreq *) ((char *) ifr + + (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr) ? + ifr->ifr_addr.sa_len : sizeof(ifr->ifr_addr)) + + IFNAMSIZ); +#else + ifr = (struct ifreq *) ((char *) ifr + sizeof(struct ifreq)); +#endif + } + +#ifdef linux + /* + * OK, maybe we have support for the "any" device, to do a cooked + * capture on all interfaces at once. + * Try opening it and, if that succeeds, add it to the end of + * the list of interfaces. + */ + pch = pcap_open_live("any", MIN_PACKET_SIZE, 0, 0, errbuf); + if (pch != NULL) { + /* + * It worked; we can use the "any" device. + */ + if_info = if_info_new("any", + "Pseudo-device that captures on all interfaces", FALSE); + il = g_list_insert(il, if_info, -1); + pcap_close(pch); + } +#endif + + g_free(ifc.ifc_buf); + close(sock); + + if (il == NULL) { + /* + * No interfaces found. + */ + *err = NO_INTERFACES_FOUND; + if (err_str != NULL) + *err_str = NULL; + } + return il; + +fail: + if (il != NULL) + free_interface_list(il); + g_free(ifc.ifc_buf); + close(sock); + *err = CANT_GET_INTERFACE_LIST; + return NULL; +} +#endif /* HAVE_PCAP_FINDALLDEVS */ + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_interface_list()". + */ +gchar * +cant_get_if_list_error_message(const char *err_str) +{ + return g_strdup_printf("Can't get list of interfaces: %s", err_str); +} + +/* + * Get the versions of libpcap, libpcap, and libnl with which we were + * compiled, and append them to a GString. + */ +void +get_compiled_caplibs_version(GString *str) +{ + /* + * NOTE: in *some* flavors of UN*X, the data from a shared + * library might be linked into executable images that are + * linked with that shared library, in which case you could + * look at pcap_version[] to get the version with which + * the program was compiled. + * + * In other flavors of UN*X, that doesn't happen, so + * pcap_version[] gives you the version the program is + * running with, not the version it was built with, and, + * in at least some of them, if the length of a data item + * referred to by the executable - such as the pcap_version[] + * string - isn't the same in the version of the library + * with which the program was built and the version with + * which it was run, the run-time linker will complain, + * which is Not Good. + * + * So, for now, we just give up on reporting the version + * of libpcap with which we were compiled. + */ + g_string_append(str, "with libpcap"); + + /* + * XXX - these libraries are actually used only by dumpcap, + * but we mention them here so that a user reporting a bug + * can get information about dumpcap's libraries without + * having to run dumpcap. + */ + /* LIBCAP */ + g_string_append(str, ", "); +#ifdef HAVE_LIBCAP + g_string_append(str, "with POSIX capabilities"); +#ifdef _LINUX_CAPABILITY_VERSION + g_string_append(str, " (Linux)"); +#endif /* _LINUX_CAPABILITY_VERSION */ +#else /* HAVE_LIBCAP */ + g_string_append(str, "without POSIX capabilities"); +#endif /* HAVE_LIBCAP */ + +#ifdef __linux__ + /* This is a Linux-specific library. */ + /* LIBNL */ + g_string_append(str, ", "); +#if defined(HAVE_LIBNL1) + g_string_append(str, "with libnl 1"); +#elif defined(HAVE_LIBNL2) + g_string_append(str, "with libnl 2"); +#elif defined(HAVE_LIBNL3) + g_string_append(str, "with libnl 3"); +#else /* no libnl */ + g_string_append(str, "without libnl"); +#endif /* libnl version */ +#endif /* __linux__ */ +} + +/* + * Append the version of libpcap with which we we're running to a GString. + */ +void +get_runtime_caplibs_version(GString *str) +{ + g_string_append_printf(str, "with "); +#ifdef HAVE_PCAP_LIB_VERSION + g_string_append(str, pcap_lib_version()); +#else + g_string_append(str, "libpcap (version unknown)"); +#endif +} + +#else /* HAVE_LIBPCAP */ + +/* + * Append an indication that we were not compiled with libpcap + * to a GString. Don't even bother mentioning the other + * libraries. + */ +void +get_compiled_caplibs_version(GString *str) +{ + g_string_append(str, "without libpcap"); +} + +/* + * Don't append anything, as we weren't even compiled to use libpcap. + */ +void +get_runtime_caplibs_version(GString *str _U_) +{ +} + +#endif /* HAVE_LIBPCAP */ diff --git a/caputils/capture-pcap-util.c b/caputils/capture-pcap-util.c new file mode 100644 index 0000000000..c4e7df3868 --- /dev/null +++ b/caputils/capture-pcap-util.c @@ -0,0 +1,639 @@ +/* capture-pcap-util.c + * Utility routines for packet capture + * + * 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 <glib.h> + +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include <wtap.h> +#include <libpcap.h> + +#include "caputils/capture_ifinfo.h" +#include "caputils/capture-pcap-util.h" +#include "caputils/capture-pcap-util-int.h" + +#include "wsutil/file_util.h" + +#ifndef _WIN32 +#include <netinet/in.h> +#endif + +#ifdef _WIN32 +#include "caputils/capture_win_ifnames.h" /* windows friendly interface names */ +#endif + +/* + * Given an interface name, find the "friendly name" and interface + * type for the interface. + */ + +#if defined(__APPLE__) + +#include <CoreFoundation/CoreFoundation.h> +#include <SystemConfiguration/SystemConfiguration.h> + +#include <wsutil/cfutils.h> + +/* + * On OS X, we get the "friendly name" and interface type for the interface + * from the System Configuration framework. + * + * To find the System Configuration framework information for the + * interface, we get all the interfaces that the System Configuration + * framework knows about and look for the one with a "BSD name" matching + * the interface name. + * + * If we find it, we use its "localized display name", if it has one, as + * the "friendly name". + * + * As for the interface type: + * + * Yes, fetching all the network addresses for an interface gets you an + * AF_LINK address, of type "struct sockaddr_dl", and, yes, that includes + * an SNMP MIB-II ifType value. + * + * However, it's IFT_ETHER, i.e. Ethernet, for AirPort interfaces, + * not IFT_IEEE80211 (which isn't defined in OS X in any case). + * + * Perhaps some other BSD-flavored OSes won't make this mistake; + * however, FreeBSD 7.0 and OpenBSD 4.2, at least, appear to have + * made the same mistake, at least for my Belkin ZyDAS stick. + * + * SCNetworkInterfaceGetInterfaceType() will get the interface + * type. The interface type is a CFString, and: + * + * kSCNetworkInterfaceTypeIEEE80211 means IF_WIRELESS; + * kSCNetworkInterfaceTypeBluetooth means IF_BLUETOOTH; + * kSCNetworkInterfaceTypeModem or + * kSCNetworkInterfaceTypePPP or + * maybe kSCNetworkInterfaceTypeWWAN means IF_DIALUP + */ +static void +add_unix_interface_ifinfo(if_info_t *if_info, const char *name, + const char *description _U_) +{ + CFStringRef name_CFString; + CFArrayRef interfaces; + CFIndex num_interfaces; + CFIndex i; + SCNetworkInterfaceRef interface; + CFStringRef bsdname_CFString; + CFStringRef friendly_name_CFString; + CFStringRef interface_type_CFString; + + interfaces = SCNetworkInterfaceCopyAll(); + if (interfaces == NULL) { + /* + * Couldn't get a list of interfaces. + */ + return; + } + + name_CFString = CFStringCreateWithCString(kCFAllocatorDefault, + name, kCFStringEncodingUTF8); + if (name_CFString == NULL) { + /* + * Couldn't convert the interface name to a CFString. + */ + CFRelease(interfaces); + return; + } + + num_interfaces = CFArrayGetCount(interfaces); + for (i = 0; i < num_interfaces; i++) { + interface = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(interfaces, i); + bsdname_CFString = SCNetworkInterfaceGetBSDName(interface); + if (bsdname_CFString == NULL) { + /* + * This interface has no BSD name, so it's not + * a regular network interface. + */ + continue; + } + if (CFStringCompare(name_CFString, bsdname_CFString, 0) == 0) { + /* + * This is the interface. + * First, get the friendly name. + */ + friendly_name_CFString = SCNetworkInterfaceGetLocalizedDisplayName(interface); + if (friendly_name_CFString != NULL) + if_info->friendly_name = CFString_to_C_string(friendly_name_CFString); + + /* + * Now get the interface type. + */ + interface_type_CFString = SCNetworkInterfaceGetInterfaceType(interface); + if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeIEEE80211, 0) == kCFCompareEqualTo) + if_info->type = IF_WIRELESS; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeBluetooth, 0) == kCFCompareEqualTo) + if_info->type = IF_BLUETOOTH; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeModem, 0) == kCFCompareEqualTo) + if_info->type = IF_DIALUP; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypePPP, 0) == kCFCompareEqualTo) + if_info->type = IF_DIALUP; + else if (CFStringCompare(interface_type_CFString, + kSCNetworkInterfaceTypeWWAN, 0) == kCFCompareEqualTo) + if_info->type = IF_DIALUP; + else + if_info->type = IF_WIRED; + break; + } + } + + CFRelease(interfaces); + CFRelease(name_CFString); +} +#elif defined(__linux__) +/* + * Linux doesn't offer any form of "friendly name", but you can + * determine an interface type to some degree. + */ +static void +add_unix_interface_ifinfo(if_info_t *if_info, const char *name, + const char *description _U_) +{ + char *wireless_path; + ws_statb64 statb; + + /* + * Look for /sys/class/net/{device}/wireless. If it exists, + * it's a wireless interface. + */ + wireless_path = g_strdup_printf("/sys/class/net/%s/wireless", name); + if (wireless_path != NULL) { + if (ws_stat64(wireless_path, &statb) == 0) + if_info->type = IF_WIRELESS; + g_free(wireless_path); + } + if (if_info->type == IF_WIRED) { + /* + * We still don't know what it is. Check for + * Bluetooth and USB devices. + */ + if (strstr(name, "bluetooth") != NULL) { + /* + * XXX - this is for raw Bluetooth capture; what + * about IP-over-Bluetooth devices? + */ + if_info->type = IF_BLUETOOTH; + } else if (strstr(name, "usbmon") != NULL) + if_info->type = IF_USB; + } +} +#else +/* + * On other UN*Xes, if there is a description, it's a friendly + * name, and there is no vendor description. ("Other UN*Xes" + * currently means "FreeBSD and OpenBSD".) + */ +void +add_unix_interface_ifinfo(if_info_t *if_info, const char *name _U_, + const char *description) +{ + if_info->friendly_name = g_strdup(description); +} +#endif + +if_info_t * +if_info_new(const char *name, const char *description, gboolean loopback) +{ + if_info_t *if_info; +#ifdef _WIN32 + const char *guid_text; + GUID guid; +#endif + + if_info = (if_info_t *)g_malloc(sizeof (if_info_t)); + if_info->name = g_strdup(name); + if_info->friendly_name = NULL; /* default - unknown */ + if_info->vendor_description = NULL; + if_info->type = IF_WIRED; /* default */ +#ifdef _WIN32 + /* + * Get the interface type. + * + * Much digging failed to reveal any obvious way to get something + * such as the SNMP MIB-II ifType value for an interface: + * + * http://www.iana.org/assignments/ianaiftype-mib + * + * by making some NDIS request. And even if there were such + * a way, there's no guarantee that the ifType reflects an + * interface type that a user would view as correct (for + * example, some systems report Wi-Fi interfaces as + * Ethernet interfaces). + * + * So we look for keywords in the vendor's interface + * description. + */ + if (description && (strstr(description, "generic dialup") != NULL || + strstr(description, "PPP/SLIP") != NULL)) { + if_info->type = IF_DIALUP; + } else if (description && (strstr(description, "Wireless") != NULL || + strstr(description,"802.11") != NULL)) { + if_info->type = IF_WIRELESS; + } else if (description && strstr(description, "AirPcap") != NULL || + strstr(name, "airpcap") != NULL) { + if_info->type = IF_AIRPCAP; + } else if (description && strstr(description, "Bluetooth") != NULL ) { + if_info->type = IF_BLUETOOTH; + } else if (description && strstr(description, "VMware") != NULL) { + /* + * Bridge, NAT, or host-only interface on a VMware host. + * + * XXX - what about guest interfaces? + */ + if_info->type = IF_VIRTUAL; + } + + /* + * On Windows, the "description" is a vendor description, + * and the friendly name isn't returned by WinPcap. + * Fetch it ourselves. + */ + + /* + * Skip over the "\Device\NPF_" prefix in the device name, + * if present. + */ + if (strncmp("\\Device\\NPF_", name, 12) == 0) + guid_text = name + 12; + else + guid_text = name; + + /* Now try to parse what remains as a GUID. */ + if (parse_as_guid(guid_text, &guid)) { + /* + * Success. Try to get a friendly name using the GUID. + * As this is a regular interface, the description is a + * vendor description. + */ + if_info->friendly_name = get_interface_friendly_name_from_device_guid(&guid); + if_info->vendor_description = g_strdup(description); + } else { + /* + * This is probably not a regular interface; we only + * support NT 5 (W2K) and later, so all regular interfaces + * should have GUIDs at the end of the name. Therefore, + * the description, if supplied, is a friendly name + * provided by WinPcap, and there is no vendor + * description. + */ + if_info->friendly_name = g_strdup(description); + if_info->vendor_description = NULL; + } +#else + /* + * On UN*X, if there is a description, it's a friendly + * name, and there is no vendor description. + * + * Try the platform's way of getting a friendly name and + * interface type first. + * + * If that fails, then, for a loopback interface, give it the + * friendly name "Loopback" and, for VMware interfaces, + * give them the type IF_VIRTUAL. + */ + add_unix_interface_ifinfo(if_info, name, description); + if (if_info->type == IF_WIRED) { + /* + * This is the default interface type. + * + * Bridge, NAT, or host-only interfaces on VMWare hosts + * have the name vmnet[0-9]+. Guests might use a native + * (LANCE or E1000) driver or the vmxnet driver. Check + * the name. + */ + if (g_ascii_strncasecmp(name, "vmnet", 5) == 0) + if_info->type = IF_VIRTUAL; + else if (g_ascii_strncasecmp(name, "vmxnet", 6) == 0) + if_info->type = IF_VIRTUAL; + } + if (if_info->friendly_name == NULL) { + /* + * We couldn't get interface information using platform- + * dependent calls. + * + * If this is a loopback interface, give it a + * "friendly name" of "Loopback". + */ + if (loopback) + if_info->friendly_name = g_strdup("Loopback"); + } + if_info->vendor_description = NULL; +#endif + if_info->loopback = loopback; + if_info->addrs = NULL; + return if_info; +} + +void +if_info_add_address(if_info_t *if_info, struct sockaddr *addr) +{ + if_addr_t *if_addr; + struct sockaddr_in *ai; +#ifdef INET6 + struct sockaddr_in6 *ai6; +#endif + + switch (addr->sa_family) { + + case AF_INET: + ai = (struct sockaddr_in *)(void *)addr; + if_addr = (if_addr_t *)g_malloc(sizeof(*if_addr)); + if_addr->ifat_type = IF_AT_IPv4; + if_addr->addr.ip4_addr = + *((guint32 *)&(ai->sin_addr.s_addr)); + if_info->addrs = g_slist_append(if_info->addrs, if_addr); + break; + +#ifdef INET6 + case AF_INET6: + ai6 = (struct sockaddr_in6 *)(void *)addr; + if_addr = (if_addr_t *)g_malloc(sizeof(*if_addr)); + if_addr->ifat_type = IF_AT_IPv6; + memcpy((void *)&if_addr->addr.ip6_addr, + (void *)&ai6->sin6_addr.s6_addr, + sizeof if_addr->addr.ip6_addr); + if_info->addrs = g_slist_append(if_info->addrs, if_addr); + break; +#endif + } +} + +#ifdef HAVE_PCAP_FINDALLDEVS +/* + * Get all IP address information for the given interface. + */ +static void +if_info_ip(if_info_t *if_info, pcap_if_t *d) +{ + pcap_addr_t *a; + + /* All addresses */ + for (a = d->addresses; a != NULL; a = a->next) { + if (a->addr != NULL) + if_info_add_address(if_info, a->addr); + } +} + +#ifdef HAVE_PCAP_REMOTE +GList * +get_interface_list_findalldevs_ex(const char *source, + struct pcap_rmtauth *auth, + int *err, char **err_str) +{ + GList *il = NULL; + pcap_if_t *alldevs, *dev; + if_info_t *if_info; + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs_ex((char *)source, auth, &alldevs, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + return NULL; + } + + if (alldevs == NULL) { + /* + * No interfaces found. + */ + *err = NO_INTERFACES_FOUND; + if (err_str != NULL) + *err_str = NULL; + return NULL; + } + + for (dev = alldevs; dev != NULL; dev = dev->next) { + if_info = if_info_new(dev->name, dev->description, + (dev->flags & PCAP_IF_LOOPBACK) ? TRUE : FALSE); + il = g_list_append(il, if_info); + if_info_ip(if_info, dev); + } + pcap_freealldevs(alldevs); + + return il; +} +#endif + +GList * +get_interface_list_findalldevs(int *err, char **err_str) +{ + GList *il = NULL; + pcap_if_t *alldevs, *dev; + if_info_t *if_info; + char errbuf[PCAP_ERRBUF_SIZE]; + + if (pcap_findalldevs(&alldevs, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + return NULL; + } + + if (alldevs == NULL) { + /* + * No interfaces found. + */ + *err = NO_INTERFACES_FOUND; + if (err_str != NULL) + *err_str = NULL; + return NULL; + } + + for (dev = alldevs; dev != NULL; dev = dev->next) { + if_info = if_info_new(dev->name, dev->description, + (dev->flags & PCAP_IF_LOOPBACK) ? TRUE : FALSE); + il = g_list_append(il, if_info); + if_info_ip(if_info, dev); + } + pcap_freealldevs(alldevs); + + return il; +} +#endif /* HAVE_PCAP_FINDALLDEVS */ + +static void +free_if_info_addr_cb(gpointer addr, gpointer user_data _U_) +{ + g_free(addr); +} + +static void +free_if_cb(gpointer data, gpointer user_data _U_) +{ + if_info_t *if_info = (if_info_t *)data; + + g_free(if_info->name); + g_free(if_info->friendly_name); + g_free(if_info->vendor_description); + + g_slist_foreach(if_info->addrs, free_if_info_addr_cb, NULL); + g_slist_free(if_info->addrs); + g_free(if_info); +} + +void +free_interface_list(GList *if_list) +{ + g_list_foreach(if_list, free_if_cb, NULL); + g_list_free(if_list); +} + +#if !defined(HAVE_PCAP_DATALINK_NAME_TO_VAL) || !defined(HAVE_PCAP_DATALINK_VAL_TO_NAME) || !defined(HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION) +struct dlt_choice { + const char *name; + const char *description; + int dlt; +}; + +#define DLT_CHOICE(code, description) { #code, description, code } +#define DLT_CHOICE_SENTINEL { NULL, NULL, 0 } + +static struct dlt_choice dlt_choices[] = { + DLT_CHOICE(DLT_NULL, "BSD loopback"), + DLT_CHOICE(DLT_EN10MB, "Ethernet"), + DLT_CHOICE(DLT_IEEE802, "Token ring"), + DLT_CHOICE(DLT_ARCNET, "ARCNET"), + DLT_CHOICE(DLT_SLIP, "SLIP"), + DLT_CHOICE(DLT_PPP, "PPP"), + DLT_CHOICE(DLT_FDDI, "FDDI"), + DLT_CHOICE(DLT_ATM_RFC1483, "RFC 1483 IP-over-ATM"), + DLT_CHOICE(DLT_RAW, "Raw IP"), + DLT_CHOICE(DLT_SLIP_BSDOS, "BSD/OS SLIP"), + DLT_CHOICE(DLT_PPP_BSDOS, "BSD/OS PPP"), + DLT_CHOICE(DLT_ATM_CLIP, "Linux Classical IP-over-ATM"), + DLT_CHOICE(DLT_PPP_SERIAL, "PPP over serial"), + DLT_CHOICE(DLT_PPP_ETHER, "PPPoE"), + DLT_CHOICE(DLT_C_HDLC, "Cisco HDLC"), + DLT_CHOICE(DLT_IEEE802_11, "802.11"), + DLT_CHOICE(DLT_FRELAY, "Frame Relay"), + DLT_CHOICE(DLT_LOOP, "OpenBSD loopback"), + DLT_CHOICE(DLT_ENC, "OpenBSD encapsulated IP"), + DLT_CHOICE(DLT_LINUX_SLL, "Linux cooked"), + DLT_CHOICE(DLT_LTALK, "Localtalk"), + DLT_CHOICE(DLT_PFLOG, "OpenBSD pflog file"), + DLT_CHOICE(DLT_PRISM_HEADER, "802.11 plus Prism header"), + DLT_CHOICE(DLT_IP_OVER_FC, "RFC 2625 IP-over-Fibre Channel"), + DLT_CHOICE(DLT_SUNATM, "Sun raw ATM"), + DLT_CHOICE(DLT_IEEE802_11_RADIO, "802.11 plus BSD radio information header"), + DLT_CHOICE(DLT_APPLE_IP_OVER_IEEE1394, "Apple IP-over-IEEE 1394"), + DLT_CHOICE(DLT_ARCNET_LINUX, "Linux ARCNET"), + DLT_CHOICE(DLT_LINUX_IRDA, "Linux IrDA"), + DLT_CHOICE(DLT_IEEE802_11_RADIO_AVS, "802.11 plus AVS radio information header"), + DLT_CHOICE_SENTINEL +}; + +#if !defined(HAVE_PCAP_DATALINK_NAME_TO_VAL) +static int +pcap_datalink_name_to_val(const char *name) +{ + int i; + + for (i = 0; dlt_choices[i].name != NULL; i++) { + if (g_ascii_strcasecmp(dlt_choices[i].name + sizeof("DLT_") - 1, + name) == 0) + return (dlt_choices[i].dlt); + } + return (-1); +} +#endif /* defined(HAVE_PCAP_DATALINK_NAME_TO_VAL) */ + +#if !defined(HAVE_PCAP_DATALINK_VAL_TO_NAME) +static const char * +pcap_datalink_val_to_name(int dlt) +{ + int i; + + for (i = 0; dlt_choices[i].name != NULL; i++) { + if (dlt_choices[i].dlt == dlt) + return (dlt_choices[i].name + sizeof("DLT_") - 1); + } + return (NULL); +} +#endif /* defined(HAVE_PCAP_DATALINK_VAL_TO_NAME) */ + +#if !defined(HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION) +const char * +pcap_datalink_val_to_description(int dlt) +{ + int i; + + for (i = 0; dlt_choices[i].name != NULL; i++) { + if (dlt_choices[i].dlt == dlt) + return (dlt_choices[i].description); + } + return (NULL); +} +#endif /* defined(HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION) */ + +#endif /* !defined(HAVE_PCAP_DATALINK_VAL_TO_NAME) || !defined(HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION) */ + +static void +free_linktype_cb(gpointer data, gpointer user_data _U_) +{ + data_link_info_t *linktype_info = (data_link_info_t *)data; + + g_free(linktype_info->name); + g_free(linktype_info->description); +} + +void +free_if_capabilities(if_capabilities_t *caps) +{ + g_list_foreach(caps->data_link_types, free_linktype_cb, NULL); + g_list_free(caps->data_link_types); + g_free(caps); +} + +const char * +linktype_val_to_name(int dlt) +{ + return pcap_datalink_val_to_name(dlt); +} + +int linktype_name_to_val(const char *linktype) +{ + return pcap_datalink_name_to_val(linktype); +} + +#endif /* HAVE_LIBPCAP */ diff --git a/caputils/capture-pcap-util.h b/caputils/capture-pcap-util.h new file mode 100644 index 0000000000..617bcc82e5 --- /dev/null +++ b/caputils/capture-pcap-util.h @@ -0,0 +1,74 @@ +/* capture-pcap-util.h + * Utility definitions for packet capture + * + * 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. + */ + +#ifndef __CAPTURE_PCAP_UTIL_H__ +#define __CAPTURE_PCAP_UTIL_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef HAVE_LIBPCAP + +#include <pcap.h> + +/* + * A snapshot length of 0 is useless - and libpcap/WinPcap don't guarantee + * that a snapshot length of 0 will work, and, on some platforms, it won't + * (with BPF, for example, the kernel is told the snapshot length via the + * return value of the BPF program, and a return value of 0 means "drop + * the packet"), so the minimum packet size is 1 byte. + */ +#define MIN_PACKET_SIZE 1 /* minimum amount of packet data we can read */ + +GList *get_interface_list(int *err, char **err_str); +#ifdef HAVE_PCAP_REMOTE +GList *get_remote_interface_list(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str); +#endif + +const char *linktype_val_to_name(int dlt); +int linktype_name_to_val(const char *linktype); + +#endif /* HAVE_LIBPCAP */ + +/* + * Get the versions of capture libraries with which we were compiled, + * and append them to a GString. + */ +extern void get_compiled_caplibs_version(GString *str); + +/* + * Append to a GString an indication of the version of capture libraries + * with which we're running, or an indication that we're not running + * with capture libraries, if we were compiled with WinPcap but + * WinPcap wasn't loaded, or nothing, if we weren't compiled with + * libpcap/WinPcap. + */ +extern void get_runtime_caplibs_version(GString *str); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPTURE_PCAP_UTIL_H__ */ diff --git a/caputils/capture-wpcap.c b/caputils/capture-wpcap.c new file mode 100644 index 0000000000..f4f9d61c25 --- /dev/null +++ b/caputils/capture-wpcap.c @@ -0,0 +1,990 @@ +/* capture-wpcap.c + * WinPcap-specific interfaces for capturing. We load WinPcap at run + * time, so that we only need one Wireshark binary and one TShark binary + * for Windows, regardless of whether WinPcap is installed or not. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 2001 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" + +#include <stdio.h> +#include <glib.h> +#include <gmodule.h> + +#include <epan/strutil.h> + +#include "caputils/capture_ifinfo.h" +#include "caputils/capture-pcap-util.h" +#include "caputils/capture-pcap-util-int.h" +#include "caputils/capture-wpcap.h" + +#include <wsutil/file_util.h> + +/* XXX - yes, I know, I should move cppmagic.h to a generic location. */ +#include "tools/lemon/cppmagic.h" + +#define MAX_WIN_IF_NAME_LEN 511 + + +gboolean has_wpcap = FALSE; + +#ifdef HAVE_LIBPCAP + +/* + * XXX - should we require at least WinPcap 3.1 both for building an + * for using Wireshark? + */ + +static char* (*p_pcap_lookupdev) (char *); +static void (*p_pcap_close) (pcap_t *); +static int (*p_pcap_stats) (pcap_t *, struct pcap_stat *); +static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, guchar *); +static int (*p_pcap_snapshot) (pcap_t *); +static int (*p_pcap_datalink) (pcap_t *); +static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); +static char* (*p_pcap_geterr) (pcap_t *); +static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, const char *, int, + bpf_u_int32); +static int (*p_pcap_compile_nopcap) (int, int, struct bpf_program *, const char *, int, + bpf_u_int32); +static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, + char *); +static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); +static int (*p_pcap_loop) (pcap_t *, int, pcap_handler, guchar *); +#ifdef HAVE_PCAP_OPEN_DEAD +static pcap_t* (*p_pcap_open_dead) (int, int); +#endif +static void (*p_pcap_freecode) (struct bpf_program *); +#ifdef HAVE_PCAP_FINDALLDEVS +static int (*p_pcap_findalldevs) (pcap_if_t **, char *); +static void (*p_pcap_freealldevs) (pcap_if_t *); +#endif +#ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL +static int (*p_pcap_datalink_name_to_val) (const char *); +#endif +#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME +static const char *(*p_pcap_datalink_val_to_name) (int); +#endif +#ifdef HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION +static const char *(*p_pcap_datalink_val_to_description) (int); +#endif +#ifdef HAVE_PCAP_BREAKLOOP +static void (*p_pcap_breakloop) (pcap_t *); +#endif +static const char *(*p_pcap_lib_version) (void); +static int (*p_pcap_setbuff) (pcap_t *, int dim); +static int (*p_pcap_next_ex) (pcap_t *, struct pcap_pkthdr **pkt_header, const u_char **pkt_data); +#ifdef HAVE_PCAP_REMOTE +static pcap_t* (*p_pcap_open) (const char *, int, int, int, + struct pcap_rmtauth *, char *); +static int (*p_pcap_findalldevs_ex) (char *, struct pcap_rmtauth *, + pcap_if_t **, char *); +static int (*p_pcap_createsrcstr) (char *, int, const char *, const char *, + const char *, char *); +#endif +#ifdef HAVE_PCAP_SETSAMPLING +static struct pcap_samp* (*p_pcap_setsampling)(pcap_t *); +#endif + +#ifdef HAVE_PCAP_LIST_DATALINKS +static int (*p_pcap_list_datalinks)(pcap_t *, int **); +#endif + +#ifdef HAVE_PCAP_SET_DATALINK +static int (*p_pcap_set_datalink)(pcap_t *, int); +#endif + +#ifdef HAVE_PCAP_FREE_DATALINKS +static int (*p_pcap_free_datalinks)(int *); +#endif + +#ifdef HAVE_BPF_IMAGE +static char *(*p_bpf_image) (const struct bpf_insn *, int); +#endif + +typedef struct { + const char *name; + gpointer *ptr; + gboolean optional; +} symbol_table_t; + +#define SYM(x, y) { G_STRINGIFY(x) , (gpointer) &CONCAT(p_,x), y } + +void +load_wpcap(void) +{ + + /* These are the symbols I need or want from Wpcap */ + static const symbol_table_t symbols[] = { + SYM(pcap_lookupdev, FALSE), + SYM(pcap_close, FALSE), + SYM(pcap_stats, FALSE), + SYM(pcap_dispatch, FALSE), + SYM(pcap_snapshot, FALSE), + SYM(pcap_datalink, FALSE), + SYM(pcap_setfilter, FALSE), + SYM(pcap_geterr, FALSE), + SYM(pcap_compile, FALSE), + SYM(pcap_compile_nopcap, FALSE), + SYM(pcap_lookupnet, FALSE), +#ifdef HAVE_PCAP_REMOTE + SYM(pcap_open, FALSE), + SYM(pcap_findalldevs_ex, FALSE), + SYM(pcap_createsrcstr, FALSE), +#endif + SYM(pcap_open_live, FALSE), +#ifdef HAVE_PCAP_OPEN_DEAD + SYM(pcap_open_dead, FALSE), +#endif +#ifdef HAVE_PCAP_SETSAMPLING + SYM(pcap_setsampling, TRUE), +#endif + SYM(pcap_loop, FALSE), + SYM(pcap_freecode, TRUE), +#ifdef HAVE_PCAP_FINDALLDEVS + SYM(pcap_findalldevs, TRUE), + SYM(pcap_freealldevs, TRUE), +#endif +#ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL + SYM(pcap_datalink_name_to_val, TRUE), +#endif +#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME + SYM(pcap_datalink_val_to_name, TRUE), +#endif +#ifdef HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION + SYM(pcap_datalink_val_to_description, TRUE), +#endif +#ifdef HAVE_PCAP_BREAKLOOP + /* + * We don't try to work around the lack of this at + * run time; it's present in WinPcap 3.1, which is + * the version we build with and ship with. + */ + SYM(pcap_breakloop, FALSE), +#endif + SYM(pcap_lib_version, TRUE), + SYM(pcap_setbuff, TRUE), + SYM(pcap_next_ex, TRUE), +#ifdef HAVE_PCAP_LIST_DATALINKS + SYM(pcap_list_datalinks, FALSE), +#endif +#ifdef HAVE_PCAP_SET_DATALINK + SYM(pcap_set_datalink, FALSE), +#endif +#ifdef HAVE_PCAP_FREE_DATALINKS + SYM(pcap_free_datalinks, TRUE), +#endif +#ifdef HAVE_BPF_IMAGE + SYM(bpf_image, FALSE), +#endif + { NULL, NULL, FALSE } + }; + + GModule *wh; /* wpcap handle */ + const symbol_table_t *sym; + + wh = ws_module_open("wpcap.dll", 0); + + if (!wh) { + return; + } + + sym = symbols; + while (sym->name) { + if (!g_module_symbol(wh, sym->name, sym->ptr)) { + if (sym->optional) { + /* + * We don't care if it's missing; we just + * don't use it. + */ + *sym->ptr = NULL; + } else { + /* + * We require this symbol. + */ + return; + } + } + sym++; + } + + + has_wpcap = TRUE; +} + +/* + * The official list of WinPcap mirrors is at + * http://www.winpcap.org/misc/mirrors.htm + */ +char * +cant_load_winpcap_err(const char *app_name) +{ + return g_strdup_printf( +"Unable to load WinPcap (wpcap.dll); %s will not be able to capture\n" +"packets.\n" +"\n" +"In order to capture packets, WinPcap must be installed; see\n" +"\n" +" http://www.winpcap.org/\n" +"\n" +"or the mirror at\n" +"\n" +" http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n" +"\n" +"or the mirror at\n" +"\n" +" http://winpcap.cs.pu.edu.tw/\n" +"\n" +"for a downloadable version of WinPcap and for instructions on how to install\n" +"WinPcap.", + app_name); +} + +char* +pcap_lookupdev (char *a) +{ + if (!has_wpcap) { + return NULL; + } + return p_pcap_lookupdev(a); +} + +void +pcap_close(pcap_t *a) +{ + g_assert(has_wpcap); + p_pcap_close(a); +} + +int +pcap_stats(pcap_t *a, struct pcap_stat *b) +{ + g_assert(has_wpcap); + return p_pcap_stats(a, b); +} + +int +pcap_dispatch(pcap_t *a, int b, pcap_handler c, guchar *d) +{ + g_assert(has_wpcap); + return p_pcap_dispatch(a, b, c, d); +} + +int +pcap_snapshot(pcap_t *a) +{ + g_assert(has_wpcap); + return p_pcap_snapshot(a); +} + +int +pcap_datalink(pcap_t *a) +{ + g_assert(has_wpcap); + return p_pcap_datalink(a); +} + +#ifdef HAVE_PCAP_SET_DATALINK +int +pcap_set_datalink(pcap_t *p, int dlt) +{ + g_assert(has_wpcap); + return p_pcap_set_datalink(p, dlt); +} +#endif + +int +pcap_setfilter(pcap_t *a, struct bpf_program *b) +{ + g_assert(has_wpcap); + return p_pcap_setfilter(a, b); +} + +char* +pcap_geterr(pcap_t *a) +{ + g_assert(has_wpcap); + return p_pcap_geterr(a); +} + +int +pcap_compile(pcap_t *a, struct bpf_program *b, const char *c, int d, + bpf_u_int32 e) +{ + g_assert(has_wpcap); + return p_pcap_compile(a, b, c, d, e); +} + +int +pcap_compile_nopcap(int a, int b, struct bpf_program *c, const char *d, int e, + bpf_u_int32 f) +{ + g_assert(has_wpcap); + return p_pcap_compile_nopcap(a, b, c, d, e, f); +} + +int +pcap_lookupnet(const char *a, bpf_u_int32 *b, bpf_u_int32 *c, char *d) +{ + g_assert(has_wpcap); + return p_pcap_lookupnet(a, b, c, d); +} + +pcap_t* +pcap_open_live(const char *a, int b, int c, int d, char *e) +{ + if (!has_wpcap) { + g_snprintf(e, PCAP_ERRBUF_SIZE, + "unable to load WinPcap (wpcap.dll); can't open %s to capture", + a); + return NULL; + } + return p_pcap_open_live(a, b, c, d, e); +} + +#ifdef HAVE_PCAP_OPEN_DEAD +pcap_t* +pcap_open_dead(int a, int b) +{ + if (!has_wpcap) { + return NULL; + } + return p_pcap_open_dead(a, b); +} +#endif + +#ifdef HAVE_BPF_IMAGE +char * +bpf_image(const struct bpf_insn *a, int b) +{ + if (!has_wpcap) { + return NULL; + } + return p_bpf_image(a, b); +} +#endif + +#ifdef HAVE_PCAP_REMOTE +pcap_t* +pcap_open(const char *a, int b, int c, int d, struct pcap_rmtauth *e, char *f) +{ + if (!has_wpcap) { + g_snprintf(f, PCAP_ERRBUF_SIZE, + "unable to load WinPcap (wpcap.dll); can't open %s to capture", + a); + return NULL; + } + return p_pcap_open(a, b, c, d, e, f); +} + +int +pcap_findalldevs_ex(char *a, struct pcap_rmtauth *b, pcap_if_t **c, char *d) +{ + g_assert(has_wpcap); + return p_pcap_findalldevs_ex(a, b, c, d); +} + +int +pcap_createsrcstr(char *a, int b, const char *c, const char *d, const char *e, + char *f) +{ + g_assert(has_wpcap); + return p_pcap_createsrcstr(a, b, c, d, e, f); +} +#endif + +#ifdef HAVE_PCAP_SETSAMPLING +struct pcap_samp * +pcap_setsampling(pcap_t *a) +{ + g_assert(has_wpcap); + if (p_pcap_setsampling != NULL) { + return p_pcap_setsampling(a); + } + return NULL; +} +#endif + +int +pcap_loop(pcap_t *a, int b, pcap_handler c, guchar *d) +{ + g_assert(has_wpcap); + return p_pcap_loop(a, b, c, d); +} + +void +pcap_freecode(struct bpf_program *a) +{ + g_assert(has_wpcap); + if(p_pcap_freecode) { + p_pcap_freecode(a); + } +} + +#ifdef HAVE_PCAP_FINDALLDEVS +int +pcap_findalldevs(pcap_if_t **a, char *b) +{ + g_assert(has_wpcap && p_pcap_findalldevs != NULL); + return p_pcap_findalldevs(a, b); +} + +void +pcap_freealldevs(pcap_if_t *a) +{ + g_assert(has_wpcap && p_pcap_freealldevs != NULL); + p_pcap_freealldevs(a); +} +#endif + +#if defined(HAVE_PCAP_DATALINK_NAME_TO_VAL) || defined(HAVE_PCAP_DATALINK_VAL_TO_NAME) || defined(HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION) +/* + * Table of DLT_ types, names, and descriptions, for use if the version + * of WinPcap we have installed lacks "pcap_datalink_name_to_val()" + * or "pcap_datalink_val_to_name()". + */ +struct dlt_choice { + const char *name; + const char *description; + int dlt; +}; + +#define DLT_CHOICE(code, description) { #code, description, code } +#define DLT_CHOICE_SENTINEL { NULL, NULL, 0 } + +static struct dlt_choice dlt_choices[] = { + DLT_CHOICE(DLT_NULL, "BSD loopback"), + DLT_CHOICE(DLT_EN10MB, "Ethernet"), + DLT_CHOICE(DLT_IEEE802, "Token ring"), + DLT_CHOICE(DLT_ARCNET, "ARCNET"), + DLT_CHOICE(DLT_SLIP, "SLIP"), + DLT_CHOICE(DLT_PPP, "PPP"), + DLT_CHOICE(DLT_FDDI, "FDDI"), + DLT_CHOICE(DLT_ATM_RFC1483, "RFC 1483 IP-over-ATM"), + DLT_CHOICE(DLT_RAW, "Raw IP"), +#ifdef DLT_SLIP_BSDOS + DLT_CHOICE(DLT_SLIP_BSDOS, "BSD/OS SLIP"), +#endif +#ifdef DLT_PPP_BSDOS + DLT_CHOICE(DLT_PPP_BSDOS, "BSD/OS PPP"), +#endif +#ifdef DLT_ATM_CLIP + DLT_CHOICE(DLT_ATM_CLIP, "Linux Classical IP-over-ATM"), +#endif +#ifdef DLT_PPP_SERIAL + DLT_CHOICE(DLT_PPP_SERIAL, "PPP over serial"), +#endif +#ifdef DLT_PPP_ETHER + DLT_CHOICE(DLT_PPP_ETHER, "PPPoE"), +#endif +#ifdef DLT_C_HDLC + DLT_CHOICE(DLT_C_HDLC, "Cisco HDLC"), +#endif +#ifdef DLT_IEEE802_11 + DLT_CHOICE(DLT_IEEE802_11, "802.11"), +#endif +#ifdef DLT_FRELAY + DLT_CHOICE(DLT_FRELAY, "Frame Relay"), +#endif +#ifdef DLT_LOOP + DLT_CHOICE(DLT_LOOP, "OpenBSD loopback"), +#endif +#ifdef DLT_ENC + DLT_CHOICE(DLT_ENC, "OpenBSD encapsulated IP"), +#endif +#ifdef DLT_LINUX_SLL + DLT_CHOICE(DLT_LINUX_SLL, "Linux cooked"), +#endif +#ifdef DLT_LTALK + DLT_CHOICE(DLT_LTALK, "Localtalk"), +#endif +#ifdef DLT_PFLOG + DLT_CHOICE(DLT_PFLOG, "OpenBSD pflog file"), +#endif +#ifdef DLT_PRISM_HEADER + DLT_CHOICE(DLT_PRISM_HEADER, "802.11 plus Prism header"), +#endif +#ifdef DLT_IP_OVER_FC + DLT_CHOICE(DLT_IP_OVER_FC, "RFC 2625 IP-over-Fibre Channel"), +#endif +#ifdef DLT_SUNATM + DLT_CHOICE(DLT_SUNATM, "Sun raw ATM"), +#endif +#ifdef DLT_IEEE802_11_RADIO + DLT_CHOICE(DLT_IEEE802_11_RADIO, "802.11 plus radio information header"), +#endif +#ifdef DLT_ARCNET_LINUX + DLT_CHOICE(DLT_ARCNET_LINUX, "Linux ARCNET"), +#endif +#ifdef DLT_LINUX_IRDA + DLT_CHOICE(DLT_LINUX_IRDA, "Linux IrDA"), +#endif +#ifdef DLT_LINUX_LAPD + DLT_CHOICE(DLT_LINUX_LAPD, "Linux vISDN LAPD"), +#endif +#ifdef DLT_LANE8023 + DLT_CHOICE(DLT_LANE8023, "Linux 802.3 LANE"), +#endif +#ifdef DLT_CIP + DLT_CHOICE(DLT_CIP, "Linux Classical IP-over-ATM"), +#endif +#ifdef DLT_HDLC + DLT_CHOICE(DLT_HDLC, "Cisco HDLC"), +#endif +#ifdef DLT_PPI + DLT_CHOICE(DLT_PPI, "Per-Packet Information"), +#endif + DLT_CHOICE_SENTINEL +}; +#endif /* defined(HAVE_PCAP_DATALINK_NAME_TO_VAL) || defined(HAVE_PCAP_DATALINK_VAL_TO_NAME) || defined(HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION */ + +#ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL +int +pcap_datalink_name_to_val(const char *name) +{ + int i; + + g_assert(has_wpcap); + + if (p_pcap_datalink_name_to_val != NULL) + return p_pcap_datalink_name_to_val(name); + else { + /* + * We don't have it in WinPcap; do it ourselves. + */ + for (i = 0; dlt_choices[i].name != NULL; i++) { + if (g_ascii_strcasecmp(dlt_choices[i].name + sizeof("DLT_") - 1, + name) == 0) + return dlt_choices[i].dlt; + } + return -1; + } +} +#endif + +#ifdef HAVE_PCAP_LIST_DATALINKS +int +pcap_list_datalinks(pcap_t *p, int **ddlt) +{ + g_assert(has_wpcap); + return p_pcap_list_datalinks(p, ddlt); +} +#endif + +#ifdef HAVE_PCAP_FREE_DATALINKS +void +pcap_free_datalinks(int *ddlt) +{ + g_assert(has_wpcap); + + /* + * If we don't have pcap_free_datalinks() in WinPcap, + * we don't free the memory - we can't use free(), as + * we might not have been built with the same version + * of the C runtime library as WinPcap was, and, if we're + * not, free() isn't guaranteed to work on something + * allocated by WinPcap. + */ + if (p_pcap_free_datalinks != NULL) + p_pcap_free_datalinks(ddlt); +} +#endif + +#ifdef HAVE_PCAP_DATALINK_VAL_TO_NAME +const char * +pcap_datalink_val_to_name(int dlt) +{ + int i; + + g_assert(has_wpcap); + + if (p_pcap_datalink_val_to_name != NULL) + return p_pcap_datalink_val_to_name(dlt); + else { + /* + * We don't have it in WinPcap; do it ourselves. + */ + for (i = 0; dlt_choices[i].name != NULL; i++) { + if (dlt_choices[i].dlt == dlt) + return dlt_choices[i].name + sizeof("DLT_") - 1; + } + return NULL; + } +} +#endif + +#ifdef HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION +const char * +pcap_datalink_val_to_description(int dlt) +{ + int i; + + g_assert(has_wpcap); + + if (p_pcap_datalink_val_to_description != NULL) + return p_pcap_datalink_val_to_description(dlt); + else { + /* + * We don't have it in WinPcap; do it ourselves. + */ + for (i = 0; dlt_choices[i].name != NULL; i++) { + if (dlt_choices[i].dlt == dlt) + return (dlt_choices[i].description); + } + return NULL; + } +} +#endif + +#ifdef HAVE_PCAP_BREAKLOOP +void pcap_breakloop(pcap_t *a) +{ + p_pcap_breakloop(a); +} +#endif + +/* setbuff is win32 specific! */ +int pcap_setbuff(pcap_t *a, int b) +{ + g_assert(has_wpcap); + return p_pcap_setbuff(a, b); +} + +/* pcap_next_ex is available since libpcap 0.8 / WinPcap 3.0! */ +/* (if you get a declaration warning here, try to update to at least WinPcap 3.1b4 develpack) */ +int pcap_next_ex (pcap_t *a, struct pcap_pkthdr **b, const u_char **c) +{ + g_assert(has_wpcap); + return p_pcap_next_ex(a, b, c); +} + +#ifdef HAVE_PCAP_REMOTE +GList * +get_remote_interface_list(const char *hostname, const char *port, + int auth_type, const char *username, + const char *passwd, int *err, char **err_str) +{ + struct pcap_rmtauth auth; + char source[PCAP_BUF_SIZE]; + char errbuf[PCAP_ERRBUF_SIZE]; + GList *result; + + if (pcap_createsrcstr(source, PCAP_SRC_IFREMOTE, hostname, port, + NULL, errbuf) == -1) { + *err = CANT_GET_INTERFACE_LIST; + if (err_str != NULL) + *err_str = cant_get_if_list_error_message(errbuf); + return NULL; + } + + auth.type = auth_type; + auth.username = g_strdup(username); + auth.password = g_strdup(passwd); + + result = get_interface_list_findalldevs_ex(source, &auth, err, err_str); + g_free(auth.username); + g_free(auth.password); + + return result; +} +#endif + +/* + * This will use "pcap_findalldevs()" if we have it, otherwise it'll + * fall back on "pcap_lookupdev()". + */ +GList * +get_interface_list(int *err, char **err_str) +{ + GList *il = NULL; + wchar_t *names; + char *win95names; + char ascii_name[MAX_WIN_IF_NAME_LEN + 1]; + char ascii_desc[MAX_WIN_IF_NAME_LEN + 1]; + int i, j; + char errbuf[PCAP_ERRBUF_SIZE]; + + if (!has_wpcap) { + /* + * We don't have WinPcap, so we can't get a list of + * interfaces. + */ + *err = DONT_HAVE_PCAP; + *err_str = cant_load_winpcap_err("you"); + return NULL; + } + +#ifdef HAVE_PCAP_FINDALLDEVS + if (p_pcap_findalldevs != NULL) + return get_interface_list_findalldevs(err, err_str); +#endif + + /* + * In WinPcap, pcap_lookupdev is implemented by calling + * PacketGetAdapterNames. According to the documentation + * I could find: + * + * http://www.winpcap.org/docs/man/html/Packet32_8c.html#a43 + * + * this means that: + * + * On Windows OT (95, 98, Me), pcap_lookupdev returns a sequence + * of bytes consisting of: + * + * a sequence of null-terminated ASCII strings (i.e., each + * one is terminated by a single 0 byte), giving the names + * of the interfaces; + * + * an empty ASCII string (i.e., a single 0 byte); + * + * a sequence of null-terminated ASCII strings, giving the + * descriptions of the interfaces; + * + * an empty ASCII string. + * + * On Windows NT (NT 4.0, W2K, WXP, W2K3, etc.), pcap_lookupdev + * returns a sequence of bytes consisting of: + * + * a sequence of null-terminated double-byte Unicode strings + * (i.e., each one consits of a sequence of double-byte + * characters, terminated by a double-byte 0), giving the + * names of the interfaces; + * + * an empty Unicode string (i.e., a double 0 byte); + * + * a sequence of null-terminated ASCII strings, giving the + * descriptions of the interfaces; + * + * an empty ASCII string. + * + * The Nth string in the first sequence is the name of the Nth + * adapter; the Nth string in the second sequence is the + * description of the Nth adapter. + */ + + names = (wchar_t *)pcap_lookupdev(errbuf); + i = 0; + + if (names) { + char* desc = 0; + int desc_pos = 0; + + if (names[0]<256) { + /* + * If names[0] is less than 256 it means the first + * byte is 0. This implies that we are using Unicode + * characters. + */ + while (*(names+desc_pos) || *(names+desc_pos-1)) + desc_pos++; + desc_pos++; /* Step over the extra '\0' */ + desc = (char*)(names + desc_pos); /* cast *after* addition */ + + while (names[i] != 0) { + /* + * Copy the Unicode description to an ASCII + * string. + */ + j = 0; + while (*desc != 0) { + if (j < MAX_WIN_IF_NAME_LEN) + ascii_desc[j++] = *desc; + desc++; + } + ascii_desc[j] = '\0'; + desc++; + + /* + * Copy the Unicode name to an ASCII string. + */ + j = 0; + while (names[i] != 0) { + if (j < MAX_WIN_IF_NAME_LEN) + ascii_name[j++] = (char) names[i++]; + } + ascii_name[j] = '\0'; + i++; + il = g_list_append(il, + if_info_new(ascii_name, ascii_desc, FALSE)); + } + } else { + /* + * Otherwise we are in Windows 95/98 and using ASCII + * (8-bit) characters. + */ + win95names=(char *)names; + while (*(win95names+desc_pos) || *(win95names+desc_pos-1)) + desc_pos++; + desc_pos++; /* Step over the extra '\0' */ + desc = win95names + desc_pos; + + while (win95names[i] != '\0') { + /* + * "&win95names[i]" points to the current + * interface name, and "desc" points to + * that interface's description. + */ + il = g_list_append(il, + if_info_new(&win95names[i], desc, FALSE)); + + /* + * Skip to the next description. + */ + while (*desc != 0) + desc++; + desc++; + + /* + * Skip to the next name. + */ + while (win95names[i] != 0) + i++; + i++; + } + } + } + + if (il == NULL) { + /* + * No interfaces found. + */ + *err = NO_INTERFACES_FOUND; + if (err_str != NULL) + *err_str = NULL; + } + + return il; +} + +/* + * Get an error message string for a CANT_GET_INTERFACE_LIST error from + * "get_interface_list()". + */ +gchar * +cant_get_if_list_error_message(const char *err_str) +{ + /* + * If the error message includes "Not enough storage is available + * to process this command" or "The operation completed successfully", + * suggest that they install a WinPcap version later than 3.0. + */ + if (strstr(err_str, "Not enough storage is available to process this command") != NULL || + strstr(err_str, "The operation completed successfully") != NULL) { + return g_strdup_printf("Can't get list of interfaces: %s\n" +"This might be a problem with WinPcap 3.0; you should try updating to\n" +"a later version of WinPcap - see the WinPcap site at www.winpcap.org", + err_str); + } + return g_strdup_printf("Can't get list of interfaces: %s", err_str); +} + +/* + * Append the version of WinPcap with which we were compiled to a GString. + */ +void +get_compiled_pcap_version(GString *str) +{ + g_string_append(str, "with WinPcap (" G_STRINGIFY(WINPCAP_VERSION) ")"); +} + +/* + * Append the version of WinPcap with which we we're running to a GString. + */ +void +get_runtime_pcap_version(GString *str) +{ + /* + * On Windows, we might have been compiled with WinPcap but + * might not have it loaded; indicate whether we have it or + * not and, if we have it and we have "pcap_lib_version()", + * what version we have. + */ + GModule *handle; /* handle returned by ws_module_open */ + static gchar *packetVer; + gchar *blankp; + + if (has_wpcap) { + g_string_append_printf(str, "with "); + if (p_pcap_lib_version != NULL) + g_string_append_printf(str, p_pcap_lib_version()); + else { + /* + * An alternative method of obtaining the version + * number, by using the PacketLibraryVersion + * string from packet.dll. + * + * Unfortunately, in WinPcap 3.0, it returns + * "3.0 alpha3", even in the final version of + * WinPcap 3.0, so if there's a blank in the + * string, we strip it and everything after + * it from the string, so we don't misleadingly + * report that 3.0 alpha3 is being used when + * the final version is being used. + */ + if (packetVer == NULL) { + packetVer = "version unknown"; + handle = ws_module_open("packet.dll", 0); + if (handle != NULL) { + if (g_module_symbol(handle, + "PacketLibraryVersion", + (gpointer*)&packetVer)) { + packetVer = g_strdup(packetVer); + blankp = strchr(packetVer, ' '); + if (blankp != NULL) + *blankp = '\0'; + } else { + packetVer = "version unknown"; + } + g_module_close(handle); + } + } + g_string_append_printf(str, "WinPcap (%s)", packetVer); + } + } else + g_string_append(str, "without WinPcap"); +} + +#else /* HAVE_LIBPCAP */ + +void +load_wpcap(void) +{ + return; +} + +/* + * Append an indication that we were not compiled with WinPcap + * to a GString. + */ +void +get_compiled_pcap_version(GString *str) +{ + g_string_append(str, "without WinPcap"); +} + +/* + * Don't append anything, as we weren't even compiled to use WinPcap. + */ +void +get_runtime_pcap_version(GString *str _U_) +{ +} + +#endif /* HAVE_LIBPCAP */ diff --git a/caputils/capture-wpcap.h b/caputils/capture-wpcap.h new file mode 100644 index 0000000000..5e6b10c3ca --- /dev/null +++ b/caputils/capture-wpcap.h @@ -0,0 +1,42 @@ +/* capture-wpcap.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 2001 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 CAPTURE_WPCAP_H +#define CAPTURE_WPCAP_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern gboolean has_wpcap; + + +extern void load_wpcap(void); + +/* error message, if WinPcap couldn't be loaded */ +/* will use g_strdup, don't forget to g_free the returned string! */ +extern char *cant_load_winpcap_err(const char *app_name); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAPTURE_WPCAP_H */ diff --git a/caputils/capture_ifinfo.h b/caputils/capture_ifinfo.h new file mode 100644 index 0000000000..f70bdf4ba5 --- /dev/null +++ b/caputils/capture_ifinfo.h @@ -0,0 +1,121 @@ +/* capture_ifinfo.h + * Definitions for routines to get information about capture interfaces + * + * 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. + */ + +#ifndef __CAPTURE_IFINFO_H__ +#define __CAPTURE_IFINFO_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + IF_WIRED, + IF_AIRPCAP, + IF_PIPE, + IF_STDIN, + IF_BLUETOOTH, + IF_WIRELESS, + IF_DIALUP, + IF_USB, + IF_VIRTUAL +} interface_type; + +/* + * The list of interfaces returned by "get_interface_list()" is + * a list of these structures. + */ +typedef struct { + char *name; /* e.g. "eth0" */ + char *friendly_name; /* from OS, e.g. "Local Area Connection", or + NULL if not available */ + char *vendor_description; + /* vendor description from pcap_findalldevs(), + e.g. "Realtek PCIe GBE Family Controller", + or NULL if not available */ + GSList *addrs; /* containing address values of if_addr_t */ + interface_type type; /* type of interface */ + gboolean loopback; /* TRUE if loopback, FALSE otherwise */ +} if_info_t; + +/* + * An address in the "addrs" list. + */ +typedef enum { + IF_AT_IPv4, + IF_AT_IPv6 +} if_address_type; + +typedef struct { + if_address_type ifat_type; + union { + guint32 ip4_addr; /* 4 byte IP V4 address, or */ + guint8 ip6_addr[16];/* 16 byte IP V6 address */ + } addr; +} if_addr_t; + +/** + * Fetch the interface list from a child process. + */ +extern GList *capture_interface_list(int *err, char **err_str, void (*update_cb)(void)); + +/* Error values from "get_interface_list()/capture_interface_list()". */ +#define CANT_GET_INTERFACE_LIST 1 /* error getting list */ +#define NO_INTERFACES_FOUND 2 /* list is empty */ +#define DONT_HAVE_PCAP 3 /* couldn't load WinPcap */ + +void free_interface_list(GList *if_list); + +/* + * "get_if_capabilities()" and "capture_if_capabilities()" return a pointer + * to an allocated instance of this structure. "free_if_capabilities()" + * frees the returned instance. + */ +typedef struct { + gboolean can_set_rfmon; /* TRUE if can be put into monitor mode */ + GList *data_link_types; /* GList of data_link_info_t's */ +} if_capabilities_t; + +/* + * Information about data link types. + */ +typedef struct { + int dlt; /* e.g. DLT_EN10MB (which is 1) */ + char *name; /* e.g. "EN10MB" or "DLT 1" */ + char *description; /* descriptive name from wiretap e.g. "Ethernet", NULL if unknown */ +} data_link_info_t; + +/** + * Fetch the linktype list for the specified interface from a child process. + */ +extern if_capabilities_t * +capture_get_if_capabilities(const char *devname, gboolean monitor_mode, + char **err_str, void (*update_cb)(void)); + +void free_if_capabilities(if_capabilities_t *caps); + +void add_interface_to_remote_list(if_info_t *if_info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPTURE_IFINFO_H__ */ diff --git a/caputils/capture_win_ifnames.c b/caputils/capture_win_ifnames.c new file mode 100644 index 0000000000..b18250f57d --- /dev/null +++ b/caputils/capture_win_ifnames.c @@ -0,0 +1,369 @@ +/* capture_win_ifnames.c +* Routines supporting the use of Windows friendly interface names within Wireshark +* Copyright 2011-2012, Mike Garratt <wireshark@evn.co.nz> +* +* 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 _WIN32 + +#include <winsock2.h> +#include <windows.h> +#include <iphlpapi.h> +#include <stdio.h> +#include <stdlib.h> + +#include <wtap.h> +#include <libpcap.h> +#include <glib.h> + +#include <ntddndis.h> + +#ifndef NDIS_IF_MAX_STRING_SIZE +#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE /* =256 in <ifdef.h> */ +#endif + +#ifndef NETIO_STATUS +#define NETIO_STATUS DWORD +#endif + +#include "log.h" + +#include <capchild/capture_ifinfo.h> +#include "caputils/capture_win_ifnames.h" +#include "wsutil/file_util.h" + +static int gethexdigit(const char *p) +{ + if(*p >= '0' && *p <= '9'){ + return *p - '0'; + }else if(*p >= 'A' && *p <= 'F'){ + return *p - 'A' + 0xA; + }else if(*p >= 'a' && *p <= 'f'){ + return *p - 'a' + 0xa; + }else{ + return -1; /* Not a hex digit */ + } +} + +static gboolean get8hexdigits(const char *p, DWORD *d) +{ + int digit; + DWORD val; + int i; + + val = 0; + for(i = 0; i < 8; i++){ + digit = gethexdigit(p++); + if(digit == -1){ + return FALSE; /* Not a hex digit */ + } + val = (val << 4) | digit; + } + *d = val; + return TRUE; +} + +static gboolean get4hexdigits(const char *p, WORD *w) +{ + int digit; + WORD val; + int i; + + val = 0; + for(i = 0; i < 4; i++){ + digit = gethexdigit(p++); + if(digit == -1){ + return FALSE; /* Not a hex digit */ + } + val = (val << 4) | digit; + } + *w = val; + return TRUE; +} + +/* + * If a string is a GUID in {}, fill in a GUID structure with the GUID + * value and return TRUE; otherwise, if the string is not a valid GUID + * in {}, return FALSE. + */ +gboolean +parse_as_guid(const char *guid_text, GUID *guid) +{ + int i; + int digit1, digit2; + + if(*guid_text != '{'){ + return FALSE; /* Nope, not enclosed in {} */ + } + guid_text++; + /* There must be 8 hex digits; if so, they go into guid->Data1 */ + if(!get8hexdigits(guid_text, &guid->Data1)){ + return FALSE; /* nope, not 8 hex digits */ + } + guid_text += 8; + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* There must be 4 hex digits; if so, they go into guid->Data2 */ + if(!get4hexdigits(guid_text, &guid->Data2)){ + return FALSE; /* nope, not 4 hex digits */ + } + guid_text += 4; + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* There must be 4 hex digits; if so, they go into guid->Data3 */ + if(!get4hexdigits(guid_text, &guid->Data3)){ + return FALSE; /* nope, not 4 hex digits */ + } + guid_text += 4; + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* + * There must be 4 hex digits; if so, they go into the first 2 bytes + * of guid->Data4. + */ + for(i = 0; i < 2; i++){ + digit1 = gethexdigit(guid_text); + if(digit1 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + digit2 = gethexdigit(guid_text); + if(digit2 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + guid->Data4[i] = (digit1 << 4)|(digit2); + } + /* Now there must be a hyphen */ + if(*guid_text != '-'){ + return FALSE; /* Nope */ + } + guid_text++; + /* + * There must be 12 hex digits; if so,t hey go into the next 6 bytes + * of guid->Data4. + */ + for(i = 0; i < 6; i++){ + digit1 = gethexdigit(guid_text); + if(digit1 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + digit2 = gethexdigit(guid_text); + if(digit2 == -1){ + return FALSE; /* Not a hex digit */ + } + guid_text++; + guid->Data4[i+2] = (digit1 << 4)|(digit2); + } + /* Now there must be a closing } */ + if(*guid_text != '}'){ + return FALSE; /* Nope */ + } + guid_text++; + /* And that must be the end of the string */ + if(*guid_text != '\0'){ + return FALSE; /* Nope */ + } + return TRUE; +} + +/**********************************************************************************/ +gboolean IsWindowsVistaOrLater() +{ +#if (_MSC_VER >= 1800) + /* + * On VS2103, GetVersionEx is deprecated. Microsoft recommend to + * use VerifyVersionInfo instead + */ + OSVERSIONINFOEX osvi; + DWORDLONG dwlConditionMask = 0; + int op = VER_GREATER_EQUAL; + + SecureZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + osvi.dwMajorVersion = 6; + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op); + return VerifyVersionInfo(&osvi, VER_MAJORVERSION, dwlConditionMask); +#else + OSVERSIONINFO osvi; + + SecureZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if(GetVersionEx(&osvi)){ + return osvi.dwMajorVersion >= 6; + } + return FALSE; +#endif +} + +/**********************************************************************************/ +/* Get the friendly name for the given GUID */ +char * +get_interface_friendly_name_from_device_guid(__in GUID *guid) +{ + HMODULE hIPHlpApi; + HRESULT status; + WCHAR wName[NDIS_IF_MAX_STRING_SIZE + 1]; + HRESULT hr; + gboolean fallbackToUnpublishedApi=TRUE; + gboolean haveInterfaceFriendlyName=FALSE; + int size; + char *name; + + /* Load the ip helper api DLL */ + hIPHlpApi = LoadLibrary(TEXT("iphlpapi.dll")); + if (hIPHlpApi == NULL) { + /* Load failed - DLL should always be available in XP+*/ + return NULL; + } + + /* Need to convert an Interface GUID to the interface friendly name (e.g. "Local Area Connection") + * The functions required to do this all reside within iphlpapi.dll + * - The preferred approach is to use published API functions (Available since Windows Vista) + * - We do however fallback to trying undocumented API if the published API is not available (Windows XP/2k3 scenario) + */ + + if(IsWindowsVistaOrLater()){ + /* Published API function prototypes (for Windows Vista/Windows Server 2008+) */ + typedef NETIO_STATUS (WINAPI *ProcAddr_CIG2L) (__in CONST GUID *InterfaceGuid, __out PNET_LUID InterfaceLuid); + typedef NETIO_STATUS (WINAPI *ProcAddr_CIL2A) ( __in CONST NET_LUID *InterfaceLuid,__out_ecount(Length) PWSTR InterfaceAlias, __in SIZE_T Length); + + /* Attempt to do the conversion using Published API functions */ + ProcAddr_CIG2L proc_ConvertInterfaceGuidToLuid=(ProcAddr_CIG2L) GetProcAddress(hIPHlpApi, "ConvertInterfaceGuidToLuid"); + if(proc_ConvertInterfaceGuidToLuid!=NULL){ + ProcAddr_CIL2A Proc_ConvertInterfaceLuidToAlias=(ProcAddr_CIL2A) GetProcAddress(hIPHlpApi, "ConvertInterfaceLuidToAlias"); + if(Proc_ConvertInterfaceLuidToAlias!=NULL){ + /* we have our functions ready to go, attempt to convert interface guid->luid->friendlyname */ + NET_LUID InterfaceLuid; + hr = proc_ConvertInterfaceGuidToLuid(guid, &InterfaceLuid); + if(hr==NO_ERROR){ + /* guid->luid success */ + hr = Proc_ConvertInterfaceLuidToAlias(&InterfaceLuid, wName, NDIS_IF_MAX_STRING_SIZE+1); + + if(hr==NO_ERROR){ + /* luid->friendly name success */ + haveInterfaceFriendlyName=TRUE; /* success */ + }else{ + /* luid->friendly name failed */ + fallbackToUnpublishedApi=FALSE; + } + }else{ + fallbackToUnpublishedApi=FALSE; + } + + } + } + } + + + if(fallbackToUnpublishedApi && !haveInterfaceFriendlyName){ + /* Didn't manage to get the friendly name using published api functions + * (most likely cause wireshark is running on Windows XP/Server 2003) + * Retry using nhGetInterfaceNameFromGuid (an older unpublished API function) */ + typedef HRESULT (WINAPI *ProcAddr_nhGINFG) (__in GUID *InterfaceGuid, __out PCWSTR InterfaceAlias, __inout DWORD *LengthAddress, wchar_t *a4, wchar_t *a5); + + ProcAddr_nhGINFG Proc_nhGetInterfaceNameFromGuid = NULL; + Proc_nhGetInterfaceNameFromGuid = (ProcAddr_nhGINFG) GetProcAddress(hIPHlpApi, "NhGetInterfaceNameFromGuid"); + if (Proc_nhGetInterfaceNameFromGuid!= NULL) { + wchar_t *p4=NULL, *p5=NULL; + DWORD NameSize; + + /* testing of nhGetInterfaceNameFromGuid indicates the unpublished API function expects the 3rd parameter + * to be the available space in bytes (as compared to wchar's) available in the second parameter buffer + * to receive the friendly name (in unicode format) including the space for the nul termination.*/ + NameSize = sizeof(wName); + + /* do the guid->friendlyname lookup */ + status = Proc_nhGetInterfaceNameFromGuid(guid, wName, &NameSize, p4, p5); + + if(status==0){ + haveInterfaceFriendlyName=TRUE; /* success */ + } + } + } + + /* we have finished with iphlpapi.dll - release it */ + FreeLibrary(hIPHlpApi); + + if(!haveInterfaceFriendlyName){ + /* failed to get the friendly name, nothing further to do */ + return NULL; + } + + /* Get the required buffer size, and then convert the string + * from UTF-16 to UTF-8. */ + size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, NULL, 0, NULL, NULL); + name=(char *) g_malloc(size); + if (name == NULL){ + return NULL; + } + size=WideCharToMultiByte(CP_UTF8, 0, wName, -1, name, size, NULL, NULL); + if(size==0){ + /* bytes written == 0, indicating some form of error*/ + g_free(name); + return NULL; + } + return name; +} + +/* + * Given an interface name, try to extract the GUID from it and parse it. + * If that fails, return NULL; if that succeeds, attempt to get the + * friendly name for the interface in question. If that fails, return + * NULL, otherwise return the friendly name, allocated with g_malloc() + * (so that it must be freed with g_free()). + */ +char * +get_windows_interface_friendly_name(const char *interface_devicename) +{ + const char* guid_text; + GUID guid; + + /* Extract the guid text from the interface device name */ + if(strncmp("\\Device\\NPF_", interface_devicename, 12)==0){ + guid_text=interface_devicename+12; /* skip over the '\Device\NPF_' prefix, assume the rest is the guid text */ + }else{ + guid_text=interface_devicename; + } + + if (!parse_as_guid(guid_text, &guid)){ + return NULL; /* not a GUID, so no friendly name */ + } + + /* guid okay, get the interface friendly name associated with the guid */ + return get_interface_friendly_name_from_device_guid(&guid); +} + +/**************************************************************************************/ +#endif + diff --git a/caputils/capture_win_ifnames.h b/caputils/capture_win_ifnames.h new file mode 100644 index 0000000000..528c73de14 --- /dev/null +++ b/caputils/capture_win_ifnames.h @@ -0,0 +1,46 @@ +/* capture_win_ifnames.h + * Routines supporting the use of Windows friendly interface names within Wireshark + * Copyright 2011-2012, Mike Garratt <wireshark@evn.co.nz> + * + * 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. + */ + +#ifndef CAPTURE_WIN_IFNAMES_H +#define CAPTURE_WIN_IFNAMES_H + +/* + * If a string is a GUID in {}, fill in a GUID structure with the GUID + * value and return TRUE; otherwise, if the string is not a valid GUID + * in {}, return FALSE. + */ +extern gboolean parse_as_guid(const char *guid_text, GUID *guid); + +/* Get the friendly name for the given GUID */ +extern char *get_interface_friendly_name_from_device_guid(GUID *guid); + +/* + * Given an interface name, try to extract the GUID from it and parse it. + * If that fails, return NULL; if that succeeds, attempt to get the + * friendly name for the interface in question. If that fails, return + * NULL, otherwise return the friendly name, allocated with g_malloc() + * (so that it must be freed with g_free()). + */ +extern char *get_windows_interface_friendly_name(const char *interface_devicename); + +#endif diff --git a/caputils/capture_wpcap_packet.c b/caputils/capture_wpcap_packet.c new file mode 100644 index 0000000000..5b3ffc479d --- /dev/null +++ b/caputils/capture_wpcap_packet.c @@ -0,0 +1,331 @@ +/* capture_wpcap_packet.c + * WinPcap-specific interfaces for low-level information (packet.dll). + * We load WinPcap at run + * time, so that we only need one Wireshark binary and one TShark binary + * for Windows, regardless of whether WinPcap is installed or not. + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 2001 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" + +#if defined HAVE_LIBPCAP && defined _WIN32 + +#include <glib.h> +#include <gmodule.h> + +#include <pcap.h> + +/* XXX - yes, I know, I should move cppmagic.h to a generic location. */ +#include "tools/lemon/cppmagic.h" + +#include <epan/value_string.h> + +#include <winsock2.h> /* Needed here to force a definition of WINVER */ + /* for some (all ?) Microsoft compilers newer than vc6. */ + /* (If windows.h were used instead, there might be */ + /* issues re winsock.h included before winsock2.h ) */ +#include <windowsx.h> +#include <Ntddndis.h> + +#include "caputils/capture_wpcap_packet.h" +#include <wsutil/file_util.h> + +/* packet32.h requires sockaddr_storage + * whether sockaddr_storage is defined or not depends on the Platform SDK + * version installed. The only one not defining it is the SDK that comes + * with MSVC 6.0 (WINVER 0x0400). + * + * copied from RFC2553 (and slightly modified because of datatypes) ... + * XXX - defined more than once, move this to a header file */ +#ifndef WINVER +#error WINVER not defined .... +#endif +#if (WINVER <= 0x0400) && defined(_MSC_VER) +typedef unsigned short eth_sa_family_t; + +/* + * Desired design of maximum size and alignment + */ +#define ETH_SS_MAXSIZE 128 /* Implementation specific max size */ +#define ETH_SS_ALIGNSIZE (sizeof (gint64 /*int64_t*/)) + /* Implementation specific desired alignment */ +/* + * Definitions used for sockaddr_storage structure paddings design. + */ +#define ETH_SS_PAD1SIZE (ETH_SS_ALIGNSIZE - sizeof (eth_sa_family_t)) +#define ETH_SS_PAD2SIZE (ETH_SS_MAXSIZE - (sizeof (eth_sa_family_t) + \ + ETH_SS_PAD1SIZE + ETH_SS_ALIGNSIZE)) + +struct sockaddr_storage { + eth_sa_family_t __ss_family; /* address family */ + /* Following fields are implementation specific */ + char __ss_pad1[ETH_SS_PAD1SIZE]; + /* 6 byte pad, this is to make implementation */ + /* specific pad up to alignment field that */ + /* follows explicit in the data structure */ + gint64 /*int64_t*/ __ss_align; /* field to force desired structure */ + /* storage alignment */ + char __ss_pad2[ETH_SS_PAD2SIZE]; + /* 112 byte pad to achieve desired size, */ + /* _SS_MAXSIZE value minus size of ss_family */ + /* __ss_pad1, __ss_align fields is 112 */ +}; +/* ... copied from RFC2553 */ +#endif /* WINVER */ + +#include <Packet32.h> + +gboolean has_wpacket = FALSE; + + +/* This module will use the PacketRequest function in packet.dll (coming with WinPcap) to "directly" access + * the Win32 NDIS network driver(s) and ask for various values (status, statistics, ...). + * + * Unfortunately, the definitions required for this are not available through the usual windows header files, + * but require the Windows "Device Driver Kit" which is not available for free :-( + * + * Fortunately, the definitions needed to access the various NDIS values are available from various OSS projects: + * - WinPcap in Ntddndis.h + * - Ndiswrapper in driver/ndis.h and driver/iw_ndis.h + * - cygwin (MingW?) in usr/include/w32api/ddk/ndis.h and ntddndis.h + * - FreeBSD (netperf) + */ + +/* The MSDN description of the NDIS driver API is available at: +/* MSDN Home > MSDN Library > Win32 and COM Development > Driver Development Kit > Network Devices and Protocols > Reference */ +/* "NDIS Objects" */ +/* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/network/hh/network/21oidovw_d55042e5-0b8a-4439-8ef2-be7331e98464.xml.asp */ + +/* Some more interesting links: + * http://sourceforge.net/projects/ndiswrapper/ + * http://www.osronline.com/lists_archive/windbg/thread521.html + * http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ndis.h?view=markup + * http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntddndis.h?view=markup + */ + + + +/******************************************************************************************************************************/ +/* stuff to load WinPcap's packet.dll and the functions required from it */ + +static PCHAR (*p_PacketGetVersion) (void); +static LPADAPTER (*p_PacketOpenAdapter) (char *adaptername); +static void (*p_PacketCloseAdapter) (LPADAPTER); +static int (*p_PacketRequest) (LPADAPTER, int, void *); + +typedef struct { + const char *name; + gpointer *ptr; + gboolean optional; +} symbol_table_t; + +#define SYM(x, y) { G_STRINGIFY(x) , (gpointer) &CONCAT(p_,x), y } + +void +wpcap_packet_load(void) +{ + + /* These are the symbols I need or want from packet.dll */ + static const symbol_table_t symbols[] = { + SYM(PacketGetVersion, FALSE), + SYM(PacketOpenAdapter, FALSE), + SYM(PacketCloseAdapter, FALSE), + SYM(PacketRequest, FALSE), + { NULL, NULL, FALSE } + }; + + GModule *wh; /* wpcap handle */ + const symbol_table_t *sym; + + wh = ws_module_open("packet.dll", 0); + + if (!wh) { + return; + } + + sym = symbols; + while (sym->name) { + if (!g_module_symbol(wh, sym->name, sym->ptr)) { + if (sym->optional) { + /* + * We don't care if it's missing; we just + * don't use it. + */ + *sym->ptr = NULL; + } else { + /* + * We require this symbol. + */ + return; + } + } + sym++; + } + + has_wpacket = TRUE; +} + + + +/******************************************************************************************************************************/ +/* functions to access the NDIS driver values */ + + +/* get dll version */ +char * +wpcap_packet_get_version(void) +{ + if(!has_wpacket) { + return NULL; + } + return p_PacketGetVersion(); +} + + +/* open the interface */ +void * +wpcap_packet_open(char *if_name) +{ + LPADAPTER adapter; + + g_assert(has_wpacket); + adapter = p_PacketOpenAdapter(if_name); + + return adapter; +} + + +/* close the interface */ +void +wpcap_packet_close(void *adapter) +{ + + g_assert(has_wpacket); + p_PacketCloseAdapter(adapter); +} + + +/* do a packet request call */ +int +wpcap_packet_request(void *adapter, ULONG Oid, int set, char *value, unsigned int *length) +{ + BOOLEAN Status; + ULONG IoCtlBufferLength=(sizeof(PACKET_OID_DATA) + (*length) - 1); + PPACKET_OID_DATA OidData; + + + g_assert(has_wpacket); + + if(p_PacketRequest == NULL) { + g_warning("packet_request not available\n"); + return 0; + } + + /* get a buffer suitable for PacketRequest() */ + OidData=GlobalAllocPtr(GMEM_MOVEABLE | GMEM_ZEROINIT,IoCtlBufferLength); + if (OidData == NULL) { + g_warning("GlobalAllocPtr failed for %u\n", IoCtlBufferLength); + return 0; + } + + OidData->Oid = Oid; + OidData->Length = *length; + memcpy(OidData->Data, value, *length); + + Status = p_PacketRequest(adapter, set, OidData); + + if(Status) { + if(OidData->Length <= *length) { + /* copy value from driver */ + memcpy(value, OidData->Data, OidData->Length); + *length = OidData->Length; + } else { + /* the driver returned a value that is longer than expected (and longer than the given buffer) */ + g_warning("returned oid too long, Oid: 0x%x OidLen:%u MaxLen:%u", Oid, OidData->Length, *length); + Status = FALSE; + } + } + + GlobalFreePtr (OidData); + + if(Status) { + return 1; + } else { + return 0; + } +} + + +/* get an UINT value using the packet request call */ +int +wpcap_packet_request_uint(void *adapter, ULONG Oid, UINT *value) +{ + BOOLEAN Status; + int length = sizeof(UINT); + + + Status = wpcap_packet_request(adapter, Oid, FALSE /* !set */, (char *) value, &length); + if(Status && length == sizeof(UINT)) { + return 1; + } else { + return 0; + } +} + + +/* get an ULONG value using the NDIS packet request call */ +int +wpcap_packet_request_ulong(void *adapter, ULONG Oid, ULONG *value) +{ + BOOLEAN Status; + int length = sizeof(ULONG); + + + Status = wpcap_packet_request(adapter, Oid, FALSE /* !set */, (char *) value, &length); + if(Status && length == sizeof(ULONG)) { + return 1; + } else { + return 0; + } +} + + +#else /* HAVE_LIBPCAP && _WIN32 */ + +void +wpcap_packet_load(void) +{ + return; +} + +#endif /* HAVE_LIBPCAP */ + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/caputils/capture_wpcap_packet.h b/caputils/capture_wpcap_packet.h new file mode 100644 index 0000000000..f043d89e92 --- /dev/null +++ b/caputils/capture_wpcap_packet.h @@ -0,0 +1,51 @@ +/* capture_wpcap_packet.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 2001 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 CAPTURE_WPCAP_PACKET_H +#define CAPTURE_WPCAP_PACKET_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern void wpcap_packet_load(void); + +/* get the packet.dll version info */ +extern char *wpcap_packet_get_version(void); + +/* open the interface */ +extern void * wpcap_packet_open(char *if_name); + +/* close the interface */ +extern void wpcap_packet_close(void * adapter); + +extern int wpcap_packet_request(void *a, ULONG Oid, int set, char *value, unsigned int *length); + +extern int wpcap_packet_request_uint(void *a, ULONG Oid, UINT *value); + +extern int wpcap_packet_request_ulong(void *a, ULONG Oid, ULONG *value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAPTURE_WPCAP_PACKET_H */ diff --git a/caputils/doxygen.cfg.in b/caputils/doxygen.cfg.in new file mode 100644 index 0000000000..cae55f4037 --- /dev/null +++ b/caputils/doxygen.cfg.in @@ -0,0 +1,81 @@ +# @configure_input@ + +@INCLUDE = ../doxygen_global.cfg + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "Wireshark Capture Utilities Library" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../wsar_html + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = caputils + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = ../doxygen-core.tag=.. + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = doxygen-caputils.tag + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = wireshark-caputils.chm diff --git a/caputils/ws80211_utils.c b/caputils/ws80211_utils.c new file mode 100644 index 0000000000..21f8a8a673 --- /dev/null +++ b/caputils/ws80211_utils.c @@ -0,0 +1,748 @@ +/* + * ws80211 utilities + * Copyright 2012, Pontus Fuchs <pontus.fuchs@gmail.com> + +Parts of this file was copied from iw: + +Copyright (c) 2007, 2008 Johannes Berg +Copyright (c) 2007 Andy Lutomirski +Copyright (c) 2007 Mike Kershaw +Copyright (c) 2008-2009 Luis R. Rodriguez + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "config.h" + +#include <stdio.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "ws80211_utils.h" + +#if defined(HAVE_LIBNL) && defined(HAVE_NL80211) +#include <string.h> +#include <errno.h> +#include <unistd.h> + +#include <net/if.h> +#include <sys/ioctl.h> + +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/msg.h> +#include <netlink/attr.h> + +#include <linux/nl80211.h> + +/* libnl 1.x compatibility code */ +#ifdef HAVE_LIBNL1 +#define nl_sock nl_handle +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 */ + +struct nl80211_state { + struct nl_sock *nl_sock; + int nl80211_id; +}; + +static struct nl80211_state nl_state; + +int ws80211_init(void) +{ + int err; + + struct nl80211_state *state = &nl_state; + + state->nl_sock = nl_socket_alloc(); + if (!state->nl_sock) { + fprintf(stderr, "Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + if (genl_connect(state->nl_sock)) { + fprintf(stderr, "Failed to connect to generic netlink.\n"); + err = -ENOLINK; + goto out_handle_destroy; + } + + state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); + if (state->nl80211_id < 0) { + fprintf(stderr, "nl80211 not found.\n"); + err = -ENOENT; + goto out_handle_destroy; + } + + return 0; + + out_handle_destroy: + nl_socket_free(state->nl_sock); + state->nl_sock = 0; + return err; +} + +static int error_handler(struct sockaddr_nl *nla _U_, struct nlmsgerr *err, + void *arg) +{ + int *ret = (int *)arg; + *ret = err->error; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int *)arg; + *ret = 0; + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg _U_, void *arg) +{ + int *ret = (int *)arg; + *ret = 0; + return NL_STOP; +} + +static int nl80211_do_cmd(struct nl_msg *msg, struct nl_cb *cb) +{ + volatile int err; + + if (!nl_state.nl_sock) + return -ENOLINK; + + err = nl_send_auto_complete(nl_state.nl_sock, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, (void *)&err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, (void *)&err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, (void *)&err); + + while (err > 0) + nl_recvmsgs(nl_state.nl_sock, cb); + out: + nl_cb_put(cb); + + return err; +} + +struct nliface_cookie +{ + char *ifname; + GArray *interfaces; +}; + +/* + * And now for a steaming heap of suck. + * + * The nla_for_each_nested() macro defined by at least some versions of the + * Linux kernel's headers doesn't do the casting required when compiling + * with a C++ compiler or with -Wc++-compat, so we get warnings, and those + * warnings are fatal when we compile this file. + * + * So we replace it with our own version, which does the requisite cast. + */ + +/** + * nla_for_each_nested - iterate over nested attributes + * @pos: loop counter, set to current attribute + * @nla: attribute containing the nested attributes + * @rem: initialized to len, holds bytes currently remaining in stream + */ +#undef nla_for_each_nested +#define nla_for_each_nested(pos, nla, rem) \ + nla_for_each_attr(pos, (struct nlattr *)nla_data(nla), nla_len(nla), rem) + +static int get_phys_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + + struct nliface_cookie *cookie = (struct nliface_cookie *)arg; + + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + {NLA_UNSPEC, 0, 0}, /* __NL80211_FREQUENCY_ATTR_INVALID */ + {NLA_U32, 0, 0}, /* NL80211_FREQUENCY_ATTR_FREQ */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_DISABLED */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_PASSIVE_SCAN */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_NO_IBSS */ + {NLA_FLAG, 0, 0}, /* NL80211_FREQUENCY_ATTR_RADAR */ + {NLA_U32, 0, 0} /* NL80211_FREQUENCY_ATTR_MAX_TX_POWER */ + }; + + struct nlattr *nl_band; + struct nlattr *nl_freq; + struct nlattr *nl_mode; + int bandidx = 1; + int rem_band, rem_freq, rem_mode; + struct ws80211_interface *iface; + int cap_monitor = 0; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + if (tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]) { + nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode) { + if (nla_type(nl_mode) == NL80211_IFTYPE_MONITOR) + cap_monitor = 1; + } + } + if (!cap_monitor) + return NL_SKIP; + + iface = (struct ws80211_interface *)g_malloc0(sizeof(*iface)); + if (!iface) + return NL_SKIP; + + iface->frequencies = g_array_new(FALSE, FALSE, sizeof(int)); + iface->channel_types = 1 << WS80211_CHAN_NO_HT; + + if (tb_msg[NL80211_ATTR_WIPHY_NAME]) { + iface->ifname = g_strdup_printf("%s.mon", + nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); + } + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { + bandidx++; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, + (struct nlattr *)nla_data(nl_band), + nla_len(nl_band), NULL); + +#ifdef NL80211_BAND_ATTR_HT_CAPA + if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { + gboolean ht40; + iface->channel_types |= 1 << WS80211_CHAN_HT20; + ht40 = !!(nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]) & 0x02); + if (ht40) { + iface->channel_types |= 1 << WS80211_CHAN_HT40MINUS; + iface->channel_types |= 1 << WS80211_CHAN_HT40PLUS; + } + } +#endif /* NL80211_BAND_ATTR_HT_CAPA */ + + nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { + uint32_t freq; + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + (struct nlattr *)nla_data(nl_freq), + nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + continue; + + freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + g_array_append_val(iface->frequencies, freq); + } + } + + /* Can frequency be set? Only newer versions of cfg80211 supports this */ +#ifdef HAVE_NL80211_CMD_SET_CHANNEL + if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) { + int cmd; + struct nlattr *nl_cmd; + nla_for_each_nested(nl_cmd, tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS], cmd) { + if(nla_get_u32(nl_cmd) == NL80211_CMD_SET_CHANNEL) + iface->can_set_freq = TRUE; + } + } +#else + iface->can_set_freq = TRUE; +#endif + g_array_append_val(cookie->interfaces, iface); + + return NL_SKIP; +} + + +static int ws80211_get_phys(GArray *interfaces) +{ + struct nliface_cookie cookie; + struct nl_msg *msg; + struct nl_cb *cb; + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + cookie.interfaces = interfaces; + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + NLM_F_DUMP, NL80211_CMD_GET_WIPHY, 0); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_phys_handler, &cookie); + + return nl80211_do_cmd(msg, cb); + +} + +static int get_freq_wext(const char *ifname) +{ + int fd; + int ret = -1; + /* Ugly hack to avoid including wireless.h */ + struct { + char name1[IFNAMSIZ]; + __s32 m; + __s16 e; + __u8 i; + __u8 flags; + } wrq; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) + return -1; + + g_strlcpy(wrq.name1, ifname, IFNAMSIZ); + /* SIOCGIWFREQ */ + if (ioctl(fd, 0x8B05, &wrq) == 0) { + if (wrq.e == 6) + ret = wrq.m; + } + close(fd); + return ret; +} + +struct __iface_info +{ + struct ws80211_iface_info *pub; + int type; + int phyidx; +}; + +static int get_iface_info_handler(struct nl_msg *msg, void *arg) +{ + struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct __iface_info *iface_info = (struct __iface_info *)arg; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_IFTYPE]) { + iface_info->type = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]); + } + if (tb_msg[NL80211_ATTR_WIPHY]) { + iface_info->phyidx = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]); + } + + if (tb_msg[NL80211_ATTR_WIPHY_FREQ]) { + iface_info->pub->current_freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]); + iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT; + + if (tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + switch (nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + + case NL80211_CHAN_NO_HT: + iface_info->pub->current_chan_type = WS80211_CHAN_NO_HT; + break; + + case NL80211_CHAN_HT20: + iface_info->pub->current_chan_type = WS80211_CHAN_HT20; + break; + + case NL80211_CHAN_HT40MINUS: + iface_info->pub->current_chan_type = WS80211_CHAN_HT40MINUS; + break; + + case NL80211_CHAN_HT40PLUS: + iface_info->pub->current_chan_type = WS80211_CHAN_HT40PLUS; + break; + } + } + + } + return NL_SKIP; +} + + +static int __ws80211_get_iface_info(const char *name, struct __iface_info *iface_info) +{ + int devidx; + struct nl_msg *msg; + struct nl_cb *cb; + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + devidx = if_nametoindex(name); + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_GET_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_iface_info_handler, iface_info); + + if (nl80211_do_cmd(msg, cb)) + return -1; + + /* Old kernels cant get the current freq via netlink. Try WEXT too :( */ + if (iface_info->pub->current_freq == -1) + iface_info->pub->current_freq = get_freq_wext(name); + return 0; + +nla_put_failure: + fprintf(stderr, "building message failed\n"); + return -1; +} + +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info) +{ + struct __iface_info __iface_info; + + memset(iface_info, 0, sizeof(*iface_info)); + __iface_info.pub = iface_info; + __iface_info.type = -1; + __iface_info.phyidx= -1; + __iface_info.pub->current_freq = -1; + __iface_info.pub->current_chan_type = WS80211_CHAN_NO_HT; + + return __ws80211_get_iface_info(name, &__iface_info); +} + +static int ws80211_populate_devices(GArray *interfaces) +{ + FILE *fh; + char line[200]; + char *t; + gchar *t2; + char *ret; + int i; + unsigned int j; + + struct ws80211_iface_info pub = {-1, WS80211_CHAN_NO_HT}; + struct __iface_info iface_info; + struct ws80211_interface *iface; + + /* Get a list of phy's that can handle monitor mode */ + ws80211_get_phys(interfaces); + + fh = g_fopen("/proc/net/dev", "r"); + if(!fh) { + fprintf(stderr, "Cannot open /proc/net/dev"); + return -ENOENT; + } + + /* Skip the first two lines */ + for (i = 0; i < 2; i++) { + ret = fgets(line, sizeof(line), fh); + if (ret == NULL) { + fprintf(stderr, "Error parsing /proc/net/dev"); + fclose(fh); + return -1; + } + } + + /* Update names of user created monitor interfaces */ + while(fgets(line, sizeof(line), fh)) { + t = index(line, ':'); + if (!t) + continue; + *t = 0; + t = line; + while (*t && *t == ' ') + t++; + memset(&iface_info, 0, sizeof(iface_info)); + iface_info.pub = &pub; + __ws80211_get_iface_info(t, &iface_info); + + if (iface_info.type == NL80211_IFTYPE_MONITOR) { + for (j = 0; j < interfaces->len; j++) { + iface = g_array_index(interfaces, struct ws80211_interface *, j); + t2 = g_strdup_printf("phy%d.mon", iface_info.phyidx); + if (t2) { + if (!strcmp(t2, iface->ifname)) { + g_free(iface->ifname); + iface->ifname = g_strdup(t); + } + g_free(t2); + } + } + } + } + fclose(fh); + return 0; +} + +static int ws80211_iface_up(const char *ifname) +{ + int sock; + struct ifreq ifreq; + + sock = socket(AF_PACKET, SOCK_RAW, 0); + if (sock == -1) + return -1; + + g_strlcpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name)); + + if (ioctl(sock, SIOCGIFFLAGS, &ifreq)) + goto out_err; + + ifreq.ifr_flags |= IFF_UP; + + if (ioctl(sock, SIOCSIFFLAGS, &ifreq)) + goto out_err; + + close(sock); + return 0; + +out_err: + close(sock); + return -1; +} + +static int ws80211_create_on_demand_interface(const char *name) +{ + int devidx, phyidx, err; + struct nl_msg *msg; + struct nl_cb *cb; + + devidx = if_nametoindex(name); + if (devidx) + return ws80211_iface_up(name); + + if (sscanf(name, "phy%d.mon", &phyidx) != 1) + return -EINVAL; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_NEW_INTERFACE, 0); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phyidx); + + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MONITOR); + + err = nl80211_do_cmd(msg, cb); + if (err) + return err; + return ws80211_iface_up(name); + +nla_put_failure: + fprintf(stderr, "building message failed\n"); + return 2; +} + +int ws80211_set_freq(const char *name, int freq, int chan_type) +{ + int devidx, err; + struct nl_msg *msg; + struct nl_cb *cb; + + err = ws80211_create_on_demand_interface(name); + if (err) + return err; + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(NL_CB_DEFAULT); + + devidx = if_nametoindex(name); + +#ifdef HAVE_NL80211_CMD_SET_CHANNEL + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_SET_CHANNEL, 0); +#else + genlmsg_put(msg, 0, 0, nl_state.nl80211_id, 0, + 0, NL80211_CMD_SET_WIPHY, 0); +#endif + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + + switch (chan_type) { + +#ifdef NL80211_BAND_ATTR_HT_CAPA + case WS80211_CHAN_NO_HT: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_NO_HT); + break; + + case WS80211_CHAN_HT20: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20); + break; + + case WS80211_CHAN_HT40MINUS: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40MINUS); + break; + + case WS80211_CHAN_HT40PLUS: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT40PLUS); + break; +#endif + + default: + break; + } + err = nl80211_do_cmd(msg, cb); + return err; + +nla_put_failure: + fprintf(stderr, "building message failed\n"); + return 2; + +} + +void ws80211_free_interfaces(GArray *interfaces) +{ + struct ws80211_interface *iface; + + if (!interfaces) + return; + + while (interfaces->len) { + iface = g_array_index(interfaces, struct ws80211_interface *, 0); + g_array_remove_index(interfaces, 0); + g_array_free(iface->frequencies, TRUE); + g_free(iface->ifname); + g_free(iface); + } + g_array_free(interfaces, TRUE); +} + +GArray* ws80211_find_interfaces(void) +{ + GArray *interfaces; + + if (!nl_state.nl_sock) + return NULL; + + interfaces = g_array_new(FALSE, FALSE, sizeof(struct ws80211_interface *)); + if (!interfaces) + return NULL; + + if (ws80211_populate_devices(interfaces)) { + ws80211_free_interfaces(interfaces); + return NULL; + } + return interfaces; +} + +int ws80211_frequency_to_channel(int freq) +{ + if (freq == 2484) + return 14; + + if (freq < 2484) + return (freq - 2407) / 5; + + return freq / 5 - 1000; +} + +int +ws80211_str_to_chan_type(const gchar *s) +{ + int ret = -1; + if (!s) + return -1; + + if (!strcmp(s, CHAN_NO_HT)) + ret = WS80211_CHAN_NO_HT; + if (!strcmp(s, CHAN_HT20)) + ret = WS80211_CHAN_HT20; + if (!strcmp(s, CHAN_HT40MINUS)) + ret = WS80211_CHAN_HT40MINUS; + if (!strcmp(s, CHAN_HT40PLUS)) + ret = WS80211_CHAN_HT40PLUS; + return ret; +} + +const gchar +*ws80211_chan_type_to_str(int type) +{ + switch (type) { + case WS80211_CHAN_NO_HT: + return CHAN_NO_HT; + case WS80211_CHAN_HT20: + return CHAN_HT20; + case WS80211_CHAN_HT40MINUS: + return CHAN_HT40MINUS; + case WS80211_CHAN_HT40PLUS: + return CHAN_HT40PLUS; + } + return NULL; +} + +#else /* HAVE_LIBNL */ +int ws80211_init(void) +{ + return -1; +} + +GArray* ws80211_find_interfaces(void) +{ + return NULL; +} + +int ws80211_get_iface_info(const char *name _U_, struct ws80211_iface_info *iface_info _U_) +{ + return -1; +} + +void ws80211_free_interfaces(GArray *interfaces _U_) +{ +} + +int ws80211_frequency_to_channel(int freq _U_) +{ + return -1; +} + +int ws80211_set_freq(const char *name _U_, int freq _U_, int chan_type _U_) +{ + return -1; +} + +int ws80211_str_to_chan_type(const gchar *s _U_) +{ + return -1; +} + +const gchar *ws80211_chan_type_to_str(int type _U_) +{ + return NULL; +} +#endif /* HAVE_LIBNL && HAVE_NL80211 */ diff --git a/caputils/ws80211_utils.h b/caputils/ws80211_utils.h new file mode 100644 index 0000000000..2a6e04f629 --- /dev/null +++ b/caputils/ws80211_utils.h @@ -0,0 +1,61 @@ +/* + * Copyright 2012, 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. + */ + +#ifndef __WS80211_UTILS_H__ +#define __WS80211_UTILS_H__ + +enum ws80211_channel_type { + WS80211_CHAN_NO_HT, + WS80211_CHAN_HT20, + WS80211_CHAN_HT40MINUS, + WS80211_CHAN_HT40PLUS +}; + +#define CHAN_NO_HT "NOHT" +#define CHAN_HT20 "HT20" +#define CHAN_HT40MINUS "HT40-" +#define CHAN_HT40PLUS "HT40+" + +struct ws80211_interface +{ + char *ifname; + gboolean can_set_freq; + GArray *frequencies; + int channel_types; /* Union for all bands */ +}; + +struct ws80211_iface_info { + int current_freq; + enum ws80211_channel_type current_chan_type; +}; + + +int ws80211_init(void); +GArray* ws80211_find_interfaces(void); +int ws80211_get_iface_info(const char *name, struct ws80211_iface_info *iface_info); +void ws80211_free_interfaces(GArray *interfaces); +int ws80211_frequency_to_channel(int freq); +int ws80211_set_freq(const char *name, int freq, int chan_type); +int ws80211_str_to_chan_type(const gchar *s); +const gchar *ws80211_chan_type_to_str(int type); + +#endif /* __WS80211_UTILS_H__ */ |