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