/* * * 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; }