xref: /freebsd/usr.sbin/bluetooth/l2ping/l2ping.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
1 /*
2  * l2ping.c
3  *
4  * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: l2ping.c,v 1.9 2002/09/04 21:28:05 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <sys/time.h>
36 #include <arpa/inet.h>
37 #include <netinet/in.h>
38 #include <assert.h>
39 #include <bitstring.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <ng_hci.h>
47 #include <ng_l2cap.h>
48 #include <ng_btsocket.h>
49 
50 static void	usage	(void);
51 static void	tv_sub	(struct timeval *, struct timeval const *);
52 static double	tv2msec	(struct timeval const *);
53 
54 #undef	min
55 #define	min(x, y)	(((x) > (y))? (y) : (x))
56 
57 static char const		pattern[] = "1234567890-";
58 #define PATTERN_SIZE		(sizeof(pattern) - 1)
59 
60 /*
61  * Main
62  */
63 
64 int
65 main(int argc, char *argv[])
66 {
67 	struct ng_btsocket_l2cap_raw_ping	r;
68 	int					n, s, count, wait, flood, fail;
69 	struct timeval				a, b;
70 
71 	/* Set defaults */
72 	memset(&r, 0, sizeof(r));
73 	r.echo_data = calloc(NG_L2CAP_MAX_ECHO_SIZE, sizeof(u_int8_t));
74 	if (r.echo_data == NULL) {
75 		fprintf(stderr, "Failed to allocate echo data buffer");
76 		exit(1);
77 	}
78 
79 	r.echo_size = 64; /* bytes */
80 	count = -1;       /* unlimited */
81 	wait = 1;         /* sec */
82 	flood = 0;
83 
84 	/* Parse command line arguments */
85 	while ((n = getopt(argc, argv, "a:c:fi:n:s:S:")) != -1) {
86 		switch (n) {
87 		case 'a':
88 		case 'S': {
89 			int	a0, a1, a2, a3, a4, a5;
90 
91 			if (sscanf(optarg, "%x:%x:%x:%x:%x:%x",
92 					&a5, &a4, &a3, &a2, &a1, &a0) != 6)
93 				usage();
94 
95 			if (n == 'a') {
96 				/* destination bdaddr */
97 				r.echo_dst.b[0] = (a0 & 0xff);
98 				r.echo_dst.b[1] = (a1 & 0xff);
99 				r.echo_dst.b[2] = (a2 & 0xff);
100 				r.echo_dst.b[3] = (a3 & 0xff);
101 				r.echo_dst.b[4] = (a4 & 0xff);
102 				r.echo_dst.b[5] = (a5 & 0xff);
103 			} else {
104 				/* source bdaddr */
105 				r.echo_src.b[0] = (a0 & 0xff);
106 				r.echo_src.b[1] = (a1 & 0xff);
107 				r.echo_src.b[2] = (a2 & 0xff);
108 				r.echo_src.b[3] = (a3 & 0xff);
109 				r.echo_src.b[4] = (a4 & 0xff);
110 				r.echo_src.b[5] = (a5 & 0xff);
111 			}
112 			} break;
113 
114 		case 'c':
115 			count = atoi(optarg);
116 			if (count <= 0)
117 				usage();
118 			break;
119 
120 		case 'f':
121 			flood = 1;
122 			break;
123 
124 		case 'i':
125 			wait = atoi(optarg);
126 			if (wait <= 0)
127 				usage();
128 			break;
129 
130 		case 's':
131 			r.echo_size = atoi(optarg);
132 			if ((int) r.echo_size < sizeof(int))
133 				usage();
134 
135 			if (r.echo_size > NG_L2CAP_MAX_ECHO_SIZE)
136 				r.echo_size = NG_L2CAP_MAX_ECHO_SIZE;
137 			break;
138 
139 		default:
140 			usage();
141 			break;
142 		}
143 	}
144 
145 	if (memcmp(&r.echo_dst, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0)
146 		usage();
147 
148 	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP);
149 	if (s < 0)
150 		err(2, "Could not create socket");
151 
152 	if (memcmp(&r.echo_src, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) != 0) {
153 		struct sockaddr_l2cap	sa;
154 
155 		memset(&sa, 0, sizeof(sa));
156 		sa.l2cap_len = sizeof(sa);
157 		sa.l2cap_family = AF_BLUETOOTH;
158 		memcpy(&sa.l2cap_bdaddr, &r.echo_src, sizeof(bdaddr_t));
159 
160 		if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
161 			err(3,
162 "Could not bind socket, src bdaddr=%x:%x:%x:%x:%x:%x",
163 				sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4],
164 				sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2],
165 				sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]);
166 
167 		if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
168 			err(4,
169 "Could not connect socket, src bdaddr=%x:%x:%x:%x:%x:%x",
170 				sa.l2cap_bdaddr.b[5], sa.l2cap_bdaddr.b[4],
171 				sa.l2cap_bdaddr.b[3], sa.l2cap_bdaddr.b[2],
172 				sa.l2cap_bdaddr.b[1], sa.l2cap_bdaddr.b[0]);
173 	}
174 
175 	/* Fill pattern */
176 	for (n = 0; n < r.echo_size; ) {
177 		int	avail = min(r.echo_size - n, PATTERN_SIZE);
178 
179 		memcpy(r.echo_data + n, pattern, avail);
180 		n += avail;
181 	}
182 
183 	/* Start ping'ing */
184 	for (n = 0; count == -1 || count > 0; n ++) {
185 		if (gettimeofday(&a, NULL) < 0)
186 			err(5, "Could not gettimeofday(a)");
187 
188 		fail = 0;
189 		*((int *)(r.echo_data)) = htonl(n);
190 		if (ioctl(s, SIOC_L2CAP_L2CA_PING, &r, sizeof(r)) < 0) {
191 			r.result = errno;
192 			fail = 1;
193 /*
194 			warn("Could not ping, dst bdaddr=%x:%x:%x:%x:%x:%x",
195 				r.echo_dst.b[5], r.echo_dst.b[4],
196 				r.echo_dst.b[3], r.echo_dst.b[2],
197 				r.echo_dst.b[1], r.echo_dst.b[0]);
198 */
199 		}
200 
201 		if (gettimeofday(&b, NULL) < 0)
202 			err(7, "Could not gettimeofday(b)");
203 
204 		tv_sub(&b, &a);
205 
206 		fprintf(stdout,
207 "%d bytes from %x:%x:%x:%x:%x:%x seq_no=%d time=%.3f ms result=%#x %s\n",
208 			r.echo_size,
209 			r.echo_dst.b[5], r.echo_dst.b[4],
210 			r.echo_dst.b[3], r.echo_dst.b[2],
211 			r.echo_dst.b[1], r.echo_dst.b[0],
212 			ntohl(*((int *)(r.echo_data))),
213 			tv2msec(&b), r.result,
214 			((fail == 0)? "" : strerror(errno)));
215 
216 		if (!flood) {
217 			/* Wait */
218 			a.tv_sec = wait;
219 			a.tv_usec = 0;
220 			select(0, NULL, NULL, NULL, &a);
221 		}
222 
223 		if (count != -1)
224 			count --;
225 	}
226 
227 	free(r.echo_data);
228 	close(s);
229 
230 	return (0);
231 } /* main */
232 
233 /*
234  * a -= b, for timevals
235  */
236 
237 static void
238 tv_sub(struct timeval *a, struct timeval const *b)
239 {
240 	if (a->tv_usec < b->tv_usec) {
241 		a->tv_usec += 1000000;
242 		a->tv_sec -= 1;
243 	}
244 
245 	a->tv_usec -= b->tv_usec;
246 	a->tv_sec -= b->tv_sec;
247 } /* tv_sub */
248 
249 /*
250  * convert tv to msec
251  */
252 
253 static double
254 tv2msec(struct timeval const *tvp)
255 {
256 	return(((double)tvp->tv_usec)/1000.0 + ((double)tvp->tv_sec)*1000.0);
257 } /* tv2msec */
258 
259 /*
260  * Usage
261  */
262 
263 static void
264 usage(void)
265 {
266 	fprintf(stderr, "Usage: l2ping -a bd_addr " \
267 		"[-S bd_addr -c count -i wait -s size]\n");
268 	fprintf(stderr, "Where:\n");
269 	fprintf(stderr, "\t-S bd_addr         - Source BD_ADDR\n");
270 	fprintf(stderr, "\t-a bd_addr         - Remote BD_ADDR to ping\n");
271 	fprintf(stderr, "\t-c count           - Number of packets to send\n");
272 	fprintf(stderr, "\t-f                 - No delay (soft of flood)\n");
273 	fprintf(stderr, "\t-i wait            - Delay between packets (sec)\n");
274 	fprintf(stderr, "\t-s size            - Packet size (bytes), " \
275 		"between %d and %d\n", sizeof(int), NG_L2CAP_MAX_ECHO_SIZE);
276 
277 	exit(255);
278 } /* usage */
279 
280