Blame tools/l2ping.c

Packit 34410b
/*
Packit 34410b
 *
Packit 34410b
 *  BlueZ - Bluetooth protocol stack for Linux
Packit 34410b
 *
Packit 34410b
 *  Copyright (C) 2000-2001  Qualcomm Incorporated
Packit 34410b
 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
Packit 34410b
 *  Copyright (C) 2002-2010  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 <unistd.h>
Packit 34410b
#include <stdlib.h>
Packit 34410b
#include <string.h>
Packit 34410b
#include <getopt.h>
Packit 34410b
#include <signal.h>
Packit 34410b
#include <sys/time.h>
Packit 34410b
#include <poll.h>
Packit 34410b
#include <sys/socket.h>
Packit 34410b
Packit 34410b
#include "lib/bluetooth.h"
Packit 34410b
#include "lib/hci.h"
Packit 34410b
#include "lib/hci_lib.h"
Packit 34410b
#include "lib/l2cap.h"
Packit 34410b
Packit 34410b
/* Defaults */
Packit 34410b
static bdaddr_t bdaddr;
Packit 34410b
static int size    = 44;
Packit 34410b
static int ident   = 200;
Packit 34410b
static int delay   = 1;
Packit 34410b
static int count   = -1;
Packit 34410b
static int timeout = 10;
Packit 34410b
static int reverse = 0;
Packit 34410b
static int verify = 0;
Packit 34410b
Packit 34410b
/* Stats */
Packit 34410b
static int sent_pkt = 0;
Packit 34410b
static int recv_pkt = 0;
Packit 34410b
Packit 34410b
static float tv2fl(struct timeval tv)
Packit 34410b
{
Packit 34410b
	return (float)(tv.tv_sec*1000.0) + (float)(tv.tv_usec/1000.0);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void stat(int sig)
Packit 34410b
{
Packit 34410b
	int loss = sent_pkt ? (float)((sent_pkt-recv_pkt)/(sent_pkt/100.0)) : 0;
Packit 34410b
	printf("%d sent, %d received, %d%% loss\n", sent_pkt, recv_pkt, loss);
Packit 34410b
	exit(0);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void ping(char *svr)
Packit 34410b
{
Packit 34410b
	struct sigaction sa;
Packit 34410b
	struct sockaddr_l2 addr;
Packit 34410b
	socklen_t optlen;
Packit 34410b
	unsigned char *send_buf;
Packit 34410b
	unsigned char *recv_buf;
Packit 34410b
	char str[18];
Packit 34410b
	int i, sk, lost;
Packit 34410b
	uint8_t id;
Packit 34410b
Packit 34410b
	memset(&sa, 0, sizeof(sa));
Packit 34410b
	sa.sa_handler = stat;
Packit 34410b
	sigaction(SIGINT, &sa, NULL);
Packit 34410b
Packit 34410b
	send_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
Packit 34410b
	recv_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
Packit 34410b
	if (!send_buf || !recv_buf) {
Packit 34410b
		perror("Can't allocate buffer");
Packit 34410b
		exit(1);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* Create socket */
Packit 34410b
	sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
Packit 34410b
	if (sk < 0) {
Packit 34410b
		perror("Can't create socket");
Packit 34410b
		goto error;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* Bind to local address */
Packit 34410b
	memset(&addr, 0, sizeof(addr));
Packit 34410b
	addr.l2_family = AF_BLUETOOTH;
Packit 34410b
	bacpy(&addr.l2_bdaddr, &bdaddr);
Packit 34410b
Packit 34410b
	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
Packit 34410b
		perror("Can't bind socket");
Packit 34410b
		goto error;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* Connect to remote device */
Packit 34410b
	memset(&addr, 0, sizeof(addr));
Packit 34410b
	addr.l2_family = AF_BLUETOOTH;
Packit 34410b
	str2ba(svr, &addr.l2_bdaddr);
Packit 34410b
Packit 34410b
	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
Packit 34410b
		perror("Can't connect");
Packit 34410b
		goto error;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* Get local address */
Packit 34410b
	memset(&addr, 0, sizeof(addr));
Packit 34410b
	optlen = sizeof(addr);
Packit 34410b
Packit 34410b
	if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
Packit 34410b
		perror("Can't get local address");
Packit 34410b
		goto error;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	ba2str(&addr.l2_bdaddr, str);
Packit 34410b
	printf("Ping: %s from %s (data size %d) ...\n", svr, str, size);
Packit 34410b
Packit 34410b
	/* Initialize send buffer */
Packit 34410b
	for (i = 0; i < size; i++)
Packit 34410b
		send_buf[L2CAP_CMD_HDR_SIZE + i] = (i % 40) + 'A';
Packit 34410b
Packit 34410b
	id = ident;
Packit 34410b
Packit 34410b
	while (count == -1 || count-- > 0) {
Packit 34410b
		struct timeval tv_send, tv_recv, tv_diff;
Packit 34410b
		l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *) send_buf;
Packit 34410b
		l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *) recv_buf;
Packit 34410b
Packit 34410b
		/* Build command header */
Packit 34410b
		send_cmd->ident = id;
Packit 34410b
		send_cmd->len   = htobs(size);
Packit 34410b
Packit 34410b
		if (reverse)
Packit 34410b
			send_cmd->code = L2CAP_ECHO_RSP;
Packit 34410b
		else
Packit 34410b
			send_cmd->code = L2CAP_ECHO_REQ;
Packit 34410b
Packit 34410b
		gettimeofday(&tv_send, NULL);
Packit 34410b
Packit 34410b
		/* Send Echo Command */
Packit 34410b
		if (send(sk, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) {
Packit 34410b
			perror("Send failed");
Packit 34410b
			goto error;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		/* Wait for Echo Response */
Packit 34410b
		lost = 0;
Packit 34410b
		while (1) {
Packit 34410b
			struct pollfd pf[1];
Packit 34410b
			int err;
Packit 34410b
Packit 34410b
			pf[0].fd = sk;
Packit 34410b
			pf[0].events = POLLIN;
Packit 34410b
Packit 34410b
			if ((err = poll(pf, 1, timeout * 1000)) < 0) {
Packit 34410b
				perror("Poll failed");
Packit 34410b
				goto error;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			if (!err) {
Packit 34410b
				lost = 1;
Packit 34410b
				break;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			if ((err = recv(sk, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0)) < 0) {
Packit 34410b
				perror("Recv failed");
Packit 34410b
				goto error;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			if (!err){
Packit 34410b
				printf("Disconnected\n");
Packit 34410b
				goto error;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			recv_cmd->len = btohs(recv_cmd->len);
Packit 34410b
Packit 34410b
			/* Check for our id */
Packit 34410b
			if (recv_cmd->ident != id)
Packit 34410b
				continue;
Packit 34410b
Packit 34410b
			/* Check type */
Packit 34410b
			if (!reverse && recv_cmd->code == L2CAP_ECHO_RSP)
Packit 34410b
				break;
Packit 34410b
Packit 34410b
			if (recv_cmd->code == L2CAP_COMMAND_REJ) {
Packit 34410b
				printf("Peer doesn't support Echo packets\n");
Packit 34410b
				goto error;
Packit 34410b
			}
Packit 34410b
Packit 34410b
		}
Packit 34410b
		sent_pkt++;
Packit 34410b
Packit 34410b
		if (!lost) {
Packit 34410b
			recv_pkt++;
Packit 34410b
Packit 34410b
			gettimeofday(&tv_recv, NULL);
Packit 34410b
			timersub(&tv_recv, &tv_send, &tv_diff);
Packit 34410b
Packit 34410b
			if (verify) {
Packit 34410b
				/* Check payload length */
Packit 34410b
				if (recv_cmd->len != size) {
Packit 34410b
					fprintf(stderr, "Received %d bytes, expected %d\n",
Packit 34410b
						   recv_cmd->len, size);
Packit 34410b
					goto error;
Packit 34410b
				}
Packit 34410b
Packit 34410b
				/* Check payload */
Packit 34410b
				if (memcmp(&send_buf[L2CAP_CMD_HDR_SIZE],
Packit 34410b
						   &recv_buf[L2CAP_CMD_HDR_SIZE], size)) {
Packit 34410b
					fprintf(stderr, "Response payload different.\n");
Packit 34410b
					goto error;
Packit 34410b
				}
Packit 34410b
			}
Packit 34410b
Packit 34410b
			printf("%d bytes from %s id %d time %.2fms\n", recv_cmd->len, svr,
Packit 34410b
				   id - ident, tv2fl(tv_diff));
Packit 34410b
Packit 34410b
			if (delay)
Packit 34410b
				sleep(delay);
Packit 34410b
		} else {
Packit 34410b
			printf("no response from %s: id %d\n", svr, id - ident);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (++id > 254)
Packit 34410b
			id = ident;
Packit 34410b
	}
Packit 34410b
	stat(0);
Packit 34410b
	free(send_buf);
Packit 34410b
	free(recv_buf);
Packit 34410b
	return;
Packit 34410b
Packit 34410b
error:
Packit 34410b
	close(sk);
Packit 34410b
	free(send_buf);
Packit 34410b
	free(recv_buf);
Packit 34410b
	exit(1);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void usage(void)
Packit 34410b
{
Packit 34410b
	printf("l2ping - L2CAP ping\n");
Packit 34410b
	printf("Usage:\n");
Packit 34410b
	printf("\tl2ping [-i device] [-s size] [-c count] [-t timeout] [-d delay] [-f] [-r] [-v] <bdaddr>\n");
Packit 34410b
	printf("\t-f  Flood ping (delay = 0)\n");
Packit 34410b
	printf("\t-r  Reverse ping\n");
Packit 34410b
	printf("\t-v  Verify request and response payload\n");
Packit 34410b
}
Packit 34410b
Packit 34410b
int main(int argc, char *argv[])
Packit 34410b
{
Packit 34410b
	int opt;
Packit 34410b
Packit 34410b
	/* Default options */
Packit 34410b
	bacpy(&bdaddr, BDADDR_ANY);
Packit 34410b
Packit 34410b
	while ((opt=getopt(argc,argv,"i:d:s:c:t:frv")) != EOF) {
Packit 34410b
		switch(opt) {
Packit 34410b
		case 'i':
Packit 34410b
			if (!strncasecmp(optarg, "hci", 3))
Packit 34410b
				hci_devba(atoi(optarg + 3), &bdaddr);
Packit 34410b
			else
Packit 34410b
				str2ba(optarg, &bdaddr);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'd':
Packit 34410b
			delay = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'f':
Packit 34410b
			/* Kinda flood ping */
Packit 34410b
			delay = 0;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'r':
Packit 34410b
			/* Use responses instead of requests */
Packit 34410b
			reverse = 1;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'v':
Packit 34410b
			verify = 1;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 'c':
Packit 34410b
			count = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 't':
Packit 34410b
			timeout = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 's':
Packit 34410b
			size = atoi(optarg);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		default:
Packit 34410b
			usage();
Packit 34410b
			exit(1);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!(argc - optind)) {
Packit 34410b
		usage();
Packit 34410b
		exit(1);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	ping(argv[optind]);
Packit 34410b
Packit 34410b
	return 0;
Packit 34410b
}