1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
6 *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdio.h>
30#include <errno.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <sys/socket.h>
34#include <sys/ioctl.h>
35
36#include <bluetooth/bluetooth.h>
37#include <bluetooth/bnep.h>
38
39#include <netinet/in.h>
40
41#include "pand.h"
42
43static int ctl;
44
45/* Compatibility with old ioctls */
46#define OLD_BNEPCONADD      1
47#define OLD_BNEPCONDEL      2
48#define OLD_BNEPGETCONLIST  3
49#define OLD_BNEPGETCONINFO  4
50
51static unsigned long bnepconnadd;
52static unsigned long bnepconndel;
53static unsigned long bnepgetconnlist;
54static unsigned long bnepgetconninfo;
55
56static struct {
57	char     *str;
58	uint16_t uuid;
59} __svc[] = {
60	{ "PANU", BNEP_SVC_PANU },
61	{ "NAP",  BNEP_SVC_NAP  },
62	{ "GN",   BNEP_SVC_GN   },
63	{ NULL }
64};
65
66int bnep_str2svc(char *svc, uint16_t *uuid)
67{
68	int i;
69	for (i = 0; __svc[i].str; i++)
70		if (!strcasecmp(svc, __svc[i].str)) {
71			*uuid = __svc[i].uuid;
72			return 0;
73		}
74	return -1;
75}
76
77char *bnep_svc2str(uint16_t uuid)
78{
79	int i;
80	for (i = 0; __svc[i].str; i++)
81		if (__svc[i].uuid == uuid)
82			return __svc[i].str;
83	return NULL;
84}
85
86int bnep_init(void)
87{
88	ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
89	if (ctl < 0) {
90		perror("Failed to open control socket");
91		return 1;
92	}
93
94	/* Temporary ioctl compatibility hack */
95	{
96		struct bnep_connlist_req req;
97		struct bnep_conninfo ci[1];
98
99		req.cnum = 1;
100		req.ci   = ci;
101
102		if (!ioctl(ctl, BNEPGETCONNLIST, &req)) {
103			/* New ioctls */
104			bnepconnadd     = BNEPCONNADD;
105			bnepconndel     = BNEPCONNDEL;
106			bnepgetconnlist = BNEPGETCONNLIST;
107			bnepgetconninfo = BNEPGETCONNINFO;
108		} else {
109			/* Old ioctls */
110			bnepconnadd     = OLD_BNEPCONADD;
111			bnepconndel     = OLD_BNEPCONDEL;
112			bnepgetconnlist = OLD_BNEPGETCONLIST;
113			bnepgetconninfo = OLD_BNEPGETCONINFO;
114		}
115	}
116
117	return 0;
118}
119
120int bnep_cleanup(void)
121{
122	close(ctl);
123	return 0;
124}
125
126int bnep_show_connections(void)
127{
128	struct bnep_connlist_req req;
129	struct bnep_conninfo ci[48];
130	unsigned int i;
131
132	req.cnum = 48;
133	req.ci   = ci;
134	if (ioctl(ctl, bnepgetconnlist, &req)) {
135		perror("Failed to get connection list");
136		return -1;
137	}
138
139	for (i = 0; i < req.cnum; i++) {
140		char addr[18];
141		ba2str((bdaddr_t *) ci[i].dst, addr);
142		printf("%s %s %s\n", ci[i].device,
143			addr, bnep_svc2str(ci[i].role));
144	}
145	return 0;
146}
147
148int bnep_kill_connection(uint8_t *dst)
149{
150	struct bnep_conndel_req req;
151
152	memcpy(req.dst, dst, ETH_ALEN);
153	req.flags = 0;
154	if (ioctl(ctl, bnepconndel, &req)) {
155		perror("Failed to kill connection");
156		return -1;
157	}
158	return 0;
159}
160
161int bnep_kill_all_connections(void)
162{
163	struct bnep_connlist_req req;
164	struct bnep_conninfo ci[48];
165	unsigned int i;
166
167	req.cnum = 48;
168	req.ci   = ci;
169	if (ioctl(ctl, bnepgetconnlist, &req)) {
170		perror("Failed to get connection list");
171		return -1;
172	}
173
174	for (i = 0; i < req.cnum; i++) {
175		struct bnep_conndel_req req;
176		memcpy(req.dst, ci[i].dst, ETH_ALEN);
177		req.flags = 0;
178		ioctl(ctl, bnepconndel, &req);
179	}
180	return 0;
181}
182
183static int bnep_connadd(int sk, uint16_t role, char *dev)
184{
185	struct bnep_connadd_req req;
186
187	strncpy(req.device, dev, 16);
188	req.device[15] = '\0';
189	req.sock = sk;
190	req.role = role;
191	if (ioctl(ctl, bnepconnadd, &req))
192		return -1;
193	strncpy(dev, req.device, 16);
194	return 0;
195}
196
197struct __service_16 {
198	uint16_t dst;
199	uint16_t src;
200} __attribute__ ((packed));
201
202struct __service_32 {
203	uint16_t unused1;
204	uint16_t dst;
205	uint16_t unused2;
206	uint16_t src;
207} __attribute__ ((packed));
208
209struct __service_128 {
210	uint16_t unused1;
211	uint16_t dst;
212	uint16_t unused2[8];
213	uint16_t src;
214	uint16_t unused3[7];
215} __attribute__ ((packed));
216
217int bnep_accept_connection(int sk, uint16_t role, char *dev)
218{
219	struct bnep_setup_conn_req *req;
220	struct bnep_control_rsp *rsp;
221	unsigned char pkt[BNEP_MTU];
222	ssize_t r;
223
224	r = recv(sk, pkt, BNEP_MTU, 0);
225	if (r <= 0)
226		return -1;
227
228	errno = EPROTO;
229
230	if ((size_t) r < sizeof(*req))
231		return -1;
232
233	req = (void *) pkt;
234
235	/* Highest known Control command ID
236	 * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
237	if (req->type == BNEP_CONTROL &&
238				req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
239		uint8_t pkt[3];
240
241		pkt[0] = BNEP_CONTROL;
242		pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
243		pkt[2] = req->ctrl;
244
245		send(sk, pkt, sizeof(pkt), 0);
246
247		return -1;
248	}
249
250	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
251		return -1;
252
253	/* FIXME: Check role UUIDs */
254
255	rsp = (void *) pkt;
256	rsp->type = BNEP_CONTROL;
257	rsp->ctrl = BNEP_SETUP_CONN_RSP;
258	rsp->resp = htons(BNEP_SUCCESS);
259	if (send(sk, rsp, sizeof(*rsp), 0) < 0)
260		return -1;
261
262	return bnep_connadd(sk, role, dev);
263}
264
265/* Create BNEP connection
266 * sk      - Connect L2CAP socket
267 * role    - Local role
268 * service - Remote service
269 * dev     - Network device (contains actual dev name on return)
270 */
271int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev)
272{
273	struct bnep_setup_conn_req *req;
274	struct bnep_control_rsp *rsp;
275	struct __service_16 *s;
276	struct timeval timeo;
277	unsigned char pkt[BNEP_MTU];
278	ssize_t r;
279
280	/* Send request */
281	req = (void *) pkt;
282	req->type = BNEP_CONTROL;
283	req->ctrl = BNEP_SETUP_CONN_REQ;
284	req->uuid_size = 2;	/* 16bit UUID */
285
286	s = (void *) req->service;
287	s->dst = htons(svc);
288	s->src = htons(role);
289
290	memset(&timeo, 0, sizeof(timeo));
291	timeo.tv_sec = 30;
292
293	setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
294
295	if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0)
296		return -1;
297
298receive:
299	/* Get response */
300	r = recv(sk, pkt, BNEP_MTU, 0);
301	if (r <= 0)
302		return -1;
303
304	memset(&timeo, 0, sizeof(timeo));
305	timeo.tv_sec = 0;
306
307	setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
308
309	errno = EPROTO;
310
311	if ((size_t) r < sizeof(*rsp))
312		return -1;
313
314	rsp = (void *) pkt;
315	if (rsp->type != BNEP_CONTROL)
316		return -1;
317
318	if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
319		goto receive;
320
321	r = ntohs(rsp->resp);
322
323	switch (r) {
324	case BNEP_SUCCESS:
325		break;
326
327	case BNEP_CONN_INVALID_DST:
328	case BNEP_CONN_INVALID_SRC:
329	case BNEP_CONN_INVALID_SVC:
330		errno = EPROTO;
331		return -1;
332
333	case BNEP_CONN_NOT_ALLOWED:
334		errno = EACCES;
335		return -1;
336	}
337
338	return bnep_connadd(sk, role, dev);
339}
340