Blame tools/hcidump.c

Packit 34410b
/*
Packit 34410b
 *
Packit 34410b
 *  BlueZ - Bluetooth protocol stack for Linux
Packit 34410b
 *
Packit 34410b
 *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
Packit 34410b
 *  Copyright (C) 2003-2011  Marcel Holtmann <marcel@holtmann.org>
Packit 34410b
 *
Packit 34410b
 *
Packit 34410b
 *  This program is free software; you can redistribute it and/or modify
Packit 34410b
 *  it under the terms of the GNU General Public License as published by
Packit 34410b
 *  the Free Software Foundation; either version 2 of the License, or
Packit 34410b
 *  (at your option) any later version.
Packit 34410b
 *
Packit 34410b
 *  This program is distributed in the hope that it will be useful,
Packit 34410b
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 34410b
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 34410b
 *  GNU General Public License for more details.
Packit 34410b
 *
Packit 34410b
 *  You should have received a copy of the GNU General Public License
Packit 34410b
 *  along with this program; if not, write to the Free Software
Packit 34410b
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit 34410b
 *
Packit 34410b
 */
Packit 34410b
Packit 34410b
#ifdef HAVE_CONFIG_H
Packit 34410b
#include <config.h>
Packit 34410b
#endif
Packit 34410b
Packit 34410b
#define _GNU_SOURCE
Packit 34410b
#include <stdio.h>
Packit 34410b
#include <errno.h>
Packit 34410b
#include <fcntl.h>
Packit 34410b
#include <unistd.h>
Packit 34410b
#include <stdlib.h>
Packit 34410b
#include <string.h>
Packit 34410b
#include <getopt.h>
Packit 34410b
#include <poll.h>
Packit 34410b
#include <sys/stat.h>
Packit 34410b
#include <sys/types.h>
Packit 34410b
#include <sys/ioctl.h>
Packit 34410b
#include <sys/socket.h>
Packit 34410b
Packit 34410b
#include "parser/parser.h"
Packit 34410b
#include "parser/sdp.h"
Packit 34410b
Packit 34410b
#include "lib/hci.h"
Packit 34410b
#include "lib/hci_lib.h"
Packit 34410b
Packit 34410b
#define SNAP_LEN	HCI_MAX_FRAME_SIZE
Packit 34410b
Packit 34410b
/* Modes */
Packit 34410b
enum {
Packit 34410b
	PARSE,
Packit 34410b
	READ,
Packit 34410b
	WRITE,
Packit 34410b
	PPPDUMP,
Packit 34410b
	AUDIO
Packit 34410b
};
Packit 34410b
Packit 34410b
/* Default options */
Packit 34410b
static int  snap_len = SNAP_LEN;
Packit 34410b
static int  mode = PARSE;
Packit 34410b
static char *dump_file = NULL;
Packit 34410b
static char *pppdump_file = NULL;
Packit 34410b
static char *audio_file = NULL;
Packit 34410b
Packit 34410b
struct hcidump_hdr {
Packit 34410b
	uint16_t	len;
Packit 34410b
	uint8_t		in;
Packit 34410b
	uint8_t		pad;
Packit 34410b
	uint32_t	ts_sec;
Packit 34410b
	uint32_t	ts_usec;
Packit 34410b
} __attribute__ ((packed));
Packit 34410b
#define HCIDUMP_HDR_SIZE (sizeof(struct hcidump_hdr))
Packit 34410b
Packit 34410b
struct btsnoop_hdr {
Packit 34410b
	uint8_t		id[8];		/* Identification Pattern */
Packit 34410b
	uint32_t	version;	/* Version Number = 1 */
Packit 34410b
	uint32_t	type;		/* Datalink Type */
Packit 34410b
} __attribute__ ((packed));
Packit 34410b
#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
Packit 34410b
Packit 34410b
struct btsnoop_pkt {
Packit 34410b
	uint32_t	size;		/* Original Length */
Packit 34410b
	uint32_t	len;		/* Included Length */
Packit 34410b
	uint32_t	flags;		/* Packet Flags */
Packit 34410b
	uint32_t	drops;		/* Cumulative Drops */
Packit 34410b
	uint64_t	ts;		/* Timestamp microseconds */
Packit 34410b
	uint8_t		data[0];	/* Packet Data */
Packit 34410b
} __attribute__ ((packed));
Packit 34410b
#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
Packit 34410b
Packit 34410b
static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
Packit 34410b
Packit 34410b
static uint32_t btsnoop_version = 0;
Packit 34410b
static uint32_t btsnoop_type = 0;
Packit 34410b
Packit 34410b
struct pktlog_hdr {
Packit 34410b
	uint32_t	len;
Packit 34410b
	uint64_t	ts;
Packit 34410b
	uint8_t		type;
Packit 34410b
} __attribute__ ((packed));
Packit 34410b
#define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr))
Packit 34410b
Packit 34410b
static inline int read_n(int fd, char *buf, int len)
Packit 34410b
{
Packit 34410b
	int t = 0, w;
Packit 34410b
Packit 34410b
	while (len > 0) {
Packit 34410b
		if ((w = read(fd, buf, len)) < 0) {
Packit 34410b
			if (errno == EINTR || errno == EAGAIN)
Packit 34410b
				continue;
Packit 34410b
			return -1;
Packit 34410b
		}
Packit 34410b
		if (!w)
Packit 34410b
			return 0;
Packit 34410b
		len -= w; buf += w; t += w;
Packit 34410b
	}
Packit 34410b
	return t;
Packit 34410b
}
Packit 34410b
Packit 34410b
static inline int write_n(int fd, char *buf, int len)
Packit 34410b
{
Packit 34410b
	int t = 0, w;
Packit 34410b
Packit 34410b
	while (len > 0) {
Packit 34410b
		if ((w = write(fd, buf, len)) < 0) {
Packit 34410b
			if (errno == EINTR || errno == EAGAIN)
Packit 34410b
				continue;
Packit 34410b
			return -1;
Packit 34410b
		}
Packit 34410b
		if (!w)
Packit 34410b
			return 0;
Packit 34410b
		len -= w; buf += w; t += w;
Packit 34410b
	}
Packit 34410b
	return t;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int process_frames(int dev, int sock, int fd, unsigned long flags)
Packit 34410b
{
Packit 34410b
	struct cmsghdr *cmsg;
Packit 34410b
	struct msghdr msg;
Packit 34410b
	struct iovec  iv;
Packit 34410b
	struct hcidump_hdr *dh;
Packit 34410b
	struct btsnoop_pkt *dp;
Packit 34410b
	struct frame frm;
Packit 34410b
	struct pollfd fds[2];
Packit 34410b
	int nfds = 0;
Packit 34410b
	char *buf;
Packit 34410b
	char ctrl[100];
Packit 34410b
	int len, hdr_size = HCIDUMP_HDR_SIZE;
Packit 34410b
Packit 34410b
	if (sock < 0)
Packit 34410b
		return -1;
Packit 34410b
Packit 34410b
	if (snap_len < SNAP_LEN)
Packit 34410b
		snap_len = SNAP_LEN;
Packit 34410b
Packit 34410b
	if (flags & DUMP_BTSNOOP)
Packit 34410b
		hdr_size = BTSNOOP_PKT_SIZE;
Packit 34410b
Packit 34410b
	buf = malloc(snap_len + hdr_size);
Packit 34410b
	if (!buf) {
Packit 34410b
		perror("Can't allocate data buffer");
Packit 34410b
		return -1;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	dh = (void *) buf;
Packit 34410b
	dp = (void *) buf;
Packit 34410b
	frm.data = buf + hdr_size;
Packit 34410b
Packit 34410b
	if (dev == HCI_DEV_NONE)
Packit 34410b
		printf("system: ");
Packit 34410b
	else
Packit 34410b
		printf("device: hci%d ", dev);
Packit 34410b
Packit 34410b
	printf("snap_len: %d filter: 0x%lx\n", snap_len, parser.filter);
Packit 34410b
Packit 34410b
	memset(&msg, 0, sizeof(msg));
Packit 34410b
Packit 34410b
	fds[nfds].fd = sock;
Packit 34410b
	fds[nfds].events = POLLIN;
Packit 34410b
	fds[nfds].revents = 0;
Packit 34410b
	nfds++;
Packit 34410b
Packit 34410b
	while (1) {
Packit 34410b
		int i, n = poll(fds, nfds, -1);
Packit 34410b
		if (n <= 0)
Packit 34410b
			continue;
Packit 34410b
Packit 34410b
		for (i = 0; i < nfds; i++) {
Packit 34410b
			if (fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) {
Packit 34410b
				if (fds[i].fd == sock)
Packit 34410b
					printf("device: disconnected\n");
Packit 34410b
				else
Packit 34410b
					printf("client: disconnect\n");
Packit 34410b
				return 0;
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
Packit 34410b
		iv.iov_base = frm.data;
Packit 34410b
		iv.iov_len  = snap_len;
Packit 34410b
Packit 34410b
		msg.msg_iov = &iv;
Packit 34410b
		msg.msg_iovlen = 1;
Packit 34410b
		msg.msg_control = ctrl;
Packit 34410b
		msg.msg_controllen = 100;
Packit 34410b
Packit 34410b
		len = recvmsg(sock, &msg, MSG_DONTWAIT);
Packit 34410b
		if (len < 0) {
Packit 34410b
			if (errno == EAGAIN || errno == EINTR)
Packit 34410b
				continue;
Packit 34410b
			perror("Receive failed");
Packit 34410b
			return -1;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		/* Process control message */
Packit 34410b
		frm.data_len = len;
Packit 34410b
		frm.dev_id = dev;
Packit 34410b
		frm.in = 0;
Packit 34410b
		frm.pppdump_fd = parser.pppdump_fd;
Packit 34410b
		frm.audio_fd   = parser.audio_fd;
Packit 34410b
Packit 34410b
		cmsg = CMSG_FIRSTHDR(&msg;;
Packit 34410b
		while (cmsg) {
Packit 34410b
			int dir;
Packit 34410b
			switch (cmsg->cmsg_type) {
Packit 34410b
			case HCI_CMSG_DIR:
Packit 34410b
				memcpy(&dir, CMSG_DATA(cmsg), sizeof(int));
Packit 34410b
				frm.in = (uint8_t) dir;
Packit 34410b
				break;
Packit 34410b
			case HCI_CMSG_TSTAMP:
Packit 34410b
				memcpy(&frm.ts, CMSG_DATA(cmsg),
Packit 34410b
						sizeof(struct timeval));
Packit 34410b
				break;
Packit 34410b
			}
Packit 34410b
			cmsg = CMSG_NXTHDR(&msg, cmsg);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		frm.ptr = frm.data;
Packit 34410b
		frm.len = frm.data_len;
Packit 34410b
Packit 34410b
		switch (mode) {
Packit 34410b
		case WRITE:
Packit 34410b
			/* Save or send dump */
Packit 34410b
			if (flags & DUMP_BTSNOOP) {
Packit 34410b
				uint64_t ts;
Packit 34410b
				uint8_t pkt_type = ((uint8_t *) frm.data)[0];
Packit 34410b
				dp->size = htobe32(frm.data_len);
Packit 34410b
				dp->len  = dp->size;
Packit 34410b
				dp->flags = be32toh(frm.in & 0x01);
Packit 34410b
				dp->drops = 0;
Packit 34410b
				ts = (frm.ts.tv_sec - 946684800ll) * 1000000ll + frm.ts.tv_usec;
Packit 34410b
				dp->ts = htobe64(ts + 0x00E03AB44A676000ll);
Packit 34410b
				if (pkt_type == HCI_COMMAND_PKT ||
Packit 34410b
						pkt_type == HCI_EVENT_PKT)
Packit 34410b
					dp->flags |= be32toh(0x02);
Packit 34410b
			} else {
Packit 34410b
				dh->len = htobs(frm.data_len);
Packit 34410b
				dh->in  = frm.in;
Packit 34410b
				dh->ts_sec  = htobl(frm.ts.tv_sec);
Packit 34410b
				dh->ts_usec = htobl(frm.ts.tv_usec);
Packit 34410b
			}
Packit 34410b
Packit 34410b
			if (write_n(fd, buf, frm.data_len + hdr_size) < 0) {
Packit 34410b
				perror("Write error");
Packit 34410b
				return -1;
Packit 34410b
			}
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		default:
Packit 34410b
			/* Parse and print */
Packit 34410b
			parse(&frm;;
Packit 34410b
			break;
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void read_dump(int fd)
Packit 34410b
{
Packit 34410b
	struct hcidump_hdr dh;
Packit 34410b
	struct btsnoop_pkt dp;
Packit 34410b
	struct pktlog_hdr ph;
Packit 34410b
	struct frame frm;
Packit 34410b
	int err;
Packit 34410b
Packit 34410b
	frm.data = malloc(HCI_MAX_FRAME_SIZE);
Packit 34410b
	if (!frm.data) {
Packit 34410b
		perror("Can't allocate data buffer");
Packit 34410b
		exit(1);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	while (1) {
Packit 34410b
		if (parser.flags & DUMP_PKTLOG)
Packit 34410b
			err = read_n(fd, (void *) &ph, PKTLOG_HDR_SIZE);
Packit 34410b
		else if (parser.flags & DUMP_BTSNOOP)
Packit 34410b
			err = read_n(fd, (void *) &dp, BTSNOOP_PKT_SIZE);
Packit 34410b
		else
Packit 34410b
			err = read_n(fd, (void *) &dh, HCIDUMP_HDR_SIZE);
Packit 34410b
Packit 34410b
		if (err < 0)
Packit 34410b
			goto failed;
Packit 34410b
		if (!err)
Packit 34410b
			goto done;
Packit 34410b
Packit 34410b
		if (parser.flags & DUMP_PKTLOG) {
Packit 34410b
			switch (ph.type) {
Packit 34410b
			case 0x00:
Packit 34410b
				((uint8_t *) frm.data)[0] = HCI_COMMAND_PKT;
Packit 34410b
				frm.in = 0;
Packit 34410b
				break;
Packit 34410b
			case 0x01:
Packit 34410b
				((uint8_t *) frm.data)[0] = HCI_EVENT_PKT;
Packit 34410b
				frm.in = 1;
Packit 34410b
				break;
Packit 34410b
			case 0x02:
Packit 34410b
				((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT;
Packit 34410b
				frm.in = 0;
Packit 34410b
				break;
Packit 34410b
			case 0x03:
Packit 34410b
				((uint8_t *) frm.data)[0] = HCI_ACLDATA_PKT;
Packit 34410b
				frm.in = 1;
Packit 34410b
				break;
Packit 34410b
			default:
Packit 34410b
				lseek(fd, be32toh(ph.len) - 9, SEEK_CUR);
Packit 34410b
				continue;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			frm.data_len = be32toh(ph.len) - 8;
Packit 34410b
			err = read_n(fd, frm.data + 1, frm.data_len - 1);
Packit 34410b
		} else if (parser.flags & DUMP_BTSNOOP) {
Packit 34410b
			uint32_t opcode;
Packit 34410b
			uint8_t pkt_type;
Packit 34410b
Packit 34410b
			switch (btsnoop_type) {
Packit 34410b
			case 1001:
Packit 34410b
				if (be32toh(dp.flags) & 0x02) {
Packit 34410b
					if (be32toh(dp.flags) & 0x01)
Packit 34410b
						pkt_type = HCI_EVENT_PKT;
Packit 34410b
					else
Packit 34410b
						pkt_type = HCI_COMMAND_PKT;
Packit 34410b
				} else
Packit 34410b
					pkt_type = HCI_ACLDATA_PKT;
Packit 34410b
Packit 34410b
				((uint8_t *) frm.data)[0] = pkt_type;
Packit 34410b
Packit 34410b
				frm.data_len = be32toh(dp.len) + 1;
Packit 34410b
				err = read_n(fd, frm.data + 1, frm.data_len - 1);
Packit 34410b
				break;
Packit 34410b
Packit 34410b
			case 1002:
Packit 34410b
				frm.data_len = be32toh(dp.len);
Packit 34410b
				err = read_n(fd, frm.data, frm.data_len);
Packit 34410b
				break;
Packit 34410b
Packit 34410b
			case 2001:
Packit 34410b
				opcode = be32toh(dp.flags) & 0xffff;
Packit 34410b
Packit 34410b
				switch (opcode) {
Packit 34410b
				case 2:
Packit 34410b
					pkt_type = HCI_COMMAND_PKT;
Packit 34410b
					frm.in = 0;
Packit 34410b
					break;
Packit 34410b
				case 3:
Packit 34410b
					pkt_type = HCI_EVENT_PKT;
Packit 34410b
					frm.in = 1;
Packit 34410b
					break;
Packit 34410b
				case 4:
Packit 34410b
					pkt_type = HCI_ACLDATA_PKT;
Packit 34410b
					frm.in = 0;
Packit 34410b
					break;
Packit 34410b
				case 5:
Packit 34410b
					pkt_type = HCI_ACLDATA_PKT;
Packit 34410b
					frm.in = 1;
Packit 34410b
					break;
Packit 34410b
				case 6:
Packit 34410b
					pkt_type = HCI_SCODATA_PKT;
Packit 34410b
					frm.in = 0;
Packit 34410b
					break;
Packit 34410b
				case 7:
Packit 34410b
					pkt_type = HCI_SCODATA_PKT;
Packit 34410b
					frm.in = 1;
Packit 34410b
					break;
Packit 34410b
				default:
Packit 34410b
					pkt_type = 0xff;
Packit 34410b
					break;
Packit 34410b
				}
Packit 34410b
Packit 34410b
				((uint8_t *) frm.data)[0] = pkt_type;
Packit 34410b
Packit 34410b
				frm.data_len = be32toh(dp.len) + 1;
Packit 34410b
				err = read_n(fd, frm.data + 1, frm.data_len - 1);
Packit 34410b
			}
Packit 34410b
		} else {
Packit 34410b
			frm.data_len = btohs(dh.len);
Packit 34410b
			err = read_n(fd, frm.data, frm.data_len);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (err < 0)
Packit 34410b
			goto failed;
Packit 34410b
		if (!err)
Packit 34410b
			goto done;
Packit 34410b
Packit 34410b
		frm.ptr = frm.data;
Packit 34410b
		frm.len = frm.data_len;
Packit 34410b
Packit 34410b
		if (parser.flags & DUMP_PKTLOG) {
Packit 34410b
			uint64_t ts;
Packit 34410b
			ts = be64toh(ph.ts);
Packit 34410b
			frm.ts.tv_sec = ts >> 32;
Packit 34410b
			frm.ts.tv_usec = ts & 0xffffffff;
Packit 34410b
		} else if (parser.flags & DUMP_BTSNOOP) {
Packit 34410b
			uint64_t ts;
Packit 34410b
			frm.in = be32toh(dp.flags) & 0x01;
Packit 34410b
			ts = be64toh(dp.ts) - 0x00E03AB44A676000ll;
Packit 34410b
			frm.ts.tv_sec = (ts / 1000000ll) + 946684800ll;
Packit 34410b
			frm.ts.tv_usec = ts % 1000000ll;
Packit 34410b
		} else {
Packit 34410b
			frm.in = dh.in;
Packit 34410b
			frm.ts.tv_sec  = btohl(dh.ts_sec);
Packit 34410b
			frm.ts.tv_usec = btohl(dh.ts_usec);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		parse(&frm;;
Packit 34410b
	}
Packit 34410b
Packit 34410b
done:
Packit 34410b
	free(frm.data);
Packit 34410b
	return;
Packit 34410b
Packit 34410b
failed:
Packit 34410b
	perror("Read failed");
Packit 34410b
	free(frm.data);
Packit 34410b
	exit(1);
Packit 34410b
}
Packit 34410b
Packit 34410b
static int open_file(char *file, int mode, unsigned long flags)
Packit 34410b
{
Packit 34410b
	unsigned char buf[BTSNOOP_HDR_SIZE];
Packit 34410b
	struct btsnoop_hdr *hdr = (struct btsnoop_hdr *) buf;
Packit 34410b
	int fd, len, open_flags;
Packit 34410b
Packit 34410b
	if (mode == WRITE || mode == PPPDUMP || mode == AUDIO)
Packit 34410b
		open_flags = O_WRONLY | O_CREAT | O_TRUNC;
Packit 34410b
	else
Packit 34410b
		open_flags = O_RDONLY;
Packit 34410b
Packit 34410b
	fd = open(file, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
Packit 34410b
	if (fd < 0) {
Packit 34410b
		perror("Can't open dump file");
Packit 34410b
		exit(1);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (mode == READ) {
Packit 34410b
		len = read(fd, buf, BTSNOOP_HDR_SIZE);
Packit 34410b
		if (len != BTSNOOP_HDR_SIZE) {
Packit 34410b
			lseek(fd, 0, SEEK_SET);
Packit 34410b
			return fd;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (!memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id))) {
Packit 34410b
			parser.flags |= DUMP_BTSNOOP;
Packit 34410b
Packit 34410b
			btsnoop_version = be32toh(hdr->version);
Packit 34410b
			btsnoop_type = be32toh(hdr->type);
Packit 34410b
Packit 34410b
			printf("btsnoop version: %d datalink type: %d\n",
Packit 34410b
						btsnoop_version, btsnoop_type);
Packit 34410b
Packit 34410b
			if (btsnoop_version != 1) {
Packit 34410b
				fprintf(stderr, "Unsupported BTSnoop version\n");
Packit 34410b
				exit(1);
Packit 34410b
			}
Packit 34410b
Packit 34410b
			if (btsnoop_type != 1001 && btsnoop_type != 1002 &&
Packit 34410b
							btsnoop_type != 2001) {
Packit 34410b
				fprintf(stderr, "Unsupported BTSnoop datalink type\n");
Packit 34410b
				exit(1);
Packit 34410b
			}
Packit 34410b
		} else {
Packit 34410b
			if (buf[0] == 0x00 && buf[1] == 0x00) {
Packit 34410b
				parser.flags |= DUMP_PKTLOG;
Packit 34410b
				printf("packet logger data format\n");
Packit 34410b
			}
Packit 34410b
Packit 34410b
			parser.flags &= ~DUMP_BTSNOOP;
Packit 34410b
			lseek(fd, 0, SEEK_SET);
Packit 34410b
			return fd;
Packit 34410b
		}
Packit 34410b
	} else {
Packit 34410b
		if (flags & DUMP_BTSNOOP) {
Packit 34410b
			btsnoop_version = 1;
Packit 34410b
			btsnoop_type = 1002;
Packit 34410b
Packit 34410b
			memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id));
Packit 34410b
			hdr->version = htobe32(btsnoop_version);
Packit 34410b
			hdr->type = htobe32(btsnoop_type);
Packit 34410b
Packit 34410b
			printf("btsnoop version: %d datalink type: %d\n",
Packit 34410b
						btsnoop_version, btsnoop_type);
Packit 34410b
Packit 34410b
			len = write(fd, buf, BTSNOOP_HDR_SIZE);
Packit 34410b
			if (len < 0) {
Packit 34410b
				perror("Can't create dump header");
Packit 34410b
				exit(1);
Packit 34410b
			}
Packit 34410b
Packit 34410b
			if (len != BTSNOOP_HDR_SIZE) {
Packit 34410b
				fprintf(stderr, "Header size mismatch\n");
Packit 34410b
				exit(1);
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return fd;
Packit 34410b
}
Packit 34410b
Packit 34410b
static int open_socket(int dev, unsigned long flags)
Packit 34410b
{
Packit 34410b
	struct sockaddr_hci addr;
Packit 34410b
	struct hci_filter flt;
Packit 34410b
	int sk, opt;
Packit 34410b
Packit 34410b
	/* Create HCI socket */
Packit 34410b
	sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
Packit 34410b
	if (sk < 0) {
Packit 34410b
		perror("Can't create raw socket");
Packit 34410b
		return -1;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	opt = 1;
Packit 34410b
	if (setsockopt(sk, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
Packit 34410b
		perror("Can't enable data direction info");
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	opt = 1;
Packit 34410b
	if (setsockopt(sk, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
Packit 34410b
		perror("Can't enable time stamp");
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* Setup filter */
Packit 34410b
	hci_filter_clear(&flt);
Packit 34410b
	hci_filter_all_ptypes(&flt);
Packit 34410b
	hci_filter_all_events(&flt);
Packit 34410b
	if (setsockopt(sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
Packit 34410b
		perror("Can't set filter");
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* Bind socket to the HCI device */
Packit 34410b
	memset(&addr, 0, sizeof(addr));
Packit 34410b
	addr.hci_family = AF_BLUETOOTH;
Packit 34410b
	addr.hci_dev = dev;
Packit 34410b
	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
Packit 34410b
		printf("Can't attach to device hci%d. %s(%d)\n",
Packit 34410b
					dev, strerror(errno), errno);
Packit 34410b
		goto fail;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return sk;
Packit 34410b
Packit 34410b
fail:
Packit 34410b
	close(sk);
Packit 34410b
	return -1;
Packit 34410b
}
Packit 34410b
Packit 34410b
static struct {
Packit 34410b
	char *name;
Packit 34410b
	int  flag;
Packit 34410b
} filters[] = {
Packit 34410b
	{ "lmp",	FILT_LMP	},
Packit 34410b
	{ "hci",	FILT_HCI	},
Packit 34410b
	{ "sco",	FILT_SCO	},
Packit 34410b
	{ "l2cap",	FILT_L2CAP	},
Packit 34410b
	{ "a2mp",	FILT_A2MP	},
Packit 34410b
	{ "rfcomm",	FILT_RFCOMM	},
Packit 34410b
	{ "sdp",	FILT_SDP	},
Packit 34410b
	{ "bnep",	FILT_BNEP	},
Packit 34410b
	{ "cmtp",	FILT_CMTP	},
Packit 34410b
	{ "hidp",	FILT_HIDP	},
Packit 34410b
	{ "hcrp",	FILT_HCRP	},
Packit 34410b
	{ "att",	FILT_ATT	},
Packit 34410b
	{ "smp",	FILT_SMP	},
Packit 34410b
	{ "avdtp",	FILT_AVDTP	},
Packit 34410b
	{ "avctp",	FILT_AVCTP	},
Packit 34410b
	{ "obex",	FILT_OBEX	},
Packit 34410b
	{ "capi",	FILT_CAPI	},
Packit 34410b
	{ "ppp",	FILT_PPP	},
Packit 34410b
	{ "sap",	FILT_SAP	},
Packit 34410b
	{ "csr",	FILT_CSR	},
Packit 34410b
	{ "dga",	FILT_DGA	},
Packit 34410b
	{ 0 }
Packit 34410b
};
Packit 34410b
Packit 34410b
static unsigned long parse_filter(int argc, char **argv)
Packit 34410b
{
Packit 34410b
	unsigned long filter = 0;
Packit 34410b
	int i,n;
Packit 34410b
Packit 34410b
	for (i = 0; i < argc; i++) {
Packit 34410b
		for (n = 0; filters[n].name; n++) {
Packit 34410b
			if (!strcasecmp(filters[n].name, argv[i])) {
Packit 34410b
				filter |= filters[n].flag;
Packit 34410b
				break;
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return filter;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void usage(void)
Packit 34410b
{
Packit 34410b
	printf(
Packit 34410b
	"Usage: hcidump [OPTION...] [filter]\n"
Packit 34410b
	"  -i, --device=hci_dev       HCI device\n"
Packit 34410b
	"  -l, --snap-len=len         Snap len (in bytes)\n"
Packit 34410b
	"  -p, --psm=psm              Default PSM\n"
Packit 34410b
	"  -m, --manufacturer=compid  Default manufacturer\n"
Packit 34410b
	"  -w, --save-dump=file       Save dump to a file\n"
Packit 34410b
	"  -r, --read-dump=file       Read dump from a file\n"
Packit 34410b
	"  -t, --ts                   Display time stamps\n"
Packit 34410b
	"  -a, --ascii                Dump data in ascii\n"
Packit 34410b
	"  -x, --hex                  Dump data in hex\n"
Packit 34410b
	"  -X, --ext                  Dump data in hex and ascii\n"
Packit 34410b
	"  -R, --raw                  Dump raw data\n"
Packit 34410b
	"  -C, --cmtp=psm             PSM for CMTP\n"
Packit 34410b
	"  -H, --hcrp=psm             PSM for HCRP\n"
Packit 34410b
	"  -O, --obex=port            Channel/PSM for OBEX\n"
Packit 34410b
	"  -P, --ppp=channel          Channel for PPP\n"
Packit 34410b
	"  -S, --sap=channel          Channel for SAP\n"
Packit 34410b
	"  -D, --pppdump=file         Extract PPP traffic\n"
Packit 34410b
	"  -A, --audio=file           Extract SCO audio data\n"
Packit 34410b
	"  -Y, --novendor             No vendor commands or events\n"
Packit 34410b
	"  -h, --help                 Give this help list\n"
Packit 34410b
	"  -v, --version              Give version information\n"
Packit 34410b
	"      --usage                Give a short usage message\n"
Packit 34410b
	);
Packit 34410b
}
Packit 34410b
Packit 34410b
static struct option main_options[] = {
Packit 34410b
	{ "device",		1, 0, 'i' },
Packit 34410b
	{ "snap-len",		1, 0, 'l' },
Packit 34410b
	{ "psm",		1, 0, 'p' },
Packit 34410b
	{ "manufacturer",	1, 0, 'm' },
Packit 34410b
	{ "save-dump",		1, 0, 'w' },
Packit 34410b
	{ "read-dump",		1, 0, 'r' },
Packit 34410b
	{ "timestamp",		0, 0, 't' },
Packit 34410b
	{ "ascii",		0, 0, 'a' },
Packit 34410b
	{ "hex",		0, 0, 'x' },
Packit 34410b
	{ "ext",		0, 0, 'X' },
Packit 34410b
	{ "raw",		0, 0, 'R' },
Packit 34410b
	{ "cmtp",		1, 0, 'C' },
Packit 34410b
	{ "hcrp",		1, 0, 'H' },
Packit 34410b
	{ "obex",		1, 0, 'O' },
Packit 34410b
	{ "ppp",		1, 0, 'P' },
Packit 34410b
	{ "sap",		1, 0, 'S' },
Packit 34410b
	{ "pppdump",		1, 0, 'D' },
Packit 34410b
	{ "audio",		1, 0, 'A' },
Packit 34410b
	{ "novendor",		0, 0, 'Y' },
Packit 34410b
	{ "help",		0, 0, 'h' },
Packit 34410b
	{ "version",		0, 0, 'v' },
Packit 34410b
	{ 0 }
Packit 34410b
};
Packit 34410b
Packit 34410b
int main(int argc, char *argv[])
Packit 34410b
{
Packit 34410b
	unsigned long flags = 0;
Packit 34410b
	unsigned long filter = 0;
Packit 34410b
	int device = 0;
Packit 34410b
	int defpsm = 0;
Packit 34410b
	int defcompid = DEFAULT_COMPID;
Packit 34410b
	int opt, pppdump_fd = -1, audio_fd = -1;
Packit 34410b
	uint16_t obex_port;
Packit 34410b
Packit 34410b
	while ((opt = getopt_long(argc, argv,
Packit 34410b
				"i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv",
Packit 34410b
				main_options, NULL)) != -1) {
Packit 34410b
		switch(opt) {
Packit 34410b
		case 'i':
Packit 34410b
			if (strcasecmp(optarg, "none") && strcasecmp(optarg, "system"))
Packit 34410b
				device = atoi(optarg + 3);
Packit 34410b
			else
Packit 34410b
				device = HCI_DEV_NONE;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'l':
Packit 34410b
			snap_len = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'p':
Packit 34410b
			defpsm = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'm':
Packit 34410b
			defcompid = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'w':
Packit 34410b
			mode = WRITE;
Packit 34410b
			dump_file = strdup(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'r':
Packit 34410b
			mode = READ;
Packit 34410b
			dump_file = strdup(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 't':
Packit 34410b
			flags |= DUMP_TSTAMP;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'a':
Packit 34410b
			flags |= DUMP_ASCII;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'x':
Packit 34410b
			flags |= DUMP_HEX;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'X':
Packit 34410b
			flags |= DUMP_EXT;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'R':
Packit 34410b
			flags |= DUMP_RAW;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'C':
Packit 34410b
			set_proto(0, atoi(optarg), 0, SDP_UUID_CMTP);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'H':
Packit 34410b
			set_proto(0, atoi(optarg), 0, SDP_UUID_HARDCOPY_CONTROL_CHANNEL);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'O':
Packit 34410b
			obex_port = atoi(optarg);
Packit 34410b
			if (obex_port > 31)
Packit 34410b
				set_proto(0, obex_port, 0, SDP_UUID_OBEX);
Packit 34410b
			else
Packit 34410b
				set_proto(0, 0, obex_port, SDP_UUID_OBEX);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'P':
Packit 34410b
			set_proto(0, 0, atoi(optarg), SDP_UUID_LAN_ACCESS_PPP);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'S':
Packit 34410b
			set_proto(0, 0, atoi(optarg), SDP_UUID_SIM_ACCESS);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'D':
Packit 34410b
			pppdump_file = strdup(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'A':
Packit 34410b
			audio_file = strdup(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'Y':
Packit 34410b
			flags |= DUMP_NOVENDOR;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'v':
Packit 34410b
			printf("%s\n", VERSION);
Packit 34410b
			exit(0);
Packit 34410b
Packit 34410b
		case 'h':
Packit 34410b
		default:
Packit 34410b
			usage();
Packit 34410b
			exit(0);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	argc -= optind;
Packit 34410b
	argv += optind;
Packit 34410b
	optind = 0;
Packit 34410b
Packit 34410b
	printf("HCI sniffer - Bluetooth packet analyzer ver %s\n", VERSION);
Packit 34410b
Packit 34410b
	if (argc > 0)
Packit 34410b
		filter = parse_filter(argc, argv);
Packit 34410b
Packit 34410b
	/* Default settings */
Packit 34410b
	if (!filter)
Packit 34410b
		filter = ~0L;
Packit 34410b
Packit 34410b
	if (pppdump_file)
Packit 34410b
		pppdump_fd = open_file(pppdump_file, PPPDUMP, flags);
Packit 34410b
Packit 34410b
	if (audio_file)
Packit 34410b
		audio_fd = open_file(audio_file, AUDIO, flags);
Packit 34410b
Packit 34410b
	switch (mode) {
Packit 34410b
	case PARSE:
Packit 34410b
		flags |= DUMP_VERBOSE;
Packit 34410b
		init_parser(flags, filter, defpsm, defcompid,
Packit 34410b
							pppdump_fd, audio_fd);
Packit 34410b
		process_frames(device, open_socket(device, flags), -1, flags);
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case READ:
Packit 34410b
		flags |= DUMP_VERBOSE;
Packit 34410b
		init_parser(flags, filter, defpsm, defcompid,
Packit 34410b
							pppdump_fd, audio_fd);
Packit 34410b
		read_dump(open_file(dump_file, mode, flags));
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case WRITE:
Packit 34410b
		flags |= DUMP_BTSNOOP;
Packit 34410b
		process_frames(device, open_socket(device, flags),
Packit 34410b
				open_file(dump_file, mode, flags), flags);
Packit 34410b
		break;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return 0;
Packit 34410b
}