Wednesday, April 20, 2011

How Wireshark lists devices

Goal: find how Wireshark gets the list of MAC addresses at the beginning (Windows, MacOS)
Edit: Later found out that neither Wireshark nor libpcap gives a list of MAC addresses.
Answer: To get a list of internet device names, Wireless uses dumpcap's pcap_findalldevs().

I. I looked for MAC address
II. I looked for just the simple list of devices Wireshark shows

I. I grep-ed for "MAC address" and found over 500 entries, under these big categories:
- epan/dissectors (huge)
- plugins
- airpcap

Interesting things found:

  • Ulf Lamping, ulf.lamping[at]web.de, put the source and destination MAC addresses into the top-level item for Ethernet.
  • "manuf" contains Ethernet vendor codes (i.e. the first half of MAC Addresses), and well-known MAC addresses. So I checked the MAC Addresses from my ifconfig -a. eth0's first half is 00:21:70, which manuf says is Dell (yes I have a Dell computer). vmnet*'s first half is 00:50:56, which manuf says is VMware. wlan0's is 00:1c:26, which manuf says is Hon Hai Precision Ind. Co. Cool.
  • Looking at epan/dissectors doesn't seem right. Hmm, 
  • Talked to Victor about it, he told me to look at the RubyGem (see other post) first


II. Give up. So what if I just track it down? The list at the beginning of Wireshark appears below the words "Start capture on interface:". So I grep-ed for those words and found that it appears in ./gtk/main_welcome.c.

There are 2 ways to go in main_welcome.c
1. WIRESHARK_STOCK_CAPTURE_INTERFACES from clicking on "Interface List" button on the main page. This is from
812 welcome_button(WIRESHARK_STOCK_CAPTURE_INTERFACES,
813         "Interface List",
814         "Live list of the capture interfaces\n(counts incoming packets)", ...
But searching down this path wasn't very fruitful

2a. By looking at how the list of devices is generated directly with the welcome_if_panel_load() function.
653 /* load the list of interfaces */
654 static void
655 welcome_if_panel_load(void)

661     GList         *if_list;
662     int err;
663     gchar         *err_str = NULL;


669     /* LOAD THE INTERFACES */

670     if_list = capture_interface_list(&err, &err_str);

btw, the g_blah() functions come from GLib

2b. So I now grep-ed for capture_interface_list(), which attempts to open all adapters it finds in order to check whether they can be captured on.


capture_interface_list() in ./capture_ifinfo.c
 1 /* capture_ifinfo.c
  2  * Routines for getting interface information from dumpcap
 23  */
 24
 25 #ifdef HAVE_CONFIG_H
 26 # include "config.h"
 27 #endif
 28
 29 #ifdef HAVE_LIBPCAP
 30
 31 #include <stdlib.h>
 32 #include <string.h>
 33
 34 #ifdef HAVE_ARPA_INET_H
 35 #include <arpa/inet.h>
 36 #endif
 37
 38 #ifdef HAVE_SYS_SOCKET_H
 39 #include <sys/socket.h>         /* needed to define AF_ values on UNIX */
 40 #endif
 41
 42 #ifdef HAVE_WINSOCK2_H
 43 #include <winsock2.h>           /* needed to define AF_ values on Windows */
 44 #endif
 45 
 46 #ifdef NEED_INET_V6DEFS_H
 47 # include "wsutil/inet_v6defs.h"
 48 #endif
 49 
 50 #include <glib.h>
 51 
 52 #include "capture_opts.h"
 53 #include "capture_sync.h"
 54 #include "log.h"
 55 
 56 #include "capture_ifinfo.h"
 57 
 58 /**
 59  * Fetch the interface list from a child process (dumpcap).
 60  *
 61  * @return A GList containing if_info_t structs if successful, NULL (with err and possibly err_str set) otherwise.
 62  *
 63  */
 64 
 65 /* XXX - We parse simple text output to get our interface list.  Should
 66  * we use "real" data serialization instead, e.g. via XML? */
 67 GList *
 68 capture_interface_list(int *err, char **err_str)
 69 {
 70     int        ret;
 71     GList     *if_list = NULL;
 72     int        i, j;
 73     gchar     *data, *primary_msg, *secondary_msg;
 74     gchar    **raw_list, **if_parts, **addr_parts;
 75     gchar     *name;
 76     if_info_t *if_info;
 77     if_addr_t *if_addr;
 78 
 79     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ...");
 80 
 81     /* Try to get our interface list */
 82     ret = sync_interface_list_open(&data, &primary_msg, &secondary_msg);
 83     if (ret != 0) {
 84         g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!");
 85         if (err_str) {
 86             *err_str = primary_msg;
 87         } else {
 88             g_free(primary_msg);
 89         }
 90         g_free(secondary_msg);
 91         *err = CANT_GET_INTERFACE_LIST;
 92         return NULL;
 93     }
 94 
 95     /* Split our lines */
 96 #ifdef _WIN32
 97     raw_list = g_strsplit(data, "\r\n", 0);
 98 #else
 99     raw_list = g_strsplit(data, "\n", 0);
100 #endif
101     g_free(data);
102 
103     for (i = 0; raw_list[i] != NULL; i++) {
104         if_parts = g_strsplit(raw_list[i], "\t", 4);
105         if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL ||
106                 if_parts[3] == NULL) {
107             g_strfreev(if_parts);
108             continue;
109         }
110 
111         /* Number followed by the name, e.g "1. eth0" */
112         name = strchr(if_parts[0], ' '); /*strchr searches to see if there is a single
                                                                space chr in if_parts[0] */
113         if (name) {
114             name++;
115         } else {
116             g_strfreev(if_parts);
117             continue;
118         }
119 
120         if_info = g_malloc0(sizeof(if_info_t));
121         if_info->name = g_strdup(name);
122         if (strlen(if_parts[1]) > 0)
123             if_info->description = g_strdup(if_parts[1]);
124         addr_parts = g_strsplit(if_parts[2], ",", 0);
125         for (j = 0; addr_parts[j] != NULL; j++) {
126             if_addr = g_malloc0(sizeof(if_addr_t));
127             if (inet_pton(AF_INET, addr_parts[j], &if_addr->addr.ip4_addr)) {
128                 if_addr->ifat_type = IF_AT_IPv4;
129             } else if (inet_pton(AF_INET6, addr_parts[j],
130                     &if_addr->addr.ip6_addr)) {
131                 if_addr->ifat_type = IF_AT_IPv6;
132             } else {
133                 g_free(if_addr);
134                 if_addr = NULL;
135             }
136             if (if_addr) {
137                 if_info->addrs = g_slist_append(if_info->addrs, if_addr);
138             }
139         }
140         if (strcmp(if_parts[3], "loopback") == 0)
141             if_info->loopback = TRUE;
142         g_strfreev(if_parts);
143         g_strfreev(addr_parts);
144         if_list = g_list_append(if_list, if_info);
145     }
146     g_strfreev(raw_list);
147 
148     /* Check to see if we built a list */
149     if (if_list == NULL) {
150         *err = NO_INTERFACES_FOUND;
151         if (err_str)
152             *err_str = g_strdup("No interfaces found");
153     }
154     return if_list;
155 }

2c. Then look at: sync_interface_list_open(), which is found in ./capture_sync.c:
1073 /*
1074  * Get the list of interfaces using dumpcap.
1075  *
1076  * On success, *data points to a buffer containing the dumpcap output,
1077  * *primary_msg and *secondary_msg are NULL, and 0 is returned.  *data
1078  * must be freed with g_free().
1079  *
1080  * On failure, *data is NULL, *primary_msg points to an error message,
1081  * *secondary_msg either points to an additional error message or is
1082  * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
1083  * must be freed with g_free().
1084  */
1085 int
1086 sync_interface_list_open(gchar **data, gchar **primary_msg,
1087                          gchar **secondary_msg)
1088 {
1089     int argc;
1090     const char **argv;
1091
1092     g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_open");
1093
1094     argv = init_pipe_args(&argc);
1095
1096     if (!argv) {
1097         *primary_msg = g_strdup("We don't know where to find dumpcap.");
1098         *secondary_msg = NULL;
1099         *data = NULL;
1100         return -1;
1101     }
1102
1103     /* Ask for the interface list */
1104     argv = sync_pipe_add_arg(argv, &argc, "-D");
1105
1106 #ifndef DEBUG_CHILD
1107     /* Run dumpcap in capture child mode */
1108     argv = sync_pipe_add_arg(argv, &argc, "-Z");
1109     argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
1110 #endif
1111     return sync_pipe_run_command(argv, data, primary_msg, secondary_msg);
1112 }
It's passing in an argument to get the devices, and the argument is "dumpcap"

2d. dumpcap and dumpcap.c
dumpcap is cross platform. :)
man dumpcap told me that the option "-D" prints a list of the interfaces on which Dumpcap can capture.

I found dumpcap's code in "dumpcap.c":
3792      * "-D" requires no interface to be selected; it's supposed to list
3793      * all interfaces.
3794      */
3795     if (list_interfaces) {
3796         /* Get the list of interfaces */
3797         GList       *if_list;
3798         int         err;
3799         gchar       *err_str;
3800 
3801         if_list = capture_interface_list(&err, &err_str);

So then I track down this function

796 GList *
 797 capture_interface_list(int *err, char **err_str)
 798 {
 799     return get_interface_list(err, err_str);
 800 }

after "grep -ir get_interface_list ." I found it in:

2e. capture-wpcap.c

676 /*
677  * This will use "pcap_findalldevs()" if we have it, otherwise it'll
678  * fall back on "pcap_lookupdev()".
679  */
680 GList *
681 get_interface_list(int *err, char **err_str)
682 {
683     GList  *il = NULL;
684     wchar_t *names;
685     char *win95names;
686     char ascii_name[MAX_WIN_IF_NAME_LEN + 1];
687     char ascii_desc[MAX_WIN_IF_NAME_LEN + 1];
688     int i, j;
689     char errbuf[PCAP_ERRBUF_SIZE];
690 
691 #ifdef HAVE_PCAP_FINDALLDEVS
692     if (p_pcap_findalldevs != NULL)
693         return get_interface_list_findalldevs(err, err_str);
694 #endif
...
738 
739     names = (wchar_t *)pcap_lookupdev(errbuf);

There are 2 methods, get_interface_list_findalldevs() or pcap_lookupdev()

Both methods will require code from libpcap, which I found on this site.

Working out the 2 different ways
2d.1. First, let's try with get_interface_list_findalldevs()
Inside capture-pcap-util.c
107 #ifdef HAVE_PCAP_FINDALLDEVS
...
166 GList *
167 get_interface_list_findalldevs(int *err, char **err_str)
168 {
169     GList  *il = NULL;
170     pcap_if_t *alldevs, *dev;
171     if_info_t *if_info;
172     char errbuf[PCAP_ERRBUF_SIZE];
173 
174     if (pcap_findalldevs(&alldevs, errbuf) == -1) {
175         *err = CANT_GET_INTERFACE_LIST;
176         if (err_str != NULL)
177             *err_str = cant_get_if_list_error_message(errbuf);
178         return NULL;
179     }
180 
181     if (alldevs == NULL) {
182         /*
183          * No interfaces found.
184          */
185         *err = NO_INTERFACES_FOUND;
186         if (err_str != NULL)
187             *err_str = NULL;
188         return NULL;
189     }
190 
191     for (dev = alldevs; dev != NULL; dev = dev->next) {
192         if_info = if_info_new(dev->name, dev->description);
193         il = g_list_append(il, if_info);
194         if_info_ip(if_info, dev);
195     }
196     pcap_freealldevs(alldevs);
197 
198     return il;
199 }
200 #endif /* HAVE_PCAP_FINDALLDEVS */


2d.2 Now, let's try with the other method pcap_lookupdev()
233 char*
234 pcap_lookupdev (char *a)
235 {
236     if (!has_wpcap) {
237         return NULL;
238     }
239     return p_pcap_lookupdev(a);
240 }
which is a pointer
56 static char*   (*p_pcap_lookupdev) (char *);

2d.2b. Now we have to look at libpcap's code for pcap_lookupdev, which will also lead to
pcap_findalldevs()
Found it in inet.c
635 #if !defined(WIN32) && !defined(MSDOS)
636 
637 /*
638  * Return the name of a network interface attached to the system, or NULL
639  * if none can be found.  The interface must be configured up; the
640  * lowest unit number is preferred; loopback is ignored.
641  */
642 char *
643 pcap_lookupdev(errbuf)
644     register char *errbuf;
645 {
...
654     if (pcap_findalldevs(&alldevs, errbuf) == -1)

777 #elif defined(WIN32)
778 
779 /*
780  * Return the name of a network interface attached to the system, or NULL
781  * if none can be found.  The interface must be configured up; the
782  * lowest unit number is preferred; loopback is ignored.
783  */
784 char *
785 pcap_lookupdev(errbuf)
786     register char *errbuf;
787 {
788     DWORD dwVersion;
789     DWORD dwWindowsMajorVersion;
790     dwVersion = GetVersion();   /* get the OS version */
791     dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
792 
793     if (dwVersion >= 0x80000000 && dwWindowsMajorVersion >= 4) {
794         /*
795          * Windows 95, 98, ME.
796          */
797         ULONG NameLength = 8192;
798         static char AdaptersName[8192];
799 
800         if (PacketGetAdapterNames(AdaptersName,&NameLength) )
801             return (AdaptersName);
802         else
803             return NULL;
804     } else {
805         /*
806          * Windows NT (NT 4.0, W2K, WXP). Convert the names to UNICODE for backward compatibiliy


3. Now we need to look inside libpcap-1.1.1 for pcap_findalldevs()

Method description from pcap-sita.html:
SMP The Supervisory Management Processor where Wireshark (or equivalent) runs in conjuction with a libpcap front-end.
IOP I/O Processors where the monitored ports exist in conjunction with a custom device driver/libpcap back-end.

pcap_findalldevs constructs a list of network devices that can be opened with pcap_open_live().

SMP It obtains a list of IOPs currently available (via /etc/hosts).
SMP -> IOP The SMP will sequentially open a connection to each IOP on its 'sniffer' port to ensure the IOP is available. It sends a null terminated empty interface ID followed by the query request command.
IOP -> SMP The IOP returns an error response and its list of devices.
SMP -> IOP The SMP closes the TCP connection with each IOP.
SMP The SMP adds the received information to its internal structure.

=========================
SMP/IOP Inter-Process Communication Protocol


  • Communications between an SMP and an IOP consists of a TCP session
    between an ephemeral port on the SMP and the well known port of 49152
    (which is the first available port in the 'dynamic and/or private port'
    range) on an IOP.


  • Following a TCP open operation the IOP receives a null terminated
    'interface ID' string to determine the type of operation that follows:


  • Every command received by an IOP implies a 'stop trace/stop forwarding' operation must
    occur before executing the received command.


  • A session is closed when the SMP closes the TCP session with the IOP.
    Obviously monitoring and forwarding is also stopped at that time.


  • Note: All multi-octet entities are sent in network neutral order.

    SMP -> IOP Open socket (to each IOP), and sends:
    Name/
    Purpose
    Size
    (in bytes)
    Description
    Interface ID 1 A NULL to indicate an an empty 'interface ID'.
    IOP -> SMP Send its (possibly empty) NULL terminated error response string.
    SMP -> IOP Sends the 'interface query request':
    Name/
    Purpose
    Size
    (in bytes)
    Description
    Interface ID 1 A 'Q' (indicating 'interface query request').
    IOP -> SMP The IOP returns a list of sequences of information as
    defined by the return parameter of this function call (as shown in the following table).
    Elements are specified by providing an unsigned byte preceeding the actual data that contains length information.
    Notes: Name/
    Purpose
    Size
    (in bytes)
    Description
    length 1 The number of octets in the name field that follows.
    Name 1-255 The name of the interface. The format of the name is an alphabetic string (indicating
    the type of interface) followed by an optional numeric string (indicating the interface's
    sequence number).
    Sequence numbers (if needed) will begin at zero and progress monotonically upwards.
    (i.e. 'eth0', 'lo', 'wan0', etc.)
    For an IOP, the alphabetic string will be one of: 'eth', 'wan', and 'lo'
    for Ethernet, WAN ports and the IP loopback device respectively.
    An IOP currently supports: 'eth0', 'eth1', 'lo', 'wan0' ... 'wan7'.

    Note: IOPs and ACNs will not currently support the concept of 'any' interface.
    length 1 The number of octets in the interface description field that follows.
    Interface Description 0-255 A description of the interface or it may be an empty string. (i.e. 'ALC')
    Interface Type 4 The type of interface as defined in the description for pcap_datalink() (in network neutral order).
    Loopback Flag 1 1 = if the interface is a loopback interface, zero = otherwise.
    count 1 # of address entries that follow.
    Each entry is a series of bytes in network neutral order.
    See the parameter definition above for more details.
    Repeated 'count' number of times. length 1 The number of octets in the address field that follows.
    Address 1-255 The address of this interface (in network neutral order).
    length 1 The number of octets in the netmask field that follows.
    Network Mask 0-255 The network mask used on this interface (if applicable) (in network neutral order).
    length 1 The number of octets in the broadcast address field that follows.
    Broadcast Address 0-255 The broadcast address of this interface (if applicable) (in network neutral order).
    length 1 The number of octets in the destination address field that follows.
    Destination Address 0-255 The destination address of this interface (if applicable) (in network neutral order).
    SMP -> IOP Close the socket.
    IOP -> SMP Close the socket.
    =========================


    I have "pcap_findalldevs" source from the following files in libpcap-1.1.1:
    they all take the same arguments
    fad-win32.c - for Windows OS
    216  * Win32 implementation, based on WinPcap
    217  */
    218 int
    219 pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf)
    
    pcap-dos.c - for DOS (predecessor of Windows)
    fad-glif.c - for Solaris
    fad-gifc.c - This is the implementation used on platforms that have SIOCGIFCONF but don't have any other mechanism for getting a list of interfaces.
    fad-getad.c - This is the implementation used on platforms that have "getifaddrs()"

    (btw MacOS uses free bsd.)

    Already have a gem to get Linux list MAC Address working
    MacOS can be on hold
    Focus on getting Windows to work! look at fad-win32.c in more detail

    No comments:

    Post a Comment