aboutsummaryrefslogtreecommitdiffstats
path: root/extcap
diff options
context:
space:
mode:
authorFlorian Bezold <florian.bezold@esrlabs.com>2018-02-06 12:39:36 +0100
committerAnders Broman <a.broman58@gmail.com>2018-02-10 07:45:23 +0000
commitbfef57ebb708445c55876b8768aad1a0b98cd29d (patch)
tree871e1f1920c03be8cae638bf5b6c21eeb894d616 /extcap
parent78b7da77164298343ad92de5b9dfef2f7d46b7d6 (diff)
androiddump: Fix and simplify tcpdump capture
1. Use "exec:" ADB command to get raw (non-PTY) tcpdump output This is also supported on Android devices before Android 7, and is a much easier approach than testing the new "shell,raw:" command and falling back if unsupported. This basically undoes commit 5ebc3277. 2. Pass "-U" to tcpdump to prevent on-target buffering Before using the "shell,raw" approach in commit 5ebc3277, I tried the "exec:" command already, but experienced extreme buffering of the tcpdump output, which is unacceptable for live trace viewing. Turns out, the buffering is determined "automatically" by libpcap: - When running in a PTY, output is flushed fast for viewing - When _not_ in a PTY, output is not flushed and thus heavily buffered. The "exec" command obviously doesn't use a PTY. Fortunately, tcpdump has a "-U" option to flush the output after each catpured packet, which is exactly what we need. 3. Ignore tcpdump stderr output Enabling "-U" caused androiddump to fail, because it happened that the tcpdump stderr logs were mixed with the stdout data. (We were probably lucky this didn't happen without -U as well). To fix this, we just ignore stderr completely by adding "2>/dev/null" to the tcpdump command. 4. Get linktype from pcap global header The stderr logs were previously parsed to get the textual linktype. This is now replaced by a simpler & less fragile approach: tcpdump prints the global pcap header, which contains precicesly the linktype info we need. 5. Parse pcap global header magic correctly for timestamps & endianness The previous code only supported the "classic" pcap header magic and might also been incorrect on big-endian host machines. Now, endian handling is simplified and we can detect the "nanosecond timestamp" magic values as well. This fixes the problem that extcap_dumper_dump expects *nano*second timestamps, but the previous code supplied *micro*seconds if on-target tcpdump outputs microseconds. 6. The parsing simplifications above allowed the main loop for tcpdump capture to be simplified considerably. Change-Id: Id66791e700a8943b86128f044f080bee60a9fa79 Reviewed-on: https://code.wireshark.org/review/25713 Petri-Dish: Michael Mann <mmann78@netscape.net> Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'extcap')
-rw-r--r--extcap/androiddump.c281
1 files changed, 89 insertions, 192 deletions
diff --git a/extcap/androiddump.c b/extcap/androiddump.c
index 7f82ed26cb..15d9d9c2a6 100644
--- a/extcap/androiddump.c
+++ b/extcap/androiddump.c
@@ -172,7 +172,6 @@ enum exit_code {
EXIT_CODE_INVALID_SOCKET_9,
EXIT_CODE_INVALID_SOCKET_10,
EXIT_CODE_INVALID_SOCKET_11,
- EXIT_CODE_INVALID_SOCKET_12,
EXIT_CODE_GENERIC = -1
};
@@ -224,6 +223,17 @@ typedef struct _own_pcap_bluetooth_h4_header {
uint32_t direction;
} own_pcap_bluetooth_h4_header;
+typedef struct pcap_hdr_s {
+ guint32 magic_number; /* magic number */
+ guint16 version_major; /* major version number */
+ guint16 version_minor; /* minor version number */
+ gint32 thiszone; /* GMT to local correction */
+ guint32 sigfigs; /* accuracy of timestamps */
+ guint32 snaplen; /* max length of captured packets, in octets */
+ guint32 network; /* data link type */
+} pcap_hdr_t;
+
+
typedef struct pcaprec_hdr_s {
guint32 ts_sec; /* timestamp seconds */
guint32 ts_usec; /* timestamp microseconds */
@@ -2269,36 +2279,6 @@ static int capture_android_logcat(char *interface, char *fifo,
return EXIT_CODE_SUCCESS;
}
-/* Translate tcpdump data link type strings to EXTCAP_ENCAP_ types
- * For info about available data link types see:
- * http://www.tcpdump.org/linktypes.html
- */
-static int linktype_to_extcap_encap(const char* linktype)
-{
- struct dlt_encap {
- int extcap_encap;
- const char* const dlt;
- };
- const struct dlt_encap lookup[] = {
- { EXTCAP_ENCAP_LINUX_SLL, "LINUX_SLL" },
- { EXTCAP_ENCAP_ETHERNET, "EN10MB" },
- { EXTCAP_ENCAP_IEEE802_11_RADIO, "IEEE802_11_RADIO" },
- { EXTCAP_ENCAP_NETLINK, "NETLINK" },
- { -1, NULL }
- };
- int i;
- int ret = EXTCAP_ENCAP_ETHERNET;
-
- if (!linktype)
- return ret;
- for (i = 0; lookup[i].dlt; i++) {
- if (!strcmp(lookup[i].dlt, linktype)) {
- ret = lookup[i].extcap_encap;
- }
- }
- return ret;
-}
-
/*----------------------------------------------------------------------------*/
/* Android Wifi Tcpdump */
@@ -2307,30 +2287,25 @@ static int linktype_to_extcap_encap(const char* linktype)
/*----------------------------------------------------------------------------*/
static int capture_android_tcpdump(char *interface, char *fifo,
const char *adb_server_ip, unsigned short *adb_server_tcp_port) {
- static const char *const adb_shell_tcpdump_format = "shell,raw:tcpdump -n -s 0 -u -i %s -w -";
- static const char *const adb_shell_legacy_tcpdump_format = "shell:tcpdump -n -s 0 -u -i %s -w -";
+ static const char *const adb_shell_tcpdump_format = "exec:tcpdump -U -n -s 0 -u -i %s -w - 2>/dev/null";
static const char *const regex_interface = INTERFACE_ANDROID_TCPDUMP "-(?<iface>.*?)-(?<serial>.*)";
- static const char *const regex_linktype = "tcpdump: listening on .*?, link-type (?<linktype>.*?) ";
struct extcap_dumper extcap_dumper;
static char data[PACKET_LENGTH];
gssize length;
gssize used_buffer_length = 0;
- gssize filter_buffer_length = 0;
gssize frame_length=0;
socket_handle_t sock;
gint result;
char *iface = NULL;
char *serial_number = NULL;
- static char filter_buffer[PACKET_LENGTH];
- gint device_endiness = G_LITTLE_ENDIAN;
- gboolean global_header_skipped=FALSE;
+ gboolean nanosecond_timestamps;
+ gboolean swap_byte_order;
+ pcap_hdr_t *global_header;
pcaprec_hdr_t p_header;
- char *linktype = NULL;
GRegex *regex = NULL;
GError *err = NULL;
GMatchInfo *match = NULL;
char tcpdump_cmd[80];
- gboolean pty_mode = FALSE;
regex = g_regex_new(regex_interface, (GRegexCompileFlags)0, (GRegexMatchFlags)0, &err);
if (!regex) {
@@ -2352,51 +2327,24 @@ static int capture_android_tcpdump(char *interface, char *fifo,
/* First check for the device if it is connected or not */
sock = adb_connect_transport(adb_server_ip, adb_server_tcp_port, serial_number);
+ g_free(serial_number);
if (sock == INVALID_SOCKET) {
g_free(iface);
- g_free(serial_number);
return EXIT_CODE_INVALID_SOCKET_11;
}
- /* Try the new raw (non-PTY) shell protocol first */
g_snprintf(tcpdump_cmd, sizeof(tcpdump_cmd), adb_shell_tcpdump_format, iface);
+ g_free(iface);
result = adb_send(sock, tcpdump_cmd);
if (result) {
- g_debug("Target does not support raw shell protocol");
- closesocket(sock);
-
- /* Fall back to the old PTY shell */
- sock = adb_connect_transport(adb_server_ip, adb_server_tcp_port, serial_number);
- if (sock == INVALID_SOCKET) {
- g_free(iface);
- g_free(serial_number);
- return EXIT_CODE_INVALID_SOCKET_12;
- }
-
- pty_mode = TRUE;
- g_snprintf(tcpdump_cmd, sizeof(tcpdump_cmd), adb_shell_legacy_tcpdump_format, iface);
- result = adb_send(sock, tcpdump_cmd);
- }
- if (result) {
g_warning("Error while setting adb transport");
closesocket(sock);
return EXIT_CODE_GENERIC;
}
- g_free(iface);
- g_free(serial_number);
-
- regex = g_regex_new(regex_linktype, (GRegexCompileFlags)0, (GRegexMatchFlags)0, &err);
- if (!regex) {
- g_warning("Failed to compile regex for tcpdump data link type matching");
- closesocket(sock);
- return EXIT_CODE_GENERIC;
- }
-
- while (endless_loop) {
- char *i_position;
+ while (used_buffer_length < PCAP_GLOBAL_HEADER_LENGTH) {
errno = 0;
- length = recv(sock, data + used_buffer_length, (int)(PACKET_LENGTH - used_buffer_length), 0);
+ length = recv(sock, data + used_buffer_length, (int)(PCAP_GLOBAL_HEADER_LENGTH - used_buffer_length), 0);
if (errno == EAGAIN
#if EWOULDBLOCK != EAGAIN
|| errno == EWOULDBLOCK
@@ -2407,155 +2355,104 @@ static int capture_android_tcpdump(char *interface, char *fifo,
else if (errno != 0) {
g_warning("ERROR capture: %s", strerror(errno));
closesocket(sock);
- g_regex_unref(regex);
return EXIT_CODE_GENERIC;
}
if (length <= 0) {
g_warning("Broken socket connection.");
closesocket(sock);
- g_regex_unref(regex);
return EXIT_CODE_GENERIC;
}
used_buffer_length += length;
+ }
- /*
- * Checking for the starting for the pcap global header using the magic number
- */
- if (used_buffer_length > 4) {
- guint * magic_number;
- magic_number= (guint *)data;
- if (*magic_number == 0xd4c3b2a1 || *magic_number == 0xa1b2c3d4) {
- if (data[0] == (char)0xd4){
- device_endiness = G_LITTLE_ENDIAN;
- }
- else {
- device_endiness = G_BIG_ENDIAN;
- }
- break;
- }
- }
-
- g_regex_match(regex, data, (GRegexMatchFlags)0, &match);
- if (g_match_info_matches(match)) {
- g_free(linktype);
- linktype = g_match_info_fetch_named(match, "linktype");
- }
- g_match_info_free(match);
- i_position = (char *) memchr(data, '\n', used_buffer_length);
- if (i_position && i_position < data + used_buffer_length) {
- memmove(data, i_position + 1 , used_buffer_length - (i_position + 1 - data));
- used_buffer_length = used_buffer_length - (gssize) (i_position + 1 - data);
- }
+ global_header = (pcap_hdr_t*) data;
+ switch (global_header->magic_number) {
+ case 0xa1b2c3d4:
+ swap_byte_order = FALSE;
+ nanosecond_timestamps = FALSE;
+ break;
+ case 0xd4c3b2a1:
+ swap_byte_order = TRUE;
+ nanosecond_timestamps = FALSE;
+ break;
+ case 0xa1b23c4d:
+ swap_byte_order = FALSE;
+ nanosecond_timestamps = TRUE;
+ break;
+ case 0x4d3cb2a1:
+ swap_byte_order = TRUE;
+ nanosecond_timestamps = TRUE;
+ break;
+ default:
+ g_warning("Received incorrect magic");
+ closesocket(sock);
+ return EXIT_CODE_GENERIC;
}
- g_regex_unref(regex);
- extcap_dumper = extcap_dumper_open(fifo, linktype_to_extcap_encap(linktype));
- g_free(linktype);
- filter_buffer_length=0;
+ extcap_dumper = extcap_dumper_open(fifo, (int) data[20]);
+
+ used_buffer_length = 0;
while (endless_loop) {
- gssize i = 0,read_offset,j=0;
-
- /*
- * Before Android 7 adb runs tcpdump in a shell/pseudoterminal and the PTY layer on the target converts
- * all \n to \r\n. In that case we need to undo this by changing all \r\n (0x0d0a) back to \n (0x0a).
- */
- for (i = 0; i < (used_buffer_length - 1); i++) {
- if (pty_mode && data[i] == 0x0d && data[i + 1] == 0x0a) {
- i++;
- }
- filter_buffer[filter_buffer_length++] = data[i];
- }
+ gssize offset = 0;
- /* Put the last characters in the start if it is still left in buffer.*/
- for (j=0; i < used_buffer_length; i++,j++) {
- data[j] = data[i];
+ errno = 0;
+ length = recv(sock, data + used_buffer_length, (int)(PACKET_LENGTH - used_buffer_length), 0);
+ if (errno == EAGAIN
+#if EWOULDBLOCK != EAGAIN
+ || errno == EWOULDBLOCK
+#endif
+ ) {
+ continue;
}
- used_buffer_length = j;
- if (global_header_skipped==FALSE && filter_buffer_length >= PCAP_GLOBAL_HEADER_LENGTH) {
- /*Skip the Global pcap header*/
- filter_buffer_length -= PCAP_GLOBAL_HEADER_LENGTH;
-
- /*Move the remaining content from start*/
- memmove(filter_buffer , filter_buffer + PCAP_GLOBAL_HEADER_LENGTH , filter_buffer_length);
- global_header_skipped = TRUE;
+ else if (errno != 0) {
+ g_warning("ERROR capture: %s", strerror(errno));
+ closesocket(sock);
+ return EXIT_CODE_GENERIC;
}
- else if (global_header_skipped && filter_buffer_length > PCAP_RECORD_HEADER_LENGTH) {
- read_offset=0;
- while (filter_buffer_length > PCAP_RECORD_HEADER_LENGTH) {
- gchar *packet;
- packet = filter_buffer + read_offset;
- /*
- * This fills the pcap header info based upon the endianess of the machine and android device.
- * If the endianess are different, pcap header bytes received from the android device are swapped
- * to be read properly by the machine else pcap header bytes are taken as it is.
- */
- if (device_endiness == G_BYTE_ORDER) {
- p_header = *((pcaprec_hdr_t*)packet);
- }
- else {
- p_header.ts_sec = GUINT32_SWAP_LE_BE(*((guint32*)packet));
- p_header.ts_usec = GUINT32_SWAP_LE_BE(*(guint32*)(packet +4));
- p_header.incl_len = GUINT32_SWAP_LE_BE(*(guint32*)(packet +8));
- p_header.orig_len = GUINT32_SWAP_LE_BE(*(guint32*)(packet +12));
- }
- if ((gssize)(p_header.incl_len + PCAP_RECORD_HEADER_LENGTH) <= filter_buffer_length) {
+ if (length <= 0) {
+ g_warning("Broken socket connection.");
+ closesocket(sock);
+ return EXIT_CODE_GENERIC;
+ }
- /*
- * It was observed that some times tcpdump reports the length of packet as '0' and that leads to the
- * ( Warn Error "Less data was read than was expected" while reading )
- * So to avoid this error we are checking for length of packet before passing it to dumper.
- */
- if (p_header.incl_len > 0) {
- endless_loop = extcap_dumper_dump(extcap_dumper, fifo, filter_buffer + read_offset+ PCAP_RECORD_HEADER_LENGTH,
- p_header.incl_len , p_header.orig_len , p_header.ts_sec , p_header.ts_usec);
- }
- frame_length = p_header.incl_len + PCAP_RECORD_HEADER_LENGTH;
+ used_buffer_length += length;
- /*update the offset value for the next packet*/
- filter_buffer_length -= frame_length;
- read_offset += frame_length;
- }
- else {
- /*The complete packet has not yet received*/
- break;
- }
+ while ((used_buffer_length - offset) > PCAP_RECORD_HEADER_LENGTH) {
+ p_header = *((pcaprec_hdr_t*) (data + offset));
+ if (swap_byte_order) {
+ p_header.ts_sec = GUINT32_SWAP_LE_BE(p_header.ts_sec);
+ p_header.ts_usec = GUINT32_SWAP_LE_BE(p_header.ts_usec);
+ p_header.incl_len = GUINT32_SWAP_LE_BE(p_header.incl_len);
+ p_header.orig_len = GUINT32_SWAP_LE_BE(p_header.orig_len);
}
- if (read_offset!=0) {
- /*Move the rest of the filter data to the beginning of the filter_buffer */
- memmove(filter_buffer, filter_buffer + read_offset , filter_buffer_length);
+ if (!nanosecond_timestamps) {
+ p_header.ts_usec = p_header.ts_usec * 1000;
}
- }
- /*Get the data from the tcpdump process running in the android device*/
- while (endless_loop) {
- errno = 0;
- length = recv(sock, data + used_buffer_length, (int)(PACKET_LENGTH -(used_buffer_length + filter_buffer_length)), 0);
- if (errno == EAGAIN
-#if EWOULDBLOCK != EAGAIN
- || errno == EWOULDBLOCK
-#endif
- ) {
- continue;
- }
- else if (errno != 0) {
- g_warning("ERROR capture: %s", strerror(errno));
- closesocket(sock);
- return EXIT_CODE_GENERIC;
+ frame_length = p_header.incl_len + PCAP_RECORD_HEADER_LENGTH;
+ if ((used_buffer_length - offset) < frame_length) {
+ break; /* wait for complete packet */
}
- if (length <= 0) {
- g_warning("Broken socket connection.");
- closesocket(sock);
- return EXIT_CODE_GENERIC;
+ /* It was observed that some times tcpdump reports the length of packet as '0' and that leads to the
+ * ( Warn Error "Less data was read than was expected" while reading )
+ * So to avoid this error we are checking for length of packet before passing it to dumper.
+ */
+ if (p_header.incl_len > 0) {
+ endless_loop = extcap_dumper_dump(extcap_dumper, fifo, data + offset + PCAP_RECORD_HEADER_LENGTH,
+ p_header.incl_len, p_header.orig_len, p_header.ts_sec, p_header.ts_usec);
}
- if ((used_buffer_length += length) > 1) {
- break;
- }
+ offset += frame_length;
+ }
+
+ if (offset < used_buffer_length) {
+ memmove(data, data + offset, used_buffer_length - offset);
}
+ used_buffer_length -= offset;
}
closesocket(sock);